Terraform Refresh Command Explained: How It Updates State Without Changing Infrastructure

The terraform refresh command performs a read-only walk through your infrastructure, re-reading every managed resource from its provider via the Read operation and updating the local state file to reflect real-world values, without generating execution plans or modifying actual resources.

The terraform refresh command serves as a critical state reconciliation tool within the HashiCorp Terraform codebase. Unlike planning or apply operations that propose or enact infrastructure changes, this command executes a specialized graph walk that queries current provider states and persists those discoveries to your backend state storage. Understanding the precise execution flow—from backend initialization through the refresh walk to final state persistence—enables operators to safely correct state drift without risking unintended resource modifications.

How the Terraform Refresh Command Executes

The command implementation centers on RefreshCommand in internal/command/refresh.go, which orchestrates a six-phase execution flow that strictly isolates read operations from mutation logic.

Backend Preparation and State Loading

First, the command initializes the configured backend and loads existing state data. The RefreshCommand.PrepareBackend method at line 113 in internal/command/refresh.go opens the state storage—whether local files, S3 backends, or other remote implementations—ensuring Terraform has a consistent view of currently managed resources before engaging any providers.

Operation Request Construction and Variable Evaluation

Next, RefreshCommand.OperationRequest (lines 129-165) constructs a backendrun.Operation struct configured with the views.Refresh operation type. This signals to the Terraform core engine that the upcoming graph walk must execute in refresh-only mode. Simultaneously, RefreshCommand.GatherVariables (lines 165-187) evaluates all input variables and computed defaults, making this data available to resource instances during the refresh evaluation without requiring configuration changes.

The Refresh Walk and Provider Interaction

The core execution happens during the refresh walk, where Terraform builds a dependency graph and traverses resource nodes. Each resource node implements a refresh method located in internal/terraform/node_resource_abstract_instance.go that:

  • Invokes PreRefresh and PostRefresh hooks for lifecycle management
  • Calls the provider's Read operation to query current infrastructure state
  • Writes the returned state into a refresh state (a temporary copy maintained during the walk and defined in internal/terraform/phasestate_string.go)

During this phase, flags like skipRefresh in internal/terraform/node_resource_plan_instance.go ensure that only read operations execute, preventing any Create, Update, or Delete provider calls from firing.

State Persistence and Output Evaluation

After the graph walk completes, the command merges the refresh state back into the working state via command.WriteState. This persists all discovered attribute changes to your configured backend. Additionally, internal/terraform/node_output.go handles output value evaluation during refresh walks, respecting the RefreshOnly field to skip output pre-condition checks unless explicitly requested via the -refresh-only flag. As implemented in hashicorp/terraform, the command returns exit code 0 on success or non-zero if any provider read operation fails.

Critical Limitations of the Refresh Command

Understanding what terraform refresh explicitly avoids prevents operational confusion:

  • No plan generation occurs: Unlike terraform plan, this command never computes diffs or proposed changes.
  • No resource mutations happen: The provider's Create, Update, or Delete operations remain uninvoked throughout the entire execution.
  • No configuration changes are applied: HCL files are parsed only to identify which resources exist, not to modify infrastructure definitions.

Practical Usage Examples

Basic CLI invocation refreshes the entire state file:


# Update terraform.tfstate to match real infrastructure

terraform refresh

Workspace-specific refresh operations:

terraform workspace select prod
terraform refresh

Enable debug logging to trace provider read operations:

TF_LOG=DEBUG terraform refresh

Programmatic invocation via Go mirrors the CLI flow:

import (
    "github.com/hashicorp/terraform/internal/command"
    "github.com/hashicorp/terraform/internal/arguments"
)

// Configure backend and variables
args := &arguments.State{
    // Populate backend config, var files, etc.
}

// Execute refresh command
refresh := &command.RefreshCommand{}
exitCode := refresh.Run([]string{}) // Raw CLI args
if exitCode != 0 {
    // Handle error
}

This snippet reflects the RefreshCommand.Run implementation at lines 22-48 of internal/command/refresh.go.

Key Source Files in the HashiCorp Terraform Repository

Summary

  • The terraform refresh command executes a read-only graph walk that updates state files without modifying infrastructure.
  • Source implementation resides in RefreshCommand at internal/command/refresh.go, utilizing lines 113-187 for backend and variable preparation.
  • The refresh walk in node_resource_abstract_instance.go calls provider Read operations and manages temporary refresh state through PreRefresh and PostRefresh hooks.
  • Unlike planning commands, refresh explicitly skips diff generation, resource mutation, and HCL file modification.
  • Exit codes indicate success (0) or provider read failures, while RefreshOnly flags control output evaluation behavior.

Frequently Asked Questions

Does the terraform refresh command modify real-world infrastructure?

No. According to the HashiCorp Terraform source code, the command strictly invokes the provider's Read operation during the refresh walk defined in internal/terraform/node_resource_abstract_instance.go. It never triggers Create, Update, or Delete operations, ensuring resources remain unchanged while only the state file updates.

What exit code does terraform refresh return when provider reads fail?

The command returns a non-zero exit code when encountering errors during resource reads or state writing operations, and exit code 0 upon successful completion of the refresh walk and state persistence via command.WriteState.

How does terraform refresh handle output values and pre-conditions?

During execution, the command evaluates output values but skips output pre-condition checks unless the -refresh-only flag is explicitly set. This behavior is controlled by the RefreshOnly field on OutputNode in internal/terraform/node_output.go, which distinguishes between standard refresh walks and refresh-only planning modes.

Where does the temporary refresh state live during command execution?

Terraform maintains a refresh state as a temporary copy of the working state during the graph walk. This transient state tracks discovered changes in internal/terraform/node_resource_abstract_instance.go before command.WriteState merges these updates back into the persistent backend state after the walk completes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s https://instagit.com/install.md

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client