AskLearn
Loading...
← Back to Terraform Course
IntermediateFundamentals

Use Refresh-Only Mode

Sync state without changes

Tutorial 12: Use Refresh-Only Mode

Learning Objectives

  • Understand refresh-only mode and its benefits over traditional refresh
  • Learn when and how to use refresh-only operations safely
  • Practice updating state without modifying infrastructure
  • Implement refresh-only workflows for state reconciliation
  • Handle complex refresh scenarios and edge cases

What is Refresh-Only Mode?

Refresh-only mode is a safer alternative to the traditional terraform refresh command. 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 (Deprecated)

# Old method - directly modifies state
terraform refresh

# Problems:
# - Modifies state immediately without review
# - No plan preview of state changes
# - Can cause unexpected side effects
# - No rollback capability

Refresh-Only Mode (Recommended)

# New method - safe state updates
terraform plan -refresh-only    # Preview state changes
terraform apply -refresh-only   # Apply state updates only

# Benefits:
# - Shows what will change before applying
# - No infrastructure modifications
# - Can be reviewed and approved
# - Safer for production environments

When to Use Refresh-Only Mode

  • 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

Basic Refresh-Only Operations

Simple Refresh-Only Workflow

Step 1: Preview State Changes

# Check what state changes would occur
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 "aws_instance" "web" {
        id            = "i-1234567890abcdef0"
      ~ instance_type = "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
terraform apply -refresh-only

Example output:

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Note: You didn't use the -out option to save this plan, so Terraform can't
guarantee that exactly these actions will be performed if you run this command again.

Using Plan Files for Safety

# Create a refresh-only plan file
terraform plan -refresh-only -out=refresh.plan

# Review the plan
terraform show refresh.plan

# Apply the specific plan
terraform apply refresh.plan

Practical Refresh-Only Scenarios

Scenario 1: Instance Type Changed Manually

Setup

# main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-west-2"
}

data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]
  
  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t2.micro"
  
  tags = {
    Name = "refresh-example"
  }
}

output "instance_id" {
  value = aws_instance.web.id
}

output "current_instance_type" {
  value = aws_instance.web.instance_type
}

Create and Modify Infrastructure

# Deploy infrastructure
terraform init
terraform apply

# Simulate manual change (outside Terraform)
INSTANCE_ID=$(terraform output -raw instance_id)
aws ec2 modify-instance-attribute \
  --instance-id $INSTANCE_ID \
  --instance-type '{"Value": "t2.small"}'

Use Refresh-Only to Update State

# Check current state vs reality
terraform plan -refresh-only

Output shows state will be updated:

  # aws_instance.web has changed
  ~ resource "aws_instance" "web" {
        id            = "i-1234567890abcdef0"
      ~ instance_type = "t2.micro" -> "t2.small"
        # (other attributes unchanged)
    }
# Update state to match reality
terraform apply -refresh-only

# Verify state is updated
terraform output current_instance_type
# Should now show "t2.small"

Decision Point After Refresh

Now you have two options:

Option 1: Update configuration to match reality

resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t2.small"  # Update to match current state
  
  tags = {
    Name = "refresh-example"
  }
}

Option 2: Revert infrastructure to match configuration

terraform plan  # Shows it will change back to t2.micro
terraform apply # Reverts instance type

Scenario 2: Tags Added Outside Terraform

Simulate Tag Changes

# Add tags manually
aws ec2 create-tags \
  --resources $INSTANCE_ID \
  --tags Key=Environment,Value=production Key=Team,Value=DevOps

Refresh State and Handle Tags

# Check state changes needed
terraform plan -refresh-only

Output shows new tags:

  # aws_instance.web has changed
  ~ resource "aws_instance" "web" {
        id   = "i-1234567890abcdef0"
      ~ tags = {
          + "Environment" = "production"
          + "Team"        = "DevOps"
            "Name"        = "refresh-example"
        }
        # (other attributes unchanged)
    }
# Update state with new tags
terraform apply -refresh-only

Handle Tag Drift

Option 1: Add tags to Terraform configuration

resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t2.micro"
  
  tags = {
    Name        = "refresh-example"
    Environment = "production"
    Team        = "DevOps"
  }
}

Option 2: Ignore external tag changes

resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t2.micro"
  
  tags = {
    Name = "refresh-example"
  }
  
  lifecycle {
    ignore_changes = [
      tags["Environment"],
      tags["Team"],
    ]
  }
}

Scenario 3: Resource Deleted and Recreated

Simulate Resource Recreation

# Get current instance details
ORIGINAL_ID=$(terraform output -raw instance_id)

# Terminate instance
aws ec2 terminate-instances --instance-ids $ORIGINAL_ID

# Wait for termination
aws ec2 wait instance-terminated --instance-ids $ORIGINAL_ID

# Create new instance manually with same name
NEW_INSTANCE_ID=$(aws ec2 run-instances \
  --image-id $(aws ec2 describe-images --owners amazon --filters "Name=name,Values=amzn2-ami-hvm-*-x86_64-gp2" --query 'Images[0].ImageId' --output text) \
  --instance-type t2.micro \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=refresh-example}]' \
  --query 'Instances[0].InstanceId' \
  --output text)

echo "New instance ID: $NEW_INSTANCE_ID"

Refresh-Only Detection

terraform plan -refresh-only

Output shows resource needs to be recreated:

  # aws_instance.web has changed
  ~ resource "aws_instance" "web" {
      ~ id                     = "i-1234567890abcdef0" -> "i-0987654321fedcba0"
      ~ private_ip             = "10.0.1.100" -> "10.0.1.150"
      ~ public_ip              = "54.123.45.67" -> "54.98.76.54"
        # (other attributes may have changed)
    }
# Update state with new instance details
terraform apply -refresh-only

Advanced Refresh-Only Usage

Targeted Refresh-Only

# Refresh only specific resources
terraform plan -refresh-only -target=aws_instance.web

# Refresh multiple specific resources
terraform plan -refresh-only \
  -target=aws_instance.web \
  -target=aws_security_group.web

Refresh-Only with Variables

# Use different variable values during refresh
terraform plan -refresh-only \
  -var="environment=production" \
  -var-file="production.tfvars"

Refresh-Only in Different Workspaces

# Switch to production workspace
terraform workspace select production

# Refresh production state
terraform plan -refresh-only
terraform apply -refresh-only

# Switch back to development
terraform workspace select development

Refresh-Only in Automation

CI/CD Pipeline Integration

# .github/workflows/state-refresh.yml
name: State Refresh Check

on:
  schedule:
    - cron: '0 8 * * *'  # Daily at 8 AM
  workflow_dispatch:

jobs:
  check-state-drift:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.5.0
          
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-west-2
          
      - name: Initialize Terraform
        run: terraform init
        
      - name: Check for state drift
        run: |
          if terraform plan -refresh-only -detailed-exitcode; then
            echo "✅ No state drift detected"
          else
            exitcode=$?
            if [ $exitcode -eq 2 ]; then
              echo "⚠️  State drift detected!"
              echo "Creating refresh plan..."
              terraform plan -refresh-only -out=refresh.plan
              
              # Upload plan for review
              echo "Please review and apply refresh.plan if appropriate"
              
              # Optional: Auto-apply refresh if safe
              # terraform apply refresh.plan
            else
              echo "❌ Error occurred during refresh check"
              exit 1
            fi
          fi

Automated Refresh Script

#!/bin/bash
# automated-refresh.sh

set -e

ENVIRONMENT=${1:-dev}
AUTO_APPLY=${2:-false}

echo "Checking state drift for environment: $ENVIRONMENT"

# Switch to appropriate workspace
terraform workspace select "$ENVIRONMENT"

# Initialize if needed
terraform init

# Check for drift
echo "Checking for state drift..."
if terraform plan -refresh-only -detailed-exitcode -out=refresh-$ENVIRONMENT.plan; then
    echo "✅ No state drift detected in $ENVIRONMENT"
    rm -f refresh-$ENVIRONMENT.plan
else
    exitcode=$?
    if [ $exitcode -eq 2 ]; then
        echo "⚠️  State drift detected in $ENVIRONMENT"
        
        # Show what would change
        echo "State changes that would be applied:"
        terraform show refresh-$ENVIRONMENT.plan
        
        if [ "$AUTO_APPLY" = "true" ]; then
            echo "Auto-applying refresh changes..."
            terraform apply refresh-$ENVIRONMENT.plan
            echo "✅ State refreshed successfully"
        else
            echo "Run 'terraform apply refresh-$ENVIRONMENT.plan' to update state"
        fi
    else
        echo "❌ Error occurred during refresh check"
        exit 1
    fi
fi

Usage:

# Check dev environment (manual approval)
./automated-refresh.sh dev

# Check production environment with auto-apply
./automated-refresh.sh prod true

Refresh-Only Best Practices

1. Always Preview First

# Never apply refresh without reviewing
# Bad:
terraform apply -refresh-only -auto-approve

# Good:
terraform plan -refresh-only
# Review output carefully
terraform apply -refresh-only

2. Use Plan Files for Important Changes

# For production or critical changes
terraform plan -refresh-only -out=prod-refresh.plan

# Have team review plan
terraform show prod-refresh.plan

# Apply after approval
terraform apply prod-refresh.plan

3. Document Refresh Decisions

#!/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

4. Combine with Configuration Updates

# Workflow for handling drift
# 1. Detect drift
terraform plan -refresh-only

# 2. Update state
terraform apply -refresh-only

# 3. Decide on configuration
# Either update config to match state or plan to revert

# 4. Apply final configuration
terraform plan
terraform apply

5. Monitor Refresh Frequency

# Track when refreshes are needed
echo "$(date): Refresh-only operation performed" >> /var/log/terraform-refresh.log

# Alert if too frequent
RECENT_REFRESHES=$(grep "$(date +%Y-%m-%d)" /var/log/terraform-refresh.log | wc -l)
if [ "$RECENT_REFRESHES" -gt 3 ]; then
    echo "⚠️  Warning: Multiple refresh operations today - investigate drift causes"
fi

Troubleshooting Refresh-Only Issues

Issue 1: Refresh Shows No Changes But Drift Exists

# Problem: State appears current but infrastructure differs
# Solution: Check if resources are filtered or targeted

# Verify all resources are being checked
terraform plan -refresh-only

# Check for ignore_changes blocks
grep -r "ignore_changes" *.tf

# Verify resource exists
terraform state list

Issue 2: Permission Errors During Refresh

# Problem: Cannot read resource attributes
# Solution: Check read permissions

# Test AWS permissions
aws ec2 describe-instances --dry-run

# Check specific resource permissions
aws iam simulate-principal-policy \
  --policy-source-arn $(aws sts get-caller-identity --query Arn --output text) \
  --action-names ec2:DescribeInstances \
  --resource-arns "*"

Issue 3: Large State File Refresh Performance

# Problem: Refresh takes too long with many resources
# Solution: Use targeted refresh

# Refresh specific resource types
terraform plan -refresh-only -target=aws_instance

# Or refresh by module
terraform plan -refresh-only -target=module.web_servers

Issue 4: Inconsistent Refresh Results

# Problem: Different results on repeated refresh
# Solution: Check for eventual consistency issues

# Wait between operations
sleep 30
terraform plan -refresh-only

# Check for resource creation in progress
aws ec2 describe-instances --instance-ids $INSTANCE_ID \
  --query 'Reservations[0].Instances[0].State.Name'

Refresh-Only vs Other State Operations

Comparison Matrix

OperationModifies StateModifies InfrastructurePreview AvailableRollback Possible
terraform refresh
terraform plan -refresh-onlyN/A
terraform apply -refresh-only
terraform apply⚠️

When to Use Each

Use refresh-only when:

  • You want to update state to match reality
  • No infrastructure changes are needed
  • You're investigating drift
  • Regular state maintenance

Use regular apply when:

  • You want to enforce configuration
  • Infrastructure needs to be modified
  • Reverting manual changes

Use plan only when:

  • Just checking for differences
  • Regular monitoring
  • Before making decisions

Key Takeaways

  • Refresh-only mode is safer than traditional refresh
  • Always preview refresh changes with plan -refresh-only
  • Use plan files for important refresh operations
  • Refresh-only updates state without changing infrastructure
  • Combine refresh-only with configuration decisions
  • Monitor refresh frequency to identify drift patterns
  • Use targeted refresh for large infrastructures
  • Document refresh operations and decisions
  • Integrate refresh checks into CI/CD pipelines

Next Steps

  1. Complete Tutorial 13: Manage Resource Targeting
  2. Learn about workspace management strategies
  3. Explore advanced state management techniques
  4. Practice with complex multi-resource refresh scenarios

Additional Resources