TutorialsTerraform › variables.tf

Terraform Tutorial · File 3 of 9

variables.tf

A fill-in-the-blank form. It lists every piece of information Terraform needs from a person, or from GitHub Actions, before it can run — things that change per-user or per-environment, like the database password, instead of being hardcoded into the files.

Codey the Sr Developer, standing with a pointer, ready to walk through the guide
Variables defined → 8 Sensitive → db_password With validation → 4

Purpose of this file

Declare every input Terraform needs, along with a description, a type, and (where it matters) a default value or a validation rule.

Codey typing on a laptop
filling in the blanks

Nothing here is hardcoded to one person's Azure subscription, one password, or one IP address — because those things are different for every person who runs this project. variables.tf is the single place all of that “it depends” information is declared, so the rest of the files can reference it by name instead of repeating it.

The full file code sample

terraform/variables.tfvariable "subscription_id" {
  description = "Azure subscription to use; authenticate locally with az login."
  type        = string
}

variable "user_name" {
  description = "Name segment shared by the original PowerShell scripts."
  type        = string
  default     = "dzierzon"

  validation {
    condition     = can(regex("^[a-z][a-z0-9-]{0,24}$", var.user_name))
    error_message = "Use 1–25 lowercase letters, digits, or hyphens, starting with a letter."
  }
}

variable "location" {
  description = "Azure region used by the original resource script."
  type        = string
  default     = "westus2"
}

variable "db_password" {
  description = "PostgreSQL administrator password. Supply using TF_VAR_db_password."
  type        = string
  sensitive   = true

  validation {
    condition     = length(var.db_password) >= 8 && length(var.db_password) <= 128
    error_message = "Use a password between 8 and 128 characters that meets Azure PostgreSQL password requirements."
  }
}

variable "image_tag" {
  description = "Tag for the metals-api/metals-ui images in the container registry. Build and push with az_deploy.ps1/.sh before applying a new tag."
  type        = string
  default     = "latest"

  validation {
    condition     = can(regex("^[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,127}$", var.image_tag))
    error_message = "Use a valid Docker image tag."
  }
}

variable "client_ip" {
  description = "Your public IPv4 address, allowed to initialize the database."
  type        = string

  validation {
    condition     = can(cidrnetmask("${var.client_ip}/32")) && var.client_ip != "0.0.0.0"
    error_message = "Supply one nonzero IPv4 address, without a CIDR suffix."
  }
}

variable "github_environment" {
  description = "Must match the environment selected by the deployment workflow."
  type        = string
  default     = "Development"
}

variable "github_subject_repository" {
  description = "Repository portion of the exact GitHub OIDC subject, including immutable IDs when present."
  type        = string
  default     = "gdzierzon@9723466/cohort-d-dzierzong-metals-simple-deploy@1361521533"
}

variable "github_federated_subject" {
  description = "Optional full subject override for repositories with a custom OIDC format."
  type        = string
  default     = null
}

Anatomy of a variable block structure

variable "name" { ... }example
variable "location" {
  description = "Azure region used by the original resource script."
  type        = string
  default     = "westus2"
}
  • variable "location" — declares an input named location, referenced elsewhere as var.location.
  • description — documentation shown when someone runs terraform plan without supplying a value, or looks at the file directly.
  • type — the kind of value expected. Every variable in this file is a string.
  • default — the value used automatically if nobody supplies one. Not every variable has one — that's deliberate, covered below.

Each variable, explained walkthrough

subscription_idrequired, no default

Which Azure subscription to create resources in. There's no default because there isn't a sensible one — every person's subscription ID is different, so leaving this out forces you to supply your own rather than accidentally deploying into someone else's.

user_namedefault + validation

A short name segment used to build resource names throughout the project (see main.tf's local.prefix). Defaults to "dzierzon". The validation block rejects anything that isn't 1–25 lowercase letters, digits, or hyphens starting with a letter — the same character restrictions Azure resource names themselves tend to require, checked here instead of failing partway through a real deployment.

locationdefault, no validation

Which Azure region to deploy into. Defaults to "westus2". Any valid Azure region name works if it's overridden.

db_passwordsensitive + validation

The PostgreSQL administrator password. Marked sensitive = true, which tells Terraform to hide its value in plan and apply output — it still ends up in the state file, which is exactly why the state file is protected in a private, access-controlled storage location rather than committed to Git. The validation enforces an 8–128 character length, matching what Azure Database for PostgreSQL requires.

image_tagdefault + validation

Which tag of metals-api and metals-ui the two web apps should run. Defaults to "latest", and the validation is a regular expression matching Docker's own rules for a legal tag. Terraform doesn't build or push images — this variable only decides which already-published tag the apps point at, which is why the description tells you to push first.

Changing it and re-applying moves both apps to the new tag. Leaving it alone and pushing new content under the same tag does not redeploy anything — see why a restart is required.

client_iprequired + validation

Your own public IPv4 address, used later to open a firewall rule so you can connect to the database. The validation uses Terraform's cidrnetmask() function as a trick to confirm the value parses as a real IPv4 address, and separately rejects the placeholder value "0.0.0.0", which would mean “every address on the internet” if it slipped through.

github_environmentdefault

Which GitHub Actions environment name the deployment identity should trust. Defaults to "Development", matching the environment configured in this repository's GitHub settings.

github_subject_repositorydefault

The repository identity portion used to build the exact string GitHub presents when it authenticates to Azure (covered in detail on the github_oidc.tf page). Defaults to this repository's own value, so someone forking or copying the project for a different repository would override it.

github_federated_subjectoptional override, default null

An escape hatch: if a repository's OIDC subject doesn't follow the standard pattern this project assumes, this variable can override the whole computed value directly. default = null means “not set” — when left alone, main.tf's coalesce() falls back to building the subject from github_subject_repository instead.

Supplying values how the blanks get filled in

terraform.tfvarslocal, everyday values
terraform/terraform.tfvars.example# Copy to terraform.tfvars and replace both placeholders.
subscription_id = "YOUR-AZURE-SUBSCRIPTION-ID"
client_ip       = "YOUR-PUBLIC-IPV4"

user_name = "dzierzon"
location  = "westus2"

# Supply the database password through TF_VAR_db_password, not this file.
# The defaults already match this repository's Development OIDC subject.

Terraform automatically loads a file named terraform.tfvars if one exists, so copying this example and filling in your own values is the normal local workflow. It's listed in .gitignore and never committed, since it can contain personal or environment-specific values.

TF_VAR_ environment variablessecrets

Terraform also reads any environment variable named TF_VAR_<name> as that variable's value — for example, TF_VAR_db_password sets var.db_password. This project deliberately keeps the password out of any file, tvars included, and relies on this mechanism instead, both locally and as a GitHub Actions secret exposed the same way.

Key terms for beginners

Variable
A named input a Terraform configuration accepts, declared with a variable block and referenced elsewhere as var.<name>.
Default value
The value Terraform uses automatically if nothing else supplies one. A variable with no default becomes required — Terraform will stop and ask for it.
Validation block
An extra rule attached to a variable that rejects bad input immediately, with a clear error message, instead of letting an invalid value cause a confusing failure later.
Sensitive
A flag that hides a variable's value from Terraform's normal console output. It does not encrypt the value in the state file — that's why protecting the state file itself still matters.
Codey giving a thumbs up

Every blank is accounted for — time to build the resource group everything else lives in.