The State File
The idea in one sentence
Section titled “The idea in one sentence”Terraform state is a JSON file that maps every resource in your configuration to the real-world object it created, and it is the only thing that lets Terraform know what it manages.
Why Terraform needs state at all
Section titled “Why Terraform needs state at all”When you run terraform apply, Terraform creates resources through the Azure Resource Manager API — a virtual machine, a storage account, a resource group. The Azure API has no concept of “Terraform.” If your subscription has 500 virtual machines, the Azure API cannot tell you which of those VMs Terraform created for a given resource "azurerm_linux_virtual_machine" "web" block, which ones a teammate created by hand in the Azure Portal, and which ones belong to a completely different Terraform configuration.
Terraform solves this by keeping its own record. Every time you apply, Terraform writes the mapping between your configuration’s resource addresses and the real infrastructure IDs into a state file, by default named terraform.tfstate:
resource "azurerm_linux_virtual_machine" "web" { name = "web-vm" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location size = "Standard_B2s" admin_username = "azureuser"
network_interface_ids = [ azurerm_network_interface.web.id, ]}After apply, the state file records something like azurerm_linux_virtual_machine.web -> /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example-resources/providers/Microsoft.Compute/virtualMachines/web-vm. That long path is what “the real-world object” actually looks like on Azure — every resource is identified by a fully qualified resource ID that encodes the subscription, the resource group, the provider namespace, and the resource name, all in one string. Alongside that ID, state also caches every attribute Azure returned for the VM — its private IP, its OS disk ID, its network interface associations, and so on. That cached data means Terraform does not have to call the Azure API and re-describe every resource just to compute a plan; it can compare your configuration against the last known state first, which is both how it detects what changed and a real performance win on large configurations. State is also where Terraform stores dependency metadata, so it knows the order in which resources must be created, updated, or destroyed.
The plaintext risk hiding in your state file
Section titled “The plaintext risk hiding in your state file”State files are not sanitized. Any attribute value Terraform receives from a provider — or that you set directly in your configuration — is written into terraform.tfstate as plaintext, including values that are secrets. A common example:
resource "azurerm_postgresql_flexible_server" "main" { name = "app-db" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location version = "15" sku_name = "GP_Standard_D2s_v3" administrator_login = "appadmin" administrator_password = "S3cretPassword!"}You never ran terraform output on that password and you never printed it in a log line. It still lands in terraform.tfstate in plaintext, because Terraform has to remember every attribute it set in order to detect drift on the next plan. Anyone who can read that file can read the password.
This is why terraform.tfstate must never be committed to version control. Add it to .gitignore from the very first commit of a new project:
terraform.tfstateterraform.tfstate.backup*.tfstate*.tfstate.*The next lesson covers moving state off your laptop entirely and into a properly access-controlled remote backend, which is the real fix for this risk — a .gitignore entry only stops the most obvious accident.
Inspecting state
Section titled “Inspecting state”Terraform ships three commands for looking inside state without opening the JSON file by hand:
# Human-readable dump of the entire current stateterraform show
# List every resource address currently tracked in stateterraform state list
# Show full attribute detail for one specific resourceterraform state show azurerm_linux_virtual_machine.webterraform state list is the fast way to answer “what does this configuration actually manage right now” — it prints addresses like azurerm_linux_virtual_machine.web or module.network.azurerm_virtual_network.main with no other detail. terraform state show <address> drills into one of those addresses and prints every attribute Terraform has recorded for it, which is often faster than going to the Azure Portal when you need to check a specific value Terraform is tracking.
flowchart LR cfg["Terraform config (.tf files)"] --> apply["terraform apply"] apply --> azure["Real Azure resources"] azure -->|attributes written back| state["terraform.tfstate"] cfg -->|next run| plan["terraform plan"] state -->|read for comparison| plan