Terraform Hands-On: Workflow, State Management, and Infrastructure Lifecycle - Part 3

Complete guide to Terraform commands: init, validate, fmt, plan, apply, show, state, output, and destroy. Learn infrastructure lifecycle management, state files, and troubleshooting with detailed output explanations for beginners.

18 min read

Introduction

Welcome to the final part of our hands-on Terraform series! You've installed Terraform, configured AWS, and written complete infrastructure configuration files. Now comes the most exciting partβ€”actually deploying real infrastructure to AWS and managing its entire lifecycle.

In this comprehensive guide, you'll execute every Terraform command, understand the detailed output, see your infrastructure come to life, and learn how to safely modify and destroy it. By the end, you'll have practical, hands-on experience with the complete Terraform workflow.

πŸ’‘

🎯 What You'll Learn in Part 3:

  • terraform init: Initialize your project and download providers
  • terraform validate: Verify configuration syntax
  • terraform fmt: Format code consistently
  • terraform plan: Preview infrastructure changes before applying
  • terraform apply: Create real AWS infrastructure
  • terraform output: Query and display output values
  • terraform show: Inspect current infrastructure state
  • terraform state: Understand and manage state files
  • Modifying infrastructure: Make changes and re-apply
  • terraform destroy: Safely remove all infrastructure
  • Understanding state files: Why they're critical and how they work
  • Troubleshooting: Common issues and solutions
  • Best practices: Production-ready workflows

Prerequisites:

  • Completed Part 1 (Terraform and AWS CLI installed)
  • Completed Part 2 (All .tf configuration files created)
  • Terminal open in your terraform-practice directory
  • AWS credentials configured
  • All six configuration files from Part 2

Series Progress:

  • Part 1: βœ… Installation and AWS setup (completed)
  • Part 2: βœ… Configuration files and resources (completed)
  • Part 3: πŸ“ Terraform workflow and state management (current)
⚠️

⚠️ Important: This tutorial creates real AWS resources. While we use free-tier-eligible resources (t2.micro), ensure you destroy resources after completion to avoid any potential charges. We'll cover the destroy process at the end.

Understanding the Terraform Workflow

Before executing commands, let's understand the complete Terraform workflow and how each command fits together:

StepCommandPurposeFrequency
1terraform initInitialize directory, download providersOnce per project, or when providers change
2terraform validateCheck configuration syntaxAfter writing/modifying configuration
3terraform fmtFormat code consistentlyBefore committing changes
4terraform planPreview changes before applyingEvery time before apply
5terraform applyCreate/modify infrastructureWhen ready to execute changes
6terraform outputDisplay output valuesAfter apply, when needed
7terraform showInspect current stateWhen troubleshooting
8terraform destroyRemove all infrastructureWhen tearing down environment

The Core Workflow Loop

Most of your time will be spent in this cycle:

Write/Modify Configuration β†’ Plan β†’ Review Changes β†’ Apply β†’ Verify
                ↑                                                 ↓
                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Step 1: terraform init

The first command initializes your Terraform working directory. This must be run before any other Terraform commands.

terraform init

What this command does:

  1. Reads your configuration files (especially main.tf)
  2. Downloads required provider plugins (AWS provider ~5.x)
  3. Initializes backend for state storage (local by default)
  4. Creates .terraform directory with downloaded plugins
  5. Creates .terraform.lock.hcl file to lock provider versions

Expected output:

Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.80.0...
- Installed hashicorp/aws v5.80.0 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

Understanding the Output

Section 1: Backend Initialization

Initializing the backend...
  • Backend: Where Terraform stores state (tracking of resources)
  • Default: Local filesystem (terraform.tfstate file)
  • Production: Often remote (S3, Terraform Cloud, etc.)

Section 2: Provider Plugin Installation

- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.80.0...
- Installed hashicorp/aws v5.80.0 (signed by HashiCorp)
ComponentExplanation
Finding versionsTerraform queries the registry for available versions matching ~> 5.0
Installing v5.80.0Downloads the AWS provider binary (~450 MB)
signed by HashiCorpCryptographic signature verified (security measure)

Section 3: Lock File Creation

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above.

Purpose of .terraform.lock.hcl:

  • Records exact provider versions used
  • Ensures team members use same versions
  • Should be committed to version control (Git)
  • Prevents unexpected provider updates breaking things

Section 4: Success Confirmation

Terraform has been successfully initialized!
  • You're now ready to use Terraform commands
  • The .terraform/ directory now contains AWS provider plugin
  • State backend is ready to track resources

What Was Created?

After terraform init, your directory contains:

terraform-practice/
β”œβ”€β”€ .terraform/                    # Provider plugins (don't commit to Git)
β”‚   └── providers/
β”‚       └── registry.terraform.io/
β”‚           └── hashicorp/
β”‚               └── aws/
β”‚                   └── 5.80.0/
β”‚                       └── linux_amd64/
β”‚                           └── terraform-provider-aws_v5.80.0_x5
β”œβ”€β”€ .terraform.lock.hcl           # Version lock file (commit to Git)
β”œβ”€β”€ main.tf
β”œβ”€β”€ variables.tf
β”œβ”€β”€ data.tf
β”œβ”€β”€ security.tf
β”œβ”€β”€ ec2.tf
└── outputs.tf
πŸ’‘

πŸ’‘ Git Best Practice: Add .terraform/ to your .gitignore file. Never commit provider plugins to Gitβ€”they're large and platform-specific. Always commit .terraform.lock.hcl to ensure consistent provider versions across your team.

Step 2: terraform validate

Validate checks your configuration syntax without accessing any remote services. It's a quick way to catch errors before attempting to deploy.

terraform validate

What this command does:

  • Parses all .tf files in the directory
  • Checks HCL syntax for errors
  • Validates resource configurations
  • Checks for required attributes
  • Verifies variable and output references

Expected output (success):

Success! The configuration is valid.

What this confirms:

  • All .tf files have valid HCL syntax
  • Resource blocks are properly structured
  • Variable references are correct
  • No missing required attributes
  • Resource dependencies can be resolved

Common Validation Errors

If you have syntax errors, you'll see specific error messages:

Example Error 1: Missing Required Attribute

Error: Missing required argument

  on ec2.tf line 2, in resource "aws_instance" "terraform_instance":
   2: resource "aws_instance" "terraform_instance" {

The argument "ami" is required, but no definition was found.

How to fix: Add the missing ami attribute to your EC2 resource.

Example Error 2: Invalid Reference

Error: Reference to undeclared resource

  on ec2.tf line 6:
   6:   subnet_id = data.aws_subnet.wrong_name.id

A data source named "aws_subnet" "wrong_name" has not been declared in the
root module.

How to fix: Correct the resource reference to match your data source name.

βœ…

βœ… Best Practice: Run terraform validate frequently while writing configuration. It catches errors early before you attempt to apply changes to real infrastructure.

Step 3: terraform fmt

Format automatically rewrites your configuration files to follow Terraform's standard formatting conventions. This ensures consistent code style across your team.

terraform fmt

What this command does:

  • Scans all .tf files in current directory
  • Adjusts indentation to standard (2 spaces)
  • Aligns equal signs in blocks
  • Removes extra blank lines
  • Fixes spacing around operators
  • Writes changes back to files

Expected output:

data.tf
ec2.tf
main.tf
outputs.tf
security.tf
variables.tf

Understanding the output:

  • Lists all files that were modified
  • If no files listed, all were already properly formatted
  • Changes are automatically saved

Before and After Formatting

Before terraform fmt (poor formatting):

resource "aws_instance" "terraform_instance"{
ami= data.aws_ami.amazon_linux.id
  instance_type=var.instance_type
    subnet_id =data.aws_subnet.default.id
}

After terraform fmt (proper formatting):

resource "aws_instance" "terraform_instance" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = var.instance_type
  subnet_id     = data.aws_subnet.default.id
}

Changes made:

  • Space added after resource block opening brace
  • Indentation standardized to 2 spaces
  • Equal signs aligned vertically
  • Spaces added around = operator
  • Consistent spacing throughout
πŸ’‘

πŸ’‘ Team Workflow Tip: Many teams add terraform fmt -check to their CI/CD pipeline to ensure all code follows consistent formatting before merging.

Useful fmt Options

CommandPurpose
terraform fmtFormat files in current directory
terraform fmt -recursiveFormat files in subdirectories too
terraform fmt -checkCheck if formatting needed (doesn't modify)
terraform fmt -diffShow formatting changes before applying

Step 4: terraform plan - The Most Important Command

Plan is perhaps the most important Terraform command. It shows you exactly what will happen before you make any changes to real infrastructure. Always run plan before apply!

terraform plan

What this command does:

  1. Reads all configuration files
  2. Queries current state (if exists)
  3. Queries AWS for actual resource state
  4. Compares desired state (config) with current state
  5. Calculates required changes
  6. Displays detailed execution plan
  7. Does NOT make any changes

This output will be long, but understanding it is crucial. Let me show you the key sections and explain each one.

Expected output (excerpt - showing key sections):

data.aws_ami.amazon_linux: Reading...
data.aws_vpc.default: Reading...
data.aws_ami.amazon_linux: Read complete after 1s [id=ami-012967cc5a8c9f891]
data.aws_vpc.default: Read complete after 1s [id=vpc-026ffc4b9]
data.aws_subnet.default: Reading...
data.aws_subnet.default: Read complete after 0s [id=subnet-03f7ad9]

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.terraform_instance will be created
  + resource "aws_instance" "terraform_instance" {
      + ami                          = "ami-012967cc5a8c9f891"
      + instance_type                = "t2.micro"
      + subnet_id                    = "subnet-03f7ad9"
      + vpc_security_group_ids       = (known after apply)

      + tags                         = {
          + "CreatedBy"   = "Terraform"
          + "Environment" = "dev"
          + "Name"        = "terraform-lab-instance"
        }
    }

  # aws_security_group.terraform_sg will be created
  + resource "aws_security_group" "terraform_sg" {
      + id                     = (known after apply)
      + name_prefix            = "terraform-lab-sg-"
      + vpc_id                 = "vpc-026ffc4b9"

      + egress {
          + cidr_blocks      = ["0.0.0.0/0"]
          + from_port        = 0
          + protocol         = "-1"
          + to_port          = 0
        }

      + ingress {
          + cidr_blocks      = ["0.0.0.0/0"]
          + description      = "HTTP"
          + from_port        = 80
          + protocol         = "tcp"
          + to_port          = 80
        }
    }

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

Changes to Outputs:
  + instance_id         = (known after apply)
  + instance_public_dns = (known after apply)
  + instance_public_ip  = (known after apply)
  + security_group_id   = (known after apply)
  + website_url         = (known after apply)

Understanding the Plan Output

Section 1: Data Source Queries

data.aws_ami.amazon_linux: Reading...
data.aws_ami.amazon_linux: Read complete after 1s [id=ami-012967cc5a8c9f891]
  • Terraform queries AWS for existing resources
  • Finds the latest Amazon Linux 2 AMI
  • Takes about 1 second to complete the query

Section 2: Resource Actions Legend

Resource actions are indicated with the following symbols:
  + create
SymbolMeaningSafety Level
+Create new resourceSafe (new resource)
~Update in-placeUsually safe (no downtime)
-/+Destroy and recreateCaution (causes downtime)
-Destroy resourceDanger (resource deleted)

Section 3: Summary

Plan: 2 to add, 0 to change, 0 to destroy.
  • 2 to add: Will create 2 new resources (security group + EC2 instance)
  • 0 to change: No existing resources will be modified
  • 0 to destroy: No resources will be deleted

Section 4: Output Changes

Changes to Outputs:
  + instance_public_ip  = (known after apply)
  • Shows which output values will become available
  • (known after apply) means the value doesn't exist yet
βœ…

βœ… Critical Safety Check: Always review the plan output carefully before applying! Pay special attention to:

  • Any -/+ (replace) or - (destroy) actions
  • The summary line (X to add, Y to change, Z to destroy)
  • Resources being modified that you didn't expect

Step 5: terraform apply - Creating Real Infrastructure

Now for the exciting partβ€”actually creating your AWS infrastructure! The apply command executes the plan and makes real changes.

terraform apply

What this command does:

  1. Runs terraform plan automatically
  2. Shows you the plan
  3. Asks for confirmation
  4. Upon approval, executes the changes
  5. Creates/modifies AWS resources
  6. Updates the state file
  7. Displays outputs

Expected output (showing key sections):

[... plan output similar to above ...]

Plan: 2 to add, 0 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

aws_security_group.terraform_sg: Creating...
aws_security_group.terraform_sg: Creation complete after 3s [id=sg-0a34cd43d1c56f4e2]
aws_instance.terraform_instance: Creating...
aws_instance.terraform_instance: Still creating... [10s elapsed]
aws_instance.terraform_instance: Still creating... [20s elapsed]
aws_instance.terraform_instance: Creation complete after 25s [id=i-0afea07377a6c62ff]

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

Outputs:

instance_id = "i-0afea07377a6c62ff"
instance_public_dns = "ec2-3-92-197-145.compute-1.amazonaws.com"
instance_public_ip = "3.92.197.145"
security_group_id = "sg-0a34cd43d1c56f4e2"
website_url = "http://3.92.197.145"

Understanding Apply Output

Confirmation Prompt:

Do you want to perform these actions?
  Enter a value: yes
  • Type exactly yes (not y or Yes)
  • Any other input cancels the operation
  • Use terraform apply -auto-approve to skip (not recommended for production)

Resource Creation Progress:

aws_security_group.terraform_sg: Creating...
aws_security_group.terraform_sg: Creation complete after 3s [id=sg-0a34cd43d1c56f4e2]
  • Shows real-time progress
  • Security group created first (EC2 depends on it)
  • 3 seconds to create
  • Shows the generated ID

Long-Running Resources:

aws_instance.terraform_instance: Still creating... [10s elapsed]
aws_instance.terraform_instance: Still creating... [20s elapsed]
aws_instance.terraform_instance: Creation complete after 25s
  • EC2 instances take longer (~25 seconds)
  • Progress updates every 10 seconds
  • User data script running during boot
⚠️

⚠️ Wait for User Data: Even after Terraform completes, the user data script (Apache installation) continues running. Wait about 60 seconds before accessing the web server to ensure Apache is fully installed and started.

Verifying Your Infrastructure

Let's verify that everything was created successfully!

Check the Web Server

Open your browser and visit the website URL from the outputs:

http://3.92.197.145

You should see a page displaying:

  • Hello from Terraform Lab!
  • Instance ID: i-0afea07377a6c62ff
  • Availability Zone: us-east-1a
βœ…

πŸŽ‰ Congratulations! You've successfully deployed real infrastructure to AWS using Terraform! Your EC2 instance is running, Apache is serving web pages, and everything is accessible from the internet.

Using AWS CLI to Verify

You can also verify using AWS CLI:

aws ec2 describe-instances --instance-ids i-0afea07377a6c62ff --query 'Reservations[0].Instances[0].State.Name'

Should return: "running"

Step 6: terraform output

Query specific output values without showing the entire state:

terraform output

Shows all outputs:

instance_id = "i-0afea07377a6c62ff"
instance_public_ip = "3.92.197.145"

Query a specific output:

terraform output instance_public_ip

Output:

"3.92.197.145"

Raw value (no quotes, useful for scripts):

terraform output -raw instance_public_ip

Step 7: terraform destroy - Clean Up

When you're done experimenting, destroy all resources to avoid charges:

terraform destroy

You'll see a plan showing resources to be destroyed, then a confirmation prompt. Type yes to proceed.

Expected output:

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

Do you really want to destroy all resources?
  Enter a value: yes

aws_instance.terraform_instance: Destroying... [id=i-0afea07377a6c62ff]
aws_instance.terraform_instance: Still destroying... [10s elapsed]
aws_instance.terraform_instance: Destruction complete after 32s
aws_security_group.terraform_sg: Destroying... [id=sg-0a34cd43d1c56f4e2]
aws_security_group.terraform_sg: Destruction complete after 2s

Destroy complete! Resources: 2 destroyed.
🚨

🚨 Important: Always run terraform destroy when you're done with tutorial infrastructure. This ensures you don't accidentally incur AWS charges for resources you're not using.

Best Practices Summary

PracticeWhy It Matters
Always run plan before applyPreview changes, catch mistakes
Review destroy plans carefullyPrevent accidental deletion
Commit .terraform.lock.hclVersion consistency across team
Never commit terraform.tfstateContains sensitive data
Use remote state for teamsEnables collaboration, locking

Command Reference Cheat Sheet

CommandPurposeWhen to Use
terraform initInitialize directoryFirst time, after adding providers
terraform validateCheck syntaxAfter writing configuration
terraform fmtFormat codeBefore committing
terraform planPreview changesBefore every apply
terraform applyCreate/modifyTo deploy changes
terraform destroyDelete allClean up resources
terraform outputShow outputsGet resource info

βœ…

πŸŽ‰ Congratulations! You've completed the entire hands-on Terraform series!

You now have practical experience with:

  • Installing and configuring Terraform and AWS
  • Writing complete infrastructure configuration files
  • Understanding every Terraform command and its output
  • Deploying real infrastructure to AWS
  • Managing infrastructure lifecycle
  • Safely destroying resources

You're now ready to:

  • Build your own Terraform projects
  • Manage AWS infrastructure professionally
  • Explore advanced Terraform features (modules, remote state, workspaces)
  • Apply these skills to other cloud providers

Part 3 of 3 in the Terraform Hands-On series. You've completed the entire journey from installation to deployment. Continue exploring Terraform's advanced features to further enhance your infrastructure automation skills!

Owais

Written by Owais

I'm an AIOps Engineer with a passion for AI, Operating Systems, Cloud, and Securityβ€”sharing insights that matter in today's tech world.

I completed the UK's Eduqual Level 6 Diploma in AIOps from Al Nafi International College, a globally recognized program that's changing careers worldwide. This diploma is:

  • βœ… Available online in 17+ languages
  • βœ… Includes free student visa guidance for Master's programs in Computer Science fields across the UK, USA, Canada, and more
  • βœ… Comes with job placement support and a 90-day success plan once you land a role
  • βœ… Offers a 1-year internship experience letter while you studyβ€”all with no hidden costs

It's not just a diplomaβ€”it's a career accelerator.

πŸ‘‰ Start your journey today with a 7-day free trial

Related Articles

Continue exploring with these handpicked articles that complement what you just read

More Reading

One more article you might find interesting