Fatskills
Practice. Master. Repeat.
Study Guide: TECH **Terraform CLI Workflow: `init`, `plan`, `apply`, `destroy`**
Source: https://www.fatskills.com/cloud-application-developer/chapter/tech-terraform-cli-workflow-init-plan-apply-destroy

TECH **Terraform CLI Workflow: `init`, `plan`, `apply`, `destroy`**

By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.

⏱️ ~8 min read

Terraform CLI Workflow: init, plan, apply, destroy

A Hyper-Practical, Zero-Fluff Study Guide


1. What This Is & Why It Matters

Terraform’s CLI workflow (init, plan, apply, destroy) is the core loop of Infrastructure as Code (IaC). Think of it like a construction crew’s playbook: - init = Unloading tools and materials (setting up the environment).
- plan = Reviewing the blueprint (what will change?).
- apply = Building the structure (deploying resources).
- destroy = Demolishing it (cleaning up).

Why this matters in production:
- Safety: plan acts as a dry run—preventing accidental deletions or misconfigurations.
- Auditability: Every change is logged, version-controlled, and repeatable.
- Collaboration: Teams review plan outputs before apply, reducing "oops" moments.
- Cost control: destroy lets you tear down unused resources (e.g., dev environments after hours).

Real-world scenario:
You inherit a Terraform repo with 50+ AWS resources. The last engineer left no documentation. You need to: 1. Safely update a VPC’s CIDR block without breaking production.
2. Verify changes before applying them.
3. Clean up unused resources to cut costs.

If you skip plan or init, you risk: - Downtime (e.g., accidentally deleting a database).
- Cost spikes (e.g., leaving orphaned EC2 instances running).
- Configuration drift (manual changes not tracked in code).


2. Core Concepts & Components


terraform init

  • Definition: Initializes a Terraform working directory by downloading providers, modules, and backend configurations.
  • Production insight: Always run init after cloning a repo or adding a new provider. Missing providers = failed deployments.
  • Key flags:
  • -upgrade: Forces download of the latest provider versions (use cautiously in prod).
  • -backend-config: Overrides backend settings (e.g., S3 bucket for remote state).

terraform plan

  • Definition: Generates an execution plan showing what Terraform will change (add, modify, destroy).
  • Production insight: Treat plan like a code review. Never apply without reviewing it first.
  • Key flags:
  • -out=tfplan: Saves the plan to a file for later apply.
  • -var-file=prod.tfvars: Uses a specific variable file (e.g., for production vs. staging).
  • -destroy: Shows what would be destroyed (useful for cleanup).

terraform apply

  • Definition: Executes the changes proposed by plan to reach the desired state.
  • Production insight: Always apply with -auto-approve=false in CI/CD (force manual review).
  • Key flags:
  • -auto-approve: Skips interactive approval (dangerous in prod!).
  • -target=aws_instance.web: Applies changes only to a specific resource (use sparingly).

terraform destroy

  • Definition: Removes all resources managed by the Terraform configuration.
  • Production insight: Use destroy for ephemeral environments (e.g., CI/CD test clusters). Never run it blindly in prod!
  • Key flags:
  • -target=aws_s3_bucket.logs: Destroys only a specific resource.
  • -auto-approve: Skips confirmation (risky—always double-check).

Terraform State

  • Definition: A JSON file (terraform.tfstate) that maps real-world resources to your configuration.
  • Production insight: State is critical—losing it means Terraform can’t manage resources. Always use remote state (e.g., S3 + DynamoDB lock).

Idempotency

  • Definition: Running apply multiple times produces the same result (no duplicate resources).
  • Production insight: Terraform is idempotent by design. If apply keeps creating resources, your config is wrong.

Backend

  • Definition: Where Terraform stores state (local file, S3, Terraform Cloud, etc.).
  • Production insight: Local state is not safe for teams. Use remote state with locking (e.g., S3 + DynamoDB).

Providers

  • Definition: Plugins that interact with APIs (AWS, Azure, GCP, Kubernetes, etc.).
  • Production insight: Provider versions can break your config. Pin versions in required_providers.


3. Step-by-Step Hands-On: Deploy an AWS EC2 Instance


Prerequisites

  • AWS account with IAM admin permissions.
  • AWS CLI configured (aws configure).
  • Terraform installed (brew install terraform or download here).

Task: Deploy a t2.micro EC2 instance in us-east-1 with a security group allowing SSH.

Step 1: Create a Terraform config file

mkdir tf-demo && cd tf-demo
touch main.tf

Step 2: Write the configuration

# main.tf
terraform {
  required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # Pin to avoid breaking changes
} } } provider "aws" { region = "us-east-1" } resource "aws_instance" "web" { ami = "ami-0c55b159cbfafe1f0" # Amazon Linux 2 (us-east-1) instance_type = "t2.micro" tags = {
Name = "tf-demo-web" } } resource "aws_security_group" "ssh" { name = "allow_ssh" description = "Allow SSH inbound traffic" ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # ⚠️ Open to the world (fix in prod!) } }

Step 3: Initialize Terraform

terraform init

Expected output:


Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.19.0...
- Installed hashicorp/aws v5.19.0 (signed by HashiCorp) Terraform has been successfully initialized!

Step 4: Generate and review the plan

terraform plan

Key things to check in the output:
- + aws_instance.web (resource will be created).
- + aws_security_group.ssh (security group will be created).
- No ~ (modify) or - (destroy) actions (this is a new deployment).


Step 5: Apply the changes

terraform apply
  • Terraform will show the plan again and ask for confirmation.
  • Type yes to proceed.

Expected output:


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

Step 6: Verify the deployment

aws ec2 describe-instances --filters "Name=tag:Name,Values=tf-demo-web" --query "Reservations[].Instances[].PublicIpAddress" --output text
  • This should return the public IP of your EC2 instance.

Step 7: Clean up (destroy)

terraform destroy
  • Type yes to confirm.
  • Expected output:
    Destroy complete! Resources: 2 destroyed.


4. ? Production-Ready Best Practices


Security

  • Least privilege IAM: Use IAM roles with minimal permissions (e.g., AmazonEC2FullAccess instead of AdministratorAccess).
  • Secrets management: Never hardcode secrets in .tf files. Use:
  • sensitive = true for variables.
  • AWS Secrets Manager or HashiCorp Vault.
  • State encryption: Enable encryption for remote state (e.g., S3 server-side encryption).
  • Locking: Use DynamoDB for state locking to prevent concurrent apply conflicts.

Cost Optimization

  • Right-size resources: Use t3.micro instead of t2.micro for burstable workloads.
  • Spot instances: For non-critical workloads, use aws_spot_instance_request.
  • Auto-scaling: Combine EC2 with aws_autoscaling_group to scale down at night.
  • Tagging: Always tag resources (e.g., Environment = "dev") for cost tracking.

Reliability & Maintainability

  • Modularize code: Break configs into modules (e.g., modules/vpc, modules/ec2).
  • Version pinning: Pin provider versions to avoid breaking changes.
    hcl terraform {
    required_providers {
    aws = {
    source = "hashicorp/aws"
    version = "~> 5.0" # Allows 5.x but not 6.0
    }
    } }
  • Remote state: Use S3 + DynamoDB for state storage.
    hcl terraform {
    backend "s3" {
    bucket = "my-tf-state-bucket"
    key = "prod/terraform.tfstate"
    region = "us-east-1"
    dynamodb_table = "terraform-lock"
    } }
  • Idempotency: Ensure apply is repeatable (no duplicate resources).

Observability

  • Logging: Enable AWS CloudTrail to log Terraform API calls.
  • Monitoring: Use CloudWatch alarms for resource metrics (e.g., EC2 CPU usage).
  • Tagging: Tag all resources for cost allocation and ownership.
    hcl tags = {
    Owner = "team-infra"
    Environment = "prod"
    Terraform = "true" }


5. ⚠️ Common Mistakes & Traps

Mistake Symptom Fix/Prevention
Running apply without plan Accidental deletions or misconfigurations. Always run plan first and review the output.
Hardcoding secrets Secrets exposed in Git history. Use sensitive = true for variables and store secrets in AWS Secrets Manager.
No remote state State file lost or corrupted. Use S3 + DynamoDB for remote state.
No state locking Concurrent apply conflicts. Enable DynamoDB locking for S3 backend.
Not pinning provider versions Breaking changes in new provider releases. Pin versions in required_providers.
destroy in production Accidental deletion of critical resources. Use -target to destroy specific resources, or avoid destroy in prod entirely.


6. ? Exam/Certification Focus


Typical Question Patterns

  1. "What does terraform init do?"
  2. Correct answer: Downloads providers and modules.
  3. Trap: "Initializes the state file" (wrong—state is initialized on first apply).

  4. "What happens if you run terraform apply twice?"

  5. Correct answer: Nothing (idempotency).
  6. Trap: "It creates duplicate resources" (wrong—Terraform tracks state).

  7. "How do you save a plan for later use?"

  8. Correct answer: terraform plan -out=tfplan.
  9. Trap: "Use terraform save" (doesn’t exist).

  10. "What’s the safest way to destroy a single resource?"

  11. Correct answer: terraform destroy -target=aws_instance.web.
  12. Trap: "Delete the resource block and run apply" (this works but is riskier).

Key ⚠️ Trap Distinctions

  • plan vs. apply: plan is a dry run; apply makes changes.
  • State file: Local state is not safe for teams (use remote state).
  • destroy: Irreversible—always double-check the plan.

Scenario-Based Question

"You need to update a production VPC’s CIDR block without downtime. What’s the safest approach?"
- Correct answer:
1. Run terraform plan to review changes.
2. Use terraform apply -target=aws_vpc.prod to update only the VPC.
3. Verify connectivity before updating dependent resources.
- Trap: Running apply without -target could modify unrelated resources.


7. ? Hands-On Challenge

Challenge:
Deploy an AWS S3 bucket with versioning enabled, then modify the configuration to add a lifecycle rule that transitions objects to Glacier after 30 days. Verify the changes with plan before applying.

Solution:


# main.tf
resource "aws_s3_bucket" "logs" {
  bucket = "my-unique-bucket-name-12345" # Must be globally unique!
}

resource "aws_s3_bucket_versioning" "logs" {
  bucket = aws_s3_bucket.logs.id
  versioning_configuration {
status = "Enabled" } } resource "aws_s3_bucket_lifecycle_configuration" "logs" { bucket = aws_s3_bucket.logs.id rule {
id = "glacier-transition"
status = "Enabled"
transition {
days = 30
storage_class = "GLACIER"
} } }

Steps:
1. Run terraform init.
2. Run terraform plan and verify:
- + aws_s3_bucket.logs (bucket will be created).
- + aws_s3_bucket_versioning.logs (versioning enabled).
- + aws_s3_bucket_lifecycle_configuration.logs (lifecycle rule added).
3. Run terraform apply.
4. Verify in the AWS Console: Bucket → Management → Lifecycle rules.

Why it works:
- Terraform tracks the state of the bucket and applies only the new lifecycle rule.
- Versioning is enabled before the lifecycle rule (order matters!).


8. ? Rapid-Reference Crib Sheet

Command Purpose Key Flags
terraform init Initialize working directory (download providers/modules). -upgrade, -backend-config=backend.tfvars
terraform plan Show changes Terraform will make. -out=tfplan, -var-file=prod.tfvars, -destroy
terraform apply Apply changes to reach desired state. -auto-approve=false, -target=aws_instance.web
terraform destroy Remove all managed resources. -target=aws_s3_bucket.logs, -auto-approve
terraform state list List resources in state.
terraform state show Show details of a resource in state. terraform state show aws_instance.web
terraform output Show outputs (e.g., public IP).
terraform validate Check syntax errors.
terraform fmt Format .tf files to canonical style. -recursive (format all files in dir)

⚠️ Exam Traps:
- terraform init does not create resources—it just sets up the environment.
- terraform plan is read-only—it doesn’t modify anything.
- terraform destroy is permanent—always review the plan first.
- Remote state is required for teams (local state is for solo dev only).


9. ? Where to Go Next

  1. Terraform Docs: CLI Commands
  2. Terraform Best Practices (Official Guide)
  3. AWS Provider Docs
  4. Terraform Up & Running (Book) – Great for real-world patterns.


ADVERTISEMENT