Apply #

terraform apply is the command that actually changes your infrastructure. Everything you planned in plan gets executed here. Because the impact is real and immediate, understanding how apply works — including what happens when something goes wrong mid-way — is the difference between a controlled deployment and an unnecessary incident.

How Apply Works #

flowchart TD
    A["terraform apply"] --> B{"Was a saved plan\nprovided?"}

    B -->|"Yes: apply tfplan"| C["Execute the\nsaved plan directly"]
    B -->|"No: plain apply"| D["Generate a new plan"]

    D --> E["Show the plan\nAsk for 'yes' confirmation"]
    E --> F["Confirmation received"]

    C --> G["Execute according to\nthe dependency graph"]
    F --> G

    G --> H["Independent resources\nexecuted in parallel\n(max 10 concurrent)"]
    H --> I["State updated after\neach resource completes"]
    I --> J["Show summary\nApply complete!"]

    I -.->|"Failed\nmid-way?"| K["Partial state:\nresources that already\nsucceeded remain recorded"]

    style A fill:#e3f2fd,stroke:#1565c0
    style J fill:#e8f5e9,stroke:#2e7d32
    style K fill:#fff3e0,stroke:#e65100
StepWhat It DoesNotes
1. Check for a saved planIf a plan file was given, use itSafest — no re-planning
2. Generate a plan (if needed)Build the execution plan from config + stateSame as terraform plan
3. ConfirmationUser types yesSkipped with -auto-approve or a saved plan
4. ExecuteRun operations per the dependency graphIndependent resources run in parallel
5. Update stateAfter each resource completesIncremental, not at the end
6. SummaryShow add/change/destroy countsIncludes output values

The key point in step 5: state is updated after each resource, not at the end after everything finishes. This is fundamental to understanding what happens when apply fails mid-way.

Interactive vs Automatic Apply #

flowchart TD
    A["Three apply modes"] --> B["Interactive\n(terminal)"]
    A --> C["Auto-approve\n(CI/CD)"]
    A --> D["Saved plan\n(safest)"]

    B --> B1["terraform apply\nThe user types 'yes'"]
    C --> C1["terraform apply -auto-approve\nExecutes directly"]
    D --> D1["terraform apply tfplan\nExecutes the saved plan"]

    B1 --> E["⚠️ A new plan can\ndiffer from what\nwas reviewed earlier"]
    C1 --> E
    D1 --> F["✅ Exactly what\nwas reviewed"]

    style E fill:#fff3e0,stroke:#e65100
    style F fill:#e8f5e9,stroke:#2e7d32
ModeCommandConfirmationNew Plan?Best For
Interactiveterraform applyType yes✅ YesDevelopment, exploration
Auto-approveterraform apply -auto-approveNo✅ YesCI/CD with existing review
Saved planterraform apply tfplanNo❌ NoProduction — safest
# Interactive apply (default) — asks for confirmation before executing
terraform apply

# Output:
# Plan: 3 to add, 1 to change, 0 to destroy.
#
# Do you want to perform these actions?
#   Terraform will perform the actions described above.
#   Only 'yes' will be accepted to approve.
#
#   Enter a value: yes  ← type this

# Automatic apply — no confirmation (for CI/CD)
terraform apply -auto-approve

# Apply from a saved plan — safest for production
terraform plan -out=tfplan
terraform apply tfplan
# No confirmation, no new plan — executes the saved plan directly

What Happens When Apply Fails Mid-Way #

This is the scenario that causes the most confusion. Because state is updated incrementally, a mid-apply failure produces partial state.

flowchart TD
    A["Apply starts\ntarget: 5 resources"] --> B["✅ aws_vpc.main\ncreated, state updated"]
    B --> C["✅ aws_subnet.public\ncreated, state updated"]
    C --> D["❌ aws_instance.web\nFAILED! AMI not found"]
    D --> E["⛔ aws_security_group\nnever executed"]
    E --> F["⛔ aws_rds.database\nnever executed"]

    D --> G["State now:\ncontains the VPC + subnet\nthat already succeeded"]

    G --> H["Solution: Fix the\ncause of the failure"]
    H --> I["terraform apply again"]
    I --> J["✅ Terraform continues\nfrom the unfinished\nresources\nVPC & subnet are NOT\nrecreated"]

    style B fill:#e8f5e9,stroke:#2e7d32
    style C fill:#e8f5e9,stroke:#2e7d32
    style D fill:#ffebee,stroke:#c62828
    style E fill:#ffebee,stroke:#c62828
    style F fill:#ffebee,stroke:#c62828
    style J fill:#e8f5e9,stroke:#2e7d32
SCENARIO: Apply fails after creating 2 of 5 resources

Before apply:
  State: empty

Apply starts:
  ✓ aws_vpc.main         → created, state updated
  ✓ aws_subnet.public    → created, state updated
  ✗ aws_instance.web     → FAILED (e.g. AMI not found)
  - aws_security_group   → never executed
  - aws_rds.database     → never executed

After failed apply:
  State: contains aws_vpc.main and aws_subnet.public
         (the resources that were successfully created)

What to do:
  1. Fix the cause of the failure (e.g. change the AMI to a valid one)
  2. Run terraform plan to see the current condition
  3. Run terraform apply again
  → Terraform will continue from the resources not yet created
  → The VPC and subnet won't be recreated (already in state)

Terraform does not roll back automatically. This is intentional — automatic rollback can be dangerous if the resources already created contain data.

When Apply FailsWhat HappensWhat to Do
Resource A succeeded, B failedA is in state, B isn’tFix the cause, apply again
Permission errorThe last resource failedCheck IAM policy, apply again
API rate limit errorSeveral resources failedReduce -parallelism, apply again
Corrupted state (rare)Inconsistent stateRestore from a state backup
Never run terraform destroy in response to a partial apply failure unless you’re truly sure you want to delete all resources. Just fix the problem and run terraform apply again — Terraform will continue from where it left off.

Applying with Targets #

-target allows applying only specific resources, ignoring everything else.

flowchart TD
    A["Need to apply\nspecific resources?"] --> B{"What's the reason?"}

    B -->|"Emergency fix\nin production"| C["terraform apply\n-target=resource\n✅ Fine occasionally"]
    B -->|"Debugging\na single resource"| D["terraform apply\n-target=resource\n✅ Fine occasionally"]
    B -->|"Daily workflow"| E["❌ DON'T use\n-target"]
    B -->|"Don't want to apply\nall resources"| F["Separate into a\ndifferent directory/module"]

    E --> E1["Use the normal flow:\nterraform apply\nAll resources"]

    style C fill:#e8f5e9,stroke:#2e7d32
    style D fill:#e8f5e9,stroke:#2e7d32
    style E fill:#ffebee,stroke:#c62828
    style F fill:#e3f2fd,stroke:#1565c0
# Apply only one specific resource
terraform apply -target=aws_instance.web

# Apply an entire module
terraform apply -target=module.vpc

# Apply several resources at once
terraform apply -target=aws_instance.web -target=aws_security_group.web
Scenario-target?Alternative
Emergency fix in production✅ Fine
Debugging a single resource✅ Fine
Routine daily apply❌ AvoidApply all resources
Only want to apply part❌ AvoidSplit into modules/directories
-target is useful for emergencies or debugging, but avoid routine use. It can make state inconsistent with the configuration because dependencies between resources aren’t fully evaluated. If you frequently need -target, that’s a sign your configuration may need refactoring.

Apply Parallelism #

By default, Terraform runs up to 10 operations in parallel. This can be adjusted as needed.

flowchart LR
    subgraph "Default (10 parallel)"
        R1["Resource 1"] --> OK1["✅"]
        R2["Resource 2"] --> OK2["✅"]
        R3["Resource 3"] --> OK3["✅"]
        DOT1["..."] --> DOT1O["✅"]
        R10["Resource 10"] --> OK10["✅"]
    end

    subgraph "Limited (3 parallel)"
        R11["Resource 1"] --> OK11["✅"]
        R12["Resource 2"] --> WAIT["⏳ waiting"]
        R13["Resource 3"] --> WAIT
        WAIT --> OK12["✅"]
        WAIT --> OK13["✅"]
    end
# Reduce parallelism (useful if the provider has rate limits)
terraform apply -parallelism=5

# Increase parallelism (be careful with provider rate limits)
terraform apply -parallelism=20

# For large infrastructures with many resources,
# AWS/GCP provider rate limits can become a bottleneck.
# Reducing -parallelism can help avoid throttling.
-parallelismBest ForRisk
3-5Providers with strict rate limitsSlower
10 (default)Most casesBalanced
15-20Very large infrastructuresAPI throttling

Safe Apply Strategy for Production #

flowchart TD
    A["1. terraform plan -out=tfplan\nGenerate and save the plan"] --> B["2. terraform show tfplan\nReview the plan (human-readable)"]
    B --> C["3. Submit for approval\nPR review, Slack notification, etc."]
    C --> D["4. Approval received"]
    D --> E["5. terraform apply tfplan\nApply from the saved plan"]
    E --> F["6. rm tfplan\nDelete the plan file containing sensitive data"]
    F --> G["✅ Deployment complete\nAudit trail saved"]

    style A fill:#e3f2fd,stroke:#1565c0
    style E fill:#e8f5e9,stroke:#2e7d32
    style G fill:#e8f5e9,stroke:#2e7d32
# RECOMMENDED WORKFLOW FOR PRODUCTION:

# 1. Generate and save the plan
terraform plan -out=tfplan

# 2. Review the plan (human-readable)
terraform show tfplan

# 3. If there's CI/CD — submit for approval
# (PR review, Slack notification, etc.)

# 4. Once approved, apply from the saved plan
terraform apply tfplan

# 5. Delete the saved plan when done
rm tfplan

# Why use a saved plan?
# - No re-planning during apply — what's reviewed is exactly what's executed
# - No surprises from condition changes between review and apply
# - Clear audit trail: the plan file can be kept as evidence

Reading Apply Output #

$ terraform apply tfplan

aws_vpc.main: Creating...
aws_vpc.main: Creation complete after 2s [id=vpc-0abcdef1234567890]
aws_subnet.public: Creating...
aws_subnet.public: Creation complete after 1s [id=subnet-0abcdef1234567890]
aws_instance.web: Creating...
aws_instance.web: Still creating... [10s elapsed]
aws_instance.web: Still creating... [20s elapsed]
aws_instance.web: Creation complete after 23s [id=i-0abcdef1234567890]

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

Outputs:

instance_public_ip = "54.123.45.67"

Terraform shows progress in real time. Resources that take a long time (like EC2 instances or RDS) will show “Still creating…” every 10 seconds.

Output PartMeaningAction
Creating...The resource is being createdWait
Still creating... [Xs elapsed]Still in progressNormal for large resources
Creation complete after Xs [id=...]SucceededNote the ID if needed
Error: ...FailedRead the error, fix, apply again
Apply complete! Resources: X added...DoneVerify the results in the cloud console

Summary #

  • State is updated incrementally as each resource completes — not at the end. A mid-apply failure produces a valid partial state.
  • There’s no automatic rollback — once a resource is created, Terraform won’t delete it if the next resource fails. Just fix the problem and re-apply.
  • Three apply modes: interactive (type yes), auto-approve (-auto-approve for CI/CD), and saved plan (apply tfplan for production — safest).
  • Use saved plans for production — make sure what’s applied is exactly what was reviewed.
  • -auto-approve is only for CI/CD with an existing review process — don’t use it directly in a production terminal.
  • -target is an emergency tool, not a routine workflow — overuse can cause inconsistent state.
  • -parallelism can be adjusted — reduce it if you hit provider API rate limits, increase it for very large infrastructures.
  • If apply fails mid-way, fix the cause then re-apply — Terraform will continue from the unfinished resources.

← Previous: Plan   Next: Destroy →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact