Skip to main content
RunBook Academy

TerraformV · The Terraform WorkflowApply

terraform apply: From Plan to Reality

Intermediate⏱ ~22 min🧪 Lab requiredbashterraform

What you'll learn

  • Describe the apply workflow and approval gates
  • Explain the difference between interactive apply and saved-plan apply
  • Recognise the structure of a partial apply failure
  • Apply a production apply workflow

Prerequisites

Verified against Terraform CLI 1.9.x · OpenTofu 1.7.x · HCL 2.0 · bpg/proxmox provider 0.66+ · hashicorp/local provider 2.5+ · hashicorp/null provider 3.2+ · hashicorp/random provider 3.6+ · hashicorp/http provider 3.4+ · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · 2026-08-12

Not yet marked complete on this device.

Apply is the moment the plan becomes reality. Every consideration that previously existed only in the configuration, the state, and the plan output now touches real infrastructure. This lesson covers what apply does, how to control it, and how to recover when it fails halfway through.

What apply does

The apply command:

  1. Re-runs the plan (unless a saved plan is provided).
  2. If the plan is empty, exits without changing anything.
  3. Otherwise, prompts for approval (unless -auto-approve is set).
  4. Walks the resource graph in dependency order.
  5. For each resource, sends the appropriate API call to the provider.
  6. Updates state after each successful operation.
  7. Writes the final state to the backend.

The apply is the irreversible step. The plan is a proposal. The apply is the change.

Interactive apply vs saved-plan apply

Two modes that produce different audit trails:

Interactive apply

terraform apply
Plan: 2 to add, 0 to change, 0 to destroy, 0 to replace.

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

The plan is computed at apply time. The engineer sees the plan and decides whether to proceed. The audit trail is the engineers “yes” — which is logged in the apply output but not separately stored.

Pitfall: the plan is computed at the moment of apply. If the state or configuration has changed between terraform plan and terraform apply, the apply produces a different plan. The engineer sees the new plan and approves it. This is fine if the engineer notices the new plan is different from what they expected. It is a problem if the difference is subtle.

Saved-plan apply

terraform plan -out=production.tfplan
terraform apply production.tfplan

The plan is computed once, written to a file, and applied verbatim. The apply does not re-plan; it executes the saved plan exactly. The audit trail is the saved plan file, which can be archived, reviewed, and compared to the actual diff.

A saved plan is the operational version of a code review. It says “this is what we agreed to apply; nothing else is acceptable”. A production CI/CD pipeline should use saved-plan apply.

The apply process in detail

The apply walks the resource graph in topological order. For each resource, the core asks the provider to perform the change:

Plan: 1 to add, 0 to change, 0 to destroy.

aws_security_group.alb: Creating...
aws_security_group.alb: Still creating... [10s elapsed]
aws_security_group.alb: Still creating... [20s elapsed]
aws_security_group.alb: Creation complete after 25s [id=sg-0abc123def456789]

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

The state is updated after each individual resource. This is intentional: it means that if the apply fails halfway through, the state records what succeeded.

Parallelism

By default, the apply executes up to 10 independent resources in parallel. The default is configurable with -parallelism=N:

terraform apply -parallelism=20

Increasing parallelism:

  • Does not change the plan.
  • Reduces wall-clock time when the graph has many independent resources.
  • May exhaust provider API rate limits.
  • Does not change the correctness of the apply.

Reducing parallelism:

  • Slows down the apply.
  • Helps when the provider is rate-limited.
  • Helps when the apply is causing observable problems in the target environment.

A production default of 10 is reasonable. Tuning is a follow-up.

Refresh during apply

By default, the apply does not refresh state before applying. The plan used the state as it was when the plan ran. If the real world has changed since the plan, the apply may:

  • Fail because the resource is no longer in the expected state.
  • Succeed but produce a different result than the plan implied.
  • Succeed and diverge from reality, leaving state stale.

The course returns to this in Part XL (Refresh Behaviour).

What happens when apply fails midway

The canonical case: 6 resources planned, 4 succeeded, 5th failed.

Plan: 6 to add, 0 to change, 0 to destroy.

aws_security_group.alb: Creating...
aws_security_group.alb: Creation complete after 25s [id=sg-0abc123def456789]

aws_lb_target_group.api: Creating...
aws_lb_target_group.api: Creation complete after 18s [id=arn:...]

aws_lb.api: Creating...
aws_lb.api: Still creating... [30s elapsed]
aws_lb.api: Still creating... [1m elapsed]
aws_lb.api: Error: error creating API Gateway Load Balancer: AccessDeniedException

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

What state looks like:

  • 2 resources added successfully, in state.
  • 1 resource failed, in state with partial attributes.
  • 3 resources not yet started, not in state.

What does the next plan do?

Plan: 4 to add, 0 to change, 0 to destroy.

The plan reads the state. It sees the 2 added resources as current. It sees the 3 not-started resources as needing to be created. The 1 failed resource is not in state at all (because the provider did not return a successful response), so it does not appear in the next plan unless the engineer re-attempts it.

The recovery procedure is:

  1. Investigate the failure. What went wrong? Was it a permissions error, a quota error, a network failure, or something else?
  2. Fix the cause. If it was a permissions error, grant the missing permission. If it was a quota error, request a quota increase. If it was a configuration error, fix the configuration.
  3. Re-plan. terraform plan should now show the 4 resources that were not yet created.
  4. Review the plan. Compare to the expected diff.
  5. Apply. The apply will create the remaining resources.

Why there is no transactional rollback

A common user expectation: “if apply fails halfway, Terraform should undo the changes it made earlier”.

Terraform does not do this, and the design choice is intentional:

  • Rollback is provider-specific. Some providers can roll back a creation; others cannot. A database creation is not reversible. An IAM role creation is reversible.
  • Rollback is state. Rolling back means creating new resources to undo the earlier ones. The new resources may also fail.
  • Rollback hides the failure. The engineers attention should be on the failure, not on a partially-completed rollback.

The courses recommendation: plan for partial apply. Work the configuration into a sequence where any single failure is visible and recoverable. Use prevent_destroy on resources that should never be destroyed. Use lifecycle.precondition to verify assumptions before applying.

Failure modes during apply

The most common failure categories:

FailureSymptomRecovery
Permission deniedProvider returns 403Grant the missing permission; verify with a manual API call
Quota exceededProvider returns 429 or quota errorRequest quota increase; consider smaller batches
Resource conflictProvider returns a uniqueness errorReconcile with the real-world resource; import or remove
Network failureTCP or TLS errorRetry; check providers status page
API outageProvider returns 5xxWait for provider to recover; re-plan
Configuration errorTerraform validates the plan, but the provider rejects the applyFix the configuration; re-plan
State mismatchProviders view of the resource is inconsistent with stateInvestigate; terraform state commands to reconcile

The first three categories (permission, quota, conflict) are the most common in production. They are also the most controllable: the relevant IAM policy and quota should be checked before the apply.

Apply in CI/CD

A production CI/CD pipeline uses saved-plan apply:

# Save the plan in CI
terraform init -input=false
terraform validate
terraform plan -out=tfplan -input=false

# Run the plan as a "test" that captures the diff
terraform show -json tfplan > plan.json

# Wait for human approval of the PR
# ...

# After approval, apply the saved plan
terraform apply -input=false tfplan

The CI pipeline never invokes terraform apply without a saved plan. The saved plan is the contract between the engineer and the CI pipeline.

Some CI pipelines also automatically upload the saved plan to artifact storage, so the audit trail is preserved:

artifacts:
  paths:
    - tfplan
  expire_in: 1 year

The course has a dedicated lab for the CI workflow in Part LXXXVII.

When to use -auto-approve

The -auto-approve flag is appropriate in three contexts:

  1. CI/CD pipelines that have already approved the plan via a separate gate (PR review, manual approval, policy engine).
  2. Emergency operations that have gone through explicit out-of-band approval and need to apply without delay.
  3. Disaster recovery where the saved plan is the only record of what was intended.

It is not appropriate for:

  • A new engineer who has not yet learned the plan reading workflow.
  • A change that “should be fine” without a separate review.
  • An apply that has not been costed.

The apply output

The apply output is the operational record of what changed:

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

This is the summary. The full output lists the timing and outcome of each resource. A production apply log should be archived alongside the saved plan.

What comes next

The next lesson is terraform destroy — the most dangerous operation in Terraform, the operation that turns a configuration no longer in use into infrastructure that no longer exists.

Knowledge check · 7 questions

  1. Q1. What is the role of terraform fmt?

  2. Q2. What is the role of terraform validate?

  3. Q3. What is the role of terraform init?

  4. Q4. terraform plan modifies the real world.

  5. Q5. Which of the following are part of the safe workflow? (Select all that apply.)

  6. Q6. What does terraform show do?

  7. Q7. A team runs terraform plan and sees no changes. The apply also shows no changes. The real world has drifted. What should they do?

Passing score: 75%. Answers are checked in this browser.