What it is
Terraform is a tool for safely and predictably creating, changing, and improving infrastructure across cloud and private environments. You reach for it when you need to define and manage your infrastructure as code.
Installation
Linux
# Using apt (Debian/Ubuntu)
sudo apt update
sudo apt install terraform
# Using yum/dnf (Fedora/CentOS/RHEL)
sudo yum install terraform
# or
sudo dnf install terraform
# Using Homebrew (if installed)
brew install terraform
macOS
# Using Homebrew
brew install terraform
Windows
# Using Chocolatey
choco install terraform
# Using Scoop
scoop install terraform
# Manual Download (extract to PATH)
# Download the latest zip archive from https://www.terraform.io/downloads.html
# Extract it and place the terraform executable in a directory included in your system's PATH.
Core Concepts
Configuration Files
Terraform code is written in HashiCorp Configuration Language (HCL) or JSON. Files typically end with .tf or .tf.json.
Providers
Providers are plugins that Terraform uses to interact with specific cloud or SaaS providers (e.g., AWS, Azure, GCP, Kubernetes, GitHub). You declare the providers you’ll use in your configuration.
Resources
Resources are the fundamental infrastructure objects that Terraform manages. Examples include virtual machines, databases, networks, DNS records. They are defined in your .tf files.
State
Terraform maintains a state file (usually terraform.tfstate) that records the mapping between your configuration and the real-world resources it manages. This is crucial for tracking changes and preventing drift.
Modules
Modules are self-contained packages of Terraform configurations that are managed as a group. They allow you to abstract and reuse infrastructure patterns.
Commands / Usage
Initialization
# Initialize a Terraform working directory
terraform init
This command downloads the provider plugins required by your configuration and initializes the backend for state storage. It must be run in the directory containing your .tf files.
Planning
# Generate an execution plan
terraform plan
This command creates an execution plan, which shows you what Terraform will do to achieve the desired state described in your configuration. It doesn’t make any changes to your infrastructure.
# Generate an execution plan and save it to a file
terraform plan -out=tfplan
Saving the plan allows you to apply exactly that plan later, ensuring that the infrastructure matches what was planned.
Applying
# Apply the changes described in the execution plan
terraform apply
# Apply a saved execution plan
terraform apply tfplan
This command provisions the infrastructure as described in your configuration or applies the saved execution plan. It will prompt for confirmation unless -auto-approve is used.
# Apply changes without interactive confirmation
terraform apply -auto-approve
Destroying
# Destroy all infrastructure managed by the current configuration
terraform destroy
# Destroy infrastructure without interactive confirmation
terraform destroy -auto-approve
This command removes all resources managed by the Terraform configuration. It will prompt for confirmation unless -auto-approve is used.
State Management
# Show the current state of the managed infrastructure
terraform state list
Lists all resources currently tracked in the Terraform state.
# Show detailed information about a specific resource in the state
terraform state show aws_instance.example
Displays the attributes of a specific resource from the state file.
# Move a resource within the state (e.g., renaming)
terraform state mv 'old.resource.name' 'new.resource.name'
Useful for refactoring your Terraform code.
# Remove a resource from the state (use with extreme caution)
terraform state rm 'resource.name'
This command removes a resource from Terraform’s state without destroying the actual infrastructure. This is dangerous and should only be used if you are certain the resource is no longer managed by Terraform and will not be managed by any other tool.
# Pull state from a remote backend
terraform init -reconfigure -upgrade
If you switch or reconfigure your remote backend, terraform init will prompt you to pull the state.
Formatting and Validation
# Format Terraform configuration files
terraform fmt
This command rewrites Terraform configuration files to a canonical format and style.
# Validate the syntax of Terraform configuration files
terraform validate
Checks whether the Terraform configuration is syntactically valid and internally consistent.
Workspace Management
# List available workspaces
terraform workspace list
Shows all available workspaces in your Terraform project.
# Create a new workspace
terraform workspace new staging
Creates a new, empty workspace named staging.
# Select a workspace
terraform workspace select production
Switches the active workspace to production.
# Delete a workspace
terraform workspace delete dev
Removes the dev workspace.
Providers
# Show information about Terraform providers
terraform providers
Lists the providers used in the current configuration and their versions.
Output Values
# Display output values from the current Terraform state
terraform output
Shows the values of output variables defined in your Terraform configuration.
# Display a specific output value
terraform output instance_ip_address
Shows the value of the instance_ip_address output variable.
Import
# Import existing infrastructure into Terraform state
terraform import aws_instance.example i-0abcdef1234567890
This command imports an existing infrastructure resource into your Terraform state, allowing you to manage it with Terraform. You need to define the resource in your .tf files first.
Common Patterns
Creating a simple AWS EC2 instance
main.tf:
provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0" # Example Amazon Linux 2 AMI
instance_type = "t2.micro"
tags = {
Name = "HelloWorld"
}
}
output "instance_id" {
value = aws_instance.example.id
}
Commands:
# Initialize
terraform init
# See what will be created
terraform plan
# Create the instance
terraform apply
# See the instance ID
terraform output instance_id
Managing a Kubernetes Deployment
main.tf:
provider "kubernetes" {
config_path = "~/.kube/config"
}
resource "kubernetes_deployment" "nginx" {
metadata {
name = "nginx-deployment"
labels = {
app = "nginx"
}
}
spec {
replicas = 3
selector {
match_labels = {
app = "nginx"
}
}
template {
metadata {
labels = {
app = "nginx"
}
}
spec {
container {
image = "nginx:latest"
name = "nginx-container"
ports {
container_port = 80
}
}
}
}
}
}
output "deployment_name" {
value = kubernetes_deployment.nginx.metadata[0].name
}
Commands:
# Initialize (if not already done)
terraform init
# See what will be created/applied
terraform plan
# Apply the deployment
terraform apply
# See the deployment name
terraform output deployment_name
Working with Remote State (AWS S3 Backend)
versions.tf:
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket-12345"
key = "global/s3/terraform.tfstate"
region = "us-east-1"
}
}
Commands:
# Initialize the backend (run in the directory with versions.tf)
terraform init
# Terraform will prompt to configure the S3 bucket.
# If the bucket does not exist, you'll need to create it manually or using another Terraform configuration.
# Subsequent commands (plan, apply, destroy) will now use the remote state.
terraform plan
terraform apply
Using Terraform Modules
main.tf:
provider "aws" {
region = "us-east-1"
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.14.2" # Specify a version for reproducibility
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
private_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
single_nat_gateway = true
enable_dns_hostnames = true
}
output "vpc_id" {
value = module.vpc.vpc_id
}
Commands:
# Initialize to download the module
terraform init
# Plan and apply as usual
terraform plan
terraform apply
Gotchas
State File is Critical
Never manually edit the terraform.tfstate file unless you really know what you’re doing and have backups. Incorrect manual edits can lead to Terraform losing track of resources or attempting to recreate them.
terraform plan vs terraform apply
Always run terraform plan before terraform apply. The plan is your chance to review exactly what changes will be made. terraform apply will execute the plan, and it’s easy to overlook something in the output.
Resource Replacement
When a resource’s configuration changes in a way that requires it to be replaced (e.g., changing the instance type of an EC2 instance), Terraform will destroy the old resource and create a new one. This can lead to downtime if not managed carefully (e.g., using rolling updates or blue/green deployments).
Provider Version Pinning
It’s highly recommended to pin provider versions in your versions.tf or main.tf to ensure consistent behavior and avoid unexpected changes when providers are updated.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0" # Use a version constraint
}
}
}
Implicit Dependencies
Terraform automatically infers dependencies between resources. However, sometimes you might need to explicitly define a dependency using depends_on if Terraform cannot infer it correctly.
resource "aws_instance" "web" {
# ...
}
resource "aws_eip" "web_ip" {
instance = aws_instance.web.id
depends_on = [aws_instance.web] # Explicit dependency
}
Locking
When using remote state backends (like S3), Terraform often employs locking mechanisms to prevent multiple users from applying changes concurrently, which could corrupt the state. Ensure your backend is configured for locking if applicable.
Sensitive Data
Avoid storing sensitive data (like passwords or API keys) directly in your Terraform code or state file. Use tools like HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, or environment variables. Mark outputs as sensitive if they contain secrets.
output "db_password" {
value = aws_db_instance.main.password
sensitive = true
}