TutorialsTerraform › outputs.tf

Terraform Tutorial · File 8 of 9

outputs.tf

The receipt. After Terraform finishes creating or updating everything, this file decides which details get printed back — every web app's URL, the registry to push images to, the identifiers needed to configure application deployment, and the database connection details other tooling needs.

Codey the Sr Developer, standing with a pointer, ready to walk through the guide
Outputs defined → 11 Contains secrets → no (IDs only) Read with → terraform output

Purpose of this file

Expose the specific values people and other tools need after Terraform runs — without requiring anyone to dig through the state file directly.

Codey pointing to the right
here's what got built

Terraform's state file contains far more detail than anyone actually needs to read by hand. Outputs are a deliberate, curated summary: exactly the handful of values someone setting up application deployment, or writing a script, would actually want.

The full file code sample

terraform/outputs.tfoutput "resource_group_name" {
  value = azurerm_resource_group.metals.name
}

output "api_webapp_name" {
  description = "Must match AZURE_WEBAPP_NAME in the GitHub Actions workflow."
  value       = azurerm_linux_web_app.api.name
}

output "api_webapp_url" {
  value = "https://${azurerm_linux_web_app.api.default_hostname}"
}

output "ui_webapp_name" {
  value = azurerm_linux_web_app.ui.name
}

output "ui_webapp_url" {
  value = "https://${azurerm_linux_web_app.ui.default_hostname}"
}

output "tutorials_webapp_name" {
  value = azurerm_linux_web_app.tutorials.name
}

output "tutorials_webapp_url" {
  description = "The hosted tutorial site."
  value       = "https://${azurerm_linux_web_app.tutorials.default_hostname}"
}

output "container_registry_login_server" {
  description = "Push metals-api/metals-ui here, e.g. with az_deploy.ps1/.sh, before apply pulls a new image_tag."
  value       = azurerm_container_registry.metals.login_server
}

output "database" {
  description = "Connection details for the schema loader; password is supplied separately."
  value = {
    host    = azurerm_postgresql_flexible_server.metals.fqdn
    port    = 5432
    user    = local.db_user
    dbname  = azurerm_postgresql_flexible_server_database.metals.name
    sslmode = "require"
  }
}

output "github_secrets" {
  description = "Copy these IDs to GitHub repository secrets. These are identifiers, not passwords."
  value = {
    AZURE_CLIENT_ID       = azurerm_user_assigned_identity.github.client_id
    AZURE_TENANT_ID       = azurerm_user_assigned_identity.github.tenant_id
    AZURE_SUBSCRIPTION_ID = var.subscription_id
  }
}

output "github_federated_subject" {
  value = azurerm_federated_identity_credential.github.subject
}

Each output, explained walkthrough

resource_group_namesimple value

The name of the resource group created in main.tf — useful for scripting anything that needs to target it, like the Azure CLI commands the GitHub Actions workflow runs directly.

api_ / ui_ / tutorials_webapp_namemust stay in sync

Each web app's name. These must match the API_WEBAPP_NAME and UI_WEBAPP_NAME values at the top of .github/workflows/deploy-api.yml and deploy-ui.yml, since those workflows address the apps by name rather than looking them up through Terraform.

They were a single webapp_name output when the API was the only deployable app. The rename to api_* was necessary once a second app existed — and it's exactly the kind of change that quietly breaks any script still asking for the old name.

api_webapp_url / ui_webapp_urlcomputed values
value = "https://${azurerm_linux_web_app.api.default_hostname}"

An output's value doesn't have to be a plain attribute — here it's a small expression, wrapping each app's auto-generated hostname in a full, clickable https:// URL. The UI's is the one to actually open in a browser; the API's is useful for curl and health checks.

container_registry_login_serverwhere to push

The registry's hostname, like expeditorsdzierzonmetalsacr.azurecr.io. Terraform creates the registry but never puts anything in it, so this output is the handoff point: it tells you (or a script) exactly where metals-api and metals-ui need to be pushed before the web apps have anything to run.

databasea structured output

An output's value can also be a whole object, not just a single string — here, a small map of connection details (host, port, user, dbname, sslmode). Notice the password is deliberately left out; this output is consumed by terraform/scripts/initialize_database.py, which loads the password separately from the same TF_VAR_db_password source Terraform itself uses, rather than round-tripping it through an output.

github_secretssetup instructions, as data

The three identifiers someone needs to paste into the application deployment workflow's GitHub environment secrets (AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_SUBSCRIPTION_ID), grouped into one output so a single command prints exactly what's needed. As the description notes, these are identifiers that describe which identity to use — not secrets that grant access on their own; the OIDC trust relationship from github_oidc.tf is what actually authorizes access.

github_federated_subjectfor troubleshooting

Echoes back the exact OIDC subject string Azure was configured to trust, so it can be compared directly against a GitHub Actions error message if authentication ever fails — a quick way to spot a mismatch.

Reading outputs after apply how they get used

This is a quick preview — terraform output has its own full page covering preconditions, postconditions, and common errors. (dig deeper)

CommandWhat it shows
terraform outputAll outputs, formatted for a person to read.
terraform output -raw ui_webapp_urlOne output's value only, with no extra formatting — useful in scripts.
terraform output -json github_secretsOne structured output as JSON, so another program can parse it directly.

The GitHub Actions workflow in this project uses exactly this pattern: after apply, it runs terraform output -json github_secrets plus -raw api_webapp_name, -raw ui_webapp_name, and -raw container_registry_login_server, writing them all into the workflow's job summary — so whoever ran it can copy the values straight into GitHub settings without opening a terminal.

Key terms for beginners

Output
A named value a Terraform configuration exposes after running, chosen deliberately by the file's author rather than requiring someone to read the raw state file.
Structured output
An output whose value is an object or map (multiple named fields) rather than a single string or number.
Codey giving a thumbs up

That covers the main project — one more page for the one-time setup that comes before it.