Terraform state drift is one of the most common sources of surprise in infrastructure as code. When resources are changed outside of Terraform, manually, via a console, or by another automation system, the state file no longer reflects reality. Terraform Apply Refresh Only provides a controlled way to reconcile state with live infrastructure without making configuration changes. It preserves the plan-and-apply safety model while allowing operators to preview and approve state updates.
Introduction
The traditional workflow for detecting drift relied on a standalone refresh subcommand. As of Terraform 0.15.4, this command has been deprecated due to concerns about unintended state modifications and lack of user control over changes. Instead, Terraform has introduced the command terraform plan -refresh-only or terraform apply -refresh-only. With this approach, you can run the terraform plan -refresh-only you can see the plan showing exactly what Terraform detects as different in the infrastructure compared to its current state. You can review these differences before deciding whether to apply them.
In contrast, the old terraform refresh command would immediately update the state file without showing you the changes first, potentially leading to unexpected updates that were difficult to track. Terraform follows a plan-and-apply model to minimise surprises. The -refresh-only flag fits into this model by ensuring that state updates are treated like a change, requiring user approval just like any infrastructure modification.
This structured approach is particularly useful in team environments or CI/CD pipelines, where unintended state changes can lead to confusion or conflicts.
What Refresh Only Mode Does
It allows you to update your Terraform state to match the real infrastructure without applying any configuration changes.
Traditional Refresh vs Refresh-Only Mode
Traditional Refresh is the old method that directly modifies state.
- terraform refresh
- Modifies state immediately without review
- No plan preview of state changes
- Can cause unexpected side effects
- No rollback capability
Refresh-Only Mode is the new safe state updates method.
- terraform plan -refresh-only
- terraform apply -refresh-only
- Shows what will change before applying
- No infrastructure modifications
- Can be reviewed and approved
- Safer for production environments
The -refresh-only mode for terraform plan and terraform apply operations makes it safer to check Terraform state against real infrastructure by letting you review proposed changes to the state file. It lets you avoid mistakenly removing an existing resource from state and gives you a chance to correct your configuration.
A refresh-only apply operation also updates outputs, if necessary. If you have any other workspaces that use the terraformremotestate data source to access the outputs of the current workspace, the -refresh-only mode allows you to anticipate the downstream effects.
When to Use Refresh Only Mode
Refresh only mode is useful for several operational scenarios.
- Drift Detection: Check if infrastructure matches state
- State Reconciliation: Update state after manual changes
- Import Follow-up: Refresh state after importing resources
- Regular Maintenance: Periodic state cleanup
- Troubleshooting: Resolve state inconsistencies
In order to propose accurate changes to your infrastructure, Terraform first attempts to reconcile the resources tracked in your state file with your actual infrastructure. Terraform plan and apply operations first run an in-memory refresh to determine which changes to propose to your infrastructure. Once you confirm a terraform apply, Terraform will update your infrastructure and state file.
Basic Workflow
Before running any commands, ensure that Terraform is initialised and has access to the state file by running the command:
bash
terraform init
Then run the following command to compare the current infrastructure with the state file by running:
bash
terraform plan -refresh-only
This command checks for differences between the expected configuration and the actual deployed resources.
You can see in the example screenshot below when I run the command it’s compared what the settings should be against what I have within my live Azure environment and highlights some changes.
To update the Terraform state file without modifying any resources, run:
bash
terraform apply -refresh-only
This refreshes the state file to reflect the actual environment, ensuring that future Terraform changes are based on the correct resource configurations. This is particularly useful before making further updates to your resources, as it prevents unintended modifications due to outdated state data.
Step by Step Preview and Apply
Step 1: Preview State Changes
Check what state changes would occur.
bash
terraform plan -refresh-only
Example output:
```
Note: Objects have changed outside of Terraform
Terraform detected the following changes made outside of Terraform since the last "terraform apply":
aws_instance.web has changed
~ resource "awsinstance" "web" {
id = "i-1234567890abcdef0"
~ instancetype = "t2.micro" -> "t2.small"
(29 unchanged attributes hidden)
}
```
This is a refresh-only plan, so Terraform will not take any actions to undo these. If you were expecting these changes then you can apply this plan to record the updated values in the Terraform state without changing any remote objects.
Step 2: Apply State Updates
Apply the state changes.
bash
terraform apply -refresh-only
Example output:
Apply complete
The refresh operation with remote state behaves the same but with additional considerations. When using remote backends like S3 or Terraform Cloud, refresh behaves the same but with additional considerations:
hcl
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
use_lockfile = true
}
}
The refresh operation:
- Acquires state lock (if locking is enabled)
- Downloads current state from S3
- Queries provider APIs
- Updates state
- Uploads new state to S3
- Releases lock
Safe Practices for Refresh Only
Always Preview First
Never apply refresh without reviewing.
Bad:
bash
terraform apply -refresh-only -auto-approve
Good:
bash
terraform plan -refresh-only
Review output carefully
bash
terraform apply -refresh-only
Use Plan Files for Important Changes
For production or critical changes:
bash
terraform plan -refresh-only -out=prod-refresh.plan
Have team review plan
bash
terraform show prod-refresh.plan
Apply after approval
bash
terraform apply prod-refresh.plan
Document Refresh Decisions
```bash
!/bin/bash
Document why refresh was needed
echo "State refresh performed on $(date)" >> refresh.log
echo "Reason: Manual instance type change for performance" >> refresh.log
echo "Resources affected: aws_instance.web" >> refresh.log
echo "Applied by: $(whoami)" >> refresh.log
echo "---" >> refresh.log
terraform plan -refresh-only -out=refresh.plan
terraform apply refresh.plan
```
Here are some best practices that I have started incorporating into my Terraform workflow:
- Run terraform plan -refresh-only before every apply, this helps to flag up any issues with drift and prepare me to update my state file if needed before making any resource modifications.
- Review changes before applying, if any changes are flagging up I make sure I review the changes before updating my state to understand why the changes have occurred.
The -refresh-only flag is an important one that you should be aware of to ensure your Terraform state files are kept up to date and help to prevent any unintended changes to your resources.
Partial Refresh and Targeting
For large infrastructures, refresh specific resources:
bash
terraform plan -refresh-only -target=aws_instance.web
bash
terraform plan -refresh-only -target=module.networking
Partial refresh with targets reduces API calls and narrows the review scope.
Combining Refresh With Configuration Updates
Workflow for handling drift:
Step 1: Detect drift
bash
terraform plan -refresh-only
Step 2: Update state
bash
terraform apply -refresh-only
Step 3: Decide on configuration
Either update config to match state or plan to revert
Step 4: Apply final configuration
bash
terraform plan
terraform apply
This sequence separates state reconciliation from configuration enforcement.
Skipping Refresh
You can skip it:
bash
terraform plan -refresh=false
Skip refresh during plan (use cached state)
bash
terraform apply -refresh=false
Skip refresh during apply
Use this cautiously - your state might not reflect reality.
Common Refresh Scenarios
Scenario 1: Import Existing Resources
After importing resources, refresh ensures complete state:
bash
terraform import aws_instance.imported i-1234567890abcdef0
Refresh to get all attributes
bash
terraform apply -refresh-only
Scenario 2: After Provider Upgrades
Provider upgrades may change how attributes are stored:
bash
terraform init -upgrade
terraform plan -refresh-only
Scenario 3: Debugging State Issues
When state seems corrupted or outdated:
Check what Terraform thinks vs
Risks of the Deprecated Command
However, this was less safe than the -refresh-only plan and apply mode since it would automatically overwrite your state file without giving you the option to review the modifications first. In this case, that would mean automatically dropping all of your resources from your state file.
Though Terraform will continue to support the refresh subcommand in future versions, it is deprecated, and we encourage you to use the -refresh-only flag instead. This allows you to review any updates to your state file.
Comparison Table
| Feature | terraform refresh (deprecated) | terraform plan -refresh-only / apply -refresh-only |
|---|---|---|
| State update timing | Immediate | Preview then apply |
| Infrastructure changes | None | None |
| Review before apply | No | Yes |
| Rollback capability | No | Yes via plan file |
| Safe for production | No | Yes |
| Updates outputs | No | Yes |
Conclusion
Terraform Apply Refresh Only is the controlled path to keep state truthful without risking unintended resource modifications. By replacing the immediate overwrite behavior of the deprecated terraform refresh command with a plan-and-apply review cycle, teams gain visibility into drift, can document why state was updated, and can approve changes through plan files before any state mutation occurs.
The workflow of running terraform plan -refresh-only before every apply, reviewing detected differences, and then running terraform apply -refresh-only to record them ensures that subsequent plans are based on real infrastructure. When combined with targeting, remote state locking, and documentation practices, refresh only mode provides a repeatable safety net for manual changes, imports, provider upgrades, and periodic maintenance.
Adopting refresh only mode as a standard step in CI/CD pipelines and team runbooks reduces surprise drift, preserves auditability, and aligns state reconciliation with the same approval gates used for infrastructure changes.