Terraform Apply Basics
Learn how to apply infrastructure with terraform apply — the core command that turns your configuration into real resources. This hands-on lesson covers the apply workflow, options, and troubleshooting.
Focus: apply infrastructure with terraform apply
You've written a Terraform configuration, run terraform init and terraform plan, and seen a list of changes that would happen. But nothing actually exists in your cloud account yet. The gap between a great plan and real infrastructure is exactly where terraform apply lives—and this lesson closes that gap for good.
The problem this lesson solves
A configuration file is just text. Until you run terraform apply, your AWS instances, VPCs, or Kubernetes clusters are nothing more than declarations. Many beginners stop after plan because they're afraid apply might break something—or worse, they skip plan entirely and run apply blindly. Both approaches lead to confusion, wasted time, and occasionally expensive mistakes.
The real pain is uncertainty: What exactly will change? What if I make a typo? How do I undo it? terraform apply is the bridge between your intent and your actual infrastructure, and once you understand its workflow, you'll stop guessing and start shipping.
Core concept / mental model
Think of terraform apply as the execution engine of Terraform. If plan is the blueprint and validate is the lint check, apply is the construction crew that reads the blueprint and builds exactly what it says.
Here's a simple analogy: you're renovating your house. init downloads the tools and materials list. plan walks through the rooms and shows you which walls will be torn down and which will be added. apply signs off on the plan and the crew starts swinging hammers.
Technically, apply performs three actions:
1. Refresh: Reads the current state of your infrastructure (unless you disable it with -refresh=false).
2. Diff: Compares desired config vs. actual state and computes a delta.
3. Execute: Creates, updates, or destroys resources to match the desired state, then writes the new state to your state file.
The key idea: apply is deterministic and idempotent. Running it twice with no config changes results in no changes the second time. That's the superpower of infrastructure-as-code—your config is the single source of truth.
Pro tip:
terraform applywill automatically run a plan first and ask for your confirmation. You can skip the separateplanstep if you're confident, but always review the output before typingyes.
How it works step by step
When you run terraform apply in a directory with .tf files, this is what happens under the hood:
- Initialize & read state — Terraform loads your state file (local
terraform.tfstateor remote backend). - Refresh resources — It queries your providers (AWS, GCP, etc.) to get the actual current attributes of each managed resource. This may take a few seconds per resource.
- Compute the diff — It compares the refreshed state against your configuration. Anything that differs becomes an action in the plan.
- Show the plan — You see a human-readable summary with
+(create),-(destroy),~(modify in-place), and-/+(replace). - Wait for confirmation — Unless you pass
-auto-approve, Terraform promptsEnter a value:and waits foryes. - Apply changes — Resources are created/updated/destroyed in dependency order. Each operation streams logs like
aws_instance.web: Creating.... - Write new state — After success, the state file is updated and saved.
If anything fails mid-apply, Terraform partially applies—resources that succeeded remain, but the state is not fully updated. That's why idempotency matters: re-running apply after a fix will complete the missing pieces.
Hands-on walkthrough
Let's make this concrete. Create a simple AWS EC2 instance (or just use a null_resource if you don't have cloud credentials—the flow is identical).
1. Write a minimal config
# main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "terraform-apply-demo"
}
}
2. Initialize and plan
terraform init
terraform plan
You'll see output like:
Terraform will perform the following actions:
# aws_instance.web will be created
+ resource "aws_instance" "web" {
+ ami = "ami-0c55b159cbfafe1f0"
+ instance_type = "t2.micro"
+ tags = {
+ "Name" = "terraform-apply-demo"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
3. Apply it
terraform apply
Terraform will re-run the plan, then prompt:
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 yes and watch the magic:
aws_instance.web: Creating...
aws_instance.web: Still creating... [10s elapsed]
aws_instance.web: Creation complete after 12s [id=i-0a1b2c3d4e5f67890]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
4. Verify and update
Now change the instance type to t3.micro and apply again:
terraform apply -auto-approve
This time you'll see ~ aws_instance.web (in-place update) and the config will be modified without destroying the instance.
Pro tip: Use
-auto-approvein CI/CD pipelines or when you're running scripts, but never in interactive demos where you want a human checkpoint.
Compare options / when to choose what
terraform apply has several flags that change its behavior. Here's a quick comparison of the most common ones:
| Flag/command | What it does | When to use it |
|---|---|---|
terraform apply |
Runs plan, prompts for approval | Interactive sessions, learning, small changes |
terraform apply -auto-approve |
Skips the approval prompt | CI/CD, automation, trusted changes |
terraform apply -target=aws_instance.web |
Applies only the specified resource and its dependencies | Testing a single change, urgent hotfix |
terraform apply -var-file=prod.tfvars |
Uses a specific variable file | Environment-specific deployments |
terraform apply -refresh=false |
Skips the refresh phase | Speed up large infrastructures when you trust state |
terraform apply -destroy |
Destroys all resources (same as destroy) |
Tearing down an environment |
Choosing between apply and plan: Always run terraform plan first when you're in an unfamiliar codebase or when the change is large. Use apply directly when you're confident and want speed. In automated pipelines, apply -auto-approve after a review of the plan artifact is the standard pattern.
Variations to consider:
- Remote backends (S3, Terraform Cloud): Your apply runs the same, but state is stored remotely, enabling team collaboration and locking.
- terraform apply -replace=aws_instance.web: Forces replacement of a resource, useful when an in-place update isn't enough (e.g., corrupt AMI).
- terraform apply -parallelism=N: Controls how many resources are created simultaneously. Lower it for rate-limit-sensitive APIs.
Troubleshooting & edge cases
Error: Error: Invalid legacy provider address
This happens when you've written provider = aws without the source in required_providers. Fix: re-run terraform init -upgrade after adding the source.
Error: Error: acquiring state lock
Someone else (or a previous process) is holding the state lock. This usually occurs with remote backends. Fix: wait a few seconds, or force unlock with terraform force-unlock <lock_id> (use only if you're sure the process is dead).
Wrong output: 0 added, 0 changed, 0 destroyed
This means your config matches the current state perfectly. If you expected changes, check that you didn't comment out a resource or change a variable input.
Partially applied state
If a apply fails midway (e.g., network timeout), resources that were created may remain but not be in the state. Re-run terraform apply; it should detect and adopt them. If not, you may need terraform import.
-auto-approve didn't skip the prompt
That's because you also need to pass -input=false if you have variable prompts that aren't set. Example: terraform apply -auto-approve -input=false.
What you learned & what's next
You now understand how to apply infrastructure with terraform apply—the command that turns your declarative configuration into real cloud resources. You learned the mental model of refresh→diff→execute, how to use flags like -auto-approve, what to do when things go wrong, and how to compare it with plan and other options.
You're ready to move to the next lesson in the track: Managing Terraform state. State files are the heart of reproducibility, and understanding how to store, inspect, and fix them will make you a truly confident Terraform practitioner.
Practice recap
Create a simple local file resource (using the local provider) and apply it with and without -auto-approve. Then modify the content argument and re-apply to see the in-place update. Finally, destroy it with terraform destroy and observe the state cleanup. This drill cements the apply workflow without any cloud costs.
Common mistakes
- Running
terraform applywithoutterraform initfirst—you'll get a provider error or a stale state. - Blindly typing
yeswithout reviewing the plan output, especially when destroy actions are included—always scan for-lines. - Using
-auto-approvein interactive sessions where you need a human checkpoint—this can lead to unintended changes. - Forgetting that
-targetapplies only the specified resource and its dependencies, not just the resource itself—unexpected dependencies may still be modified.
Variations
terraform apply -replace=aws_instance.webforces resource replacement, useful when an in-place update isn't enough.terraform apply -parallelism=5limits concurrent operations to avoid API rate limits.terraform apply -target=module.vpctargets an entire module, applying all resources within it.
Real-world use cases
- Deploying a new microservice: run
terraform applyto create the EC2 instance, security group, and load balancer in one go. - Scaling an existing environment: update
instance_typein config andapplyto modify a running instance in-place. - Automating CI/CD: push a change to a git repo, trigger a pipeline that runs
terraform apply -auto-approveafter a plan review.
Key takeaways
terraform applyis the execution engine that turns config into real resources, with refresh→diff→execute under the hood.- Always review the plan output before approving—look specifically for destroy actions and unexpected changes.
- Use
-auto-approveonly in automation, and-targetonly for focused changes. - Partial applies are normal on failures; re-running
applywill fix them due to idempotency. - State is updated only after successful apply—keep your state file safe and backed up.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.