# Provider configuration
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-west-2" # AWS region
}
# Create an S3 bucket
resource "aws_s3_bucket" "example" {
bucket = "my-unique-bucket-name" # Bucket name (must be globally unique)
tags = {
Name = "My bucket"
Environment = "Dev"
}
}
# EC2 instance
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0" # Amazon Machine Image ID
instance_type = "t2.micro" # Instance size
tags = {
Name = "WebServer"
}
}
# Reference another resource's attribute
resource "aws_eip" "web" {
instance = aws_instance.web.id # Reference EC2 instance ID
domain = "vpc"
}
# Input variable declaration
variable "region" {
description = "AWS region to deploy resources"
type = string
default = "us-west-2" # Default value (optional)
}
variable "instance_count" {
description = "Number of instances to create"
type = number
default = 1
}
variable "tags" {
description = "Common tags for all resources"
type = map(string)
default = {
Environment = "Development"
Project = "MyApp"
}
}
# Using variables
resource "aws_instance" "app" {
count = var.instance_count # Reference variable
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = var.tags # Use map variable
}
# Output values (displayed after apply)
output "instance_id" {
description = "ID of the EC2 instance"
value = aws_instance.web.id # Export resource attribute
}
output "instance_public_ip" {
description = "Public IP address"
value = aws_instance.web.public_ip
}
output "bucket_arn" {
description = "ARN of the S3 bucket"
value = aws_s3_bucket.example.arn
sensitive = false # Mark if output contains secrets
}
# Query existing resources (read-only)
data "aws_ami" "ubuntu" {
most_recent = true # Get latest AMI
owners = ["099720109477"] # Canonical's AWS account ID
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-*-22.04-amd64-server-*"]
}
}
# Use data source in resource
resource "aws_instance" "ubuntu_server" {
ami = data.aws_ami.ubuntu.id # Reference data source
instance_type = "t2.micro"
}
# Get current AWS account info
data "aws_caller_identity" "current" {}
output "account_id" {
value = data.aws_caller_identity.current.account_id
}
# Call a reusable module
module "vpc" {
source = "terraform-aws-modules/vpc/aws" # Module source
version = "5.0.0" # Pin module version
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["us-west-2a", "us-west-2b"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
enable_nat_gateway = true
enable_vpn_gateway = false
tags = {
Terraform = "true"
Environment = "dev"
}
}
# Reference module outputs
resource "aws_instance" "app" {
subnet_id = module.vpc.private_subnets[0] # Use module output
# ... other configuration
}
# Count for multiple resources
resource "aws_instance" "server" {
count = 3 # Create 3 instances
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "Server-${count.index}" # Use count.index (0, 1, 2)
}
}
# for_each for dynamic resources
variable "users" {
default = ["alice", "bob", "charlie"]
}
resource "aws_iam_user" "example" {
for_each = toset(var.users) # Convert list to set
name = each.key # Current item value
}
# Conditional expression
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = var.environment == "prod" ? "t3.large" : "t2.micro"
}
# Dynamic blocks
resource "aws_security_group" "example" {
name = "example"
dynamic "ingress" {
for_each = var.service_ports # Iterate over collection
content {
from_port = ingress.value
to_port = ingress.value
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
}
# Define local computed values
locals {
environment = "production"
common_tags = {
Environment = local.environment
ManagedBy = "Terraform"
Project = var.project_name
}
# Computed values
full_name = "${var.prefix}-${var.name}-${var.suffix}"
}
# Use locals in resources
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = merge(
local.common_tags, # Spread common tags
{
Name = local.full_name # Add specific tag
}
)
}
# Remote state storage
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate" # State file path
region = "us-west-2"
encrypt = true # Enable encryption
dynamodb_table = "terraform-lock" # State locking table
}
}
# Remote state data source (read state from another workspace)
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "my-terraform-state"
key = "network/terraform.tfstate"
region = "us-west-2"
}
}
# Use remote state outputs
resource "aws_instance" "app" {
subnet_id = data.terraform_remote_state.network.outputs.subnet_id
}
# Initialize working directory (download providers, modules)
terraform init
# Preview changes without applying
terraform plan
# Preview with variable file
terraform plan -var-file="prod.tfvars"
# Apply changes (creates/updates infrastructure)
terraform apply
# Apply without confirmation prompt
terraform apply -auto-approve
# Apply specific resource
terraform apply -target=aws_instance.web
# Destroy all resources
terraform destroy
# Destroy specific resource
terraform destroy -target=aws_instance.web
# Format code to canonical style
terraform fmt
# Validate configuration syntax
terraform validate
# Show current state
terraform show
# List resources in state
terraform state list
# Show specific resource details
terraform state show aws_instance.web
# Remove resource from state (doesn't destroy)
terraform state rm aws_instance.web
# Import existing infrastructure
terraform import aws_instance.web i-1234567890abcdef0
# View available outputs
terraform output
# Get specific output value
terraform output instance_id
# Create workspace (environment isolation)
terraform workspace new staging
# List workspaces
terraform workspace list
# Switch workspace
terraform workspace select production
# Refresh state from real infrastructure
terraform refresh
# Generate dependency graph
terraform graph | dot -Tsvg > graph.svg
# terraform.tfvars (automatically loaded)
region = "us-east-1"
instance_count = 3
environment = "production"
tags = {
Environment = "Production"
Project = "MyApp"
ManagedBy = "Terraform"
}
# prod.tfvars (load with -var-file flag)
instance_type = "t3.large"
enable_monitoring = true
# Variable interpolation
resource "aws_instance" "example" {
tags = {
Name = "server-${var.environment}" # String interpolation
ID = "${var.project}-${var.environment}" # Combine multiple vars
}
}
# Function calls in strings
locals {
uppercase_name = upper(var.name) # Call function
formatted_date = formatdate("YYYY-MM-DD", timestamp())
}
# Conditional strings
output "message" {
value = var.enabled ? "Feature enabled" : "Feature disabled"
}
# Common built-in functions
locals {
# String functions
upper_name = upper("hello") # "HELLO"
lower_name = lower("WORLD") # "world"
title_name = title("hello world") # "Hello World"
trimmed = trimspace(" hello ") # "hello"
replaced = replace("hello", "l", "w") # "hewwo"
# Collection functions
list_length = length(["a", "b", "c"]) # 3
joined = join(", ", ["a", "b", "c"]) # "a, b, c"
sorted = sort(["c", "a", "b"]) # ["a", "b", "c"]
distinct = distinct([1, 2, 2, 3]) # [1, 2, 3]
merged = merge({a = 1}, {b = 2}) # {a = 1, b = 2}
# Numeric functions
max_value = max(5, 12, 9) # 12
min_value = min(5, 12, 9) # 5
# Type conversion
to_string = tostring(42) # "42"
to_number = tonumber("42") # 42
to_list = tolist(["a", "b"]) # List type
to_set = toset(["a", "b", "a"]) # Set (unique values)
# File functions
file_content = file("${path.module}/config.txt") # Read file
template_file = templatefile("user-data.sh", { # Template with vars
hostname = "web-server"
})
}
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
lifecycle {
create_before_destroy = true # Create new before destroying old
prevent_destroy = true # Prevent accidental deletion
ignore_changes = [ # Ignore changes to these attributes
tags,
user_data
]
}
}
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
# Run command on remote resource
provisioner "remote-exec" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx"
]
connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
}
# Run local command after creation
provisioner "local-exec" {
command = "echo ${self.public_ip} >> ip_list.txt"
}
# Run on destruction
provisioner "local-exec" {
when = destroy
command = "echo 'Resource destroyed' >> log.txt"
}
}
# Implicit dependency (automatic)
resource "aws_eip" "example" {
instance = aws_instance.web.id # Terraform detects dependency
}
# Explicit dependency (manual)
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
depends_on = [
aws_iam_role_policy.example # Force dependency order
]
}