Loading...
Loading...
Comprehensive Terraform Infrastructure as Code skill covering resources, modules, state management, workspaces, providers, and advanced patterns for cloud-agnostic infrastructure deployment
npx skill4agent add manutej/luxor-claude-marketplace terraform-infrastructure-as-code# Initialize Terraform working directory
terraform init
# Initialize and upgrade provider versions
terraform init -upgrade
# Initialize with backend configuration
terraform init -backend-config="bucket=my-state-bucket"
# Generate a plan
terraform plan
# Save plan to file for later apply
terraform plan -out=tfplan
# Plan with specific variable values
terraform plan -var="region=us-west-2" -var="instance_type=t2.micro"
# Apply with interactive approval
terraform apply
# Auto-approve without confirmation
terraform apply -auto-approve
# Apply a saved plan file
terraform apply tfplan
# Destroy with confirmation prompt
terraform destroy
# Auto-approve destruction
terraform destroy -auto-approve# Basic resource declaration
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "WebServer"
Environment = "production"
}
}# AWS provider configuration
provider "aws" {
region = "us-west-2"
default_tags {
tags = {
ManagedBy = "Terraform"
Project = "MyApp"
}
}
}resource "resource_type" "resource_name" {
argument1 = "value1"
argument2 = "value2"
nested_block {
nested_argument = "nested_value"
}
}resource "aws_instance" "app_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
key_name = "my-keypair"
vpc_security_group_ids = [aws_security_group.app.id]
subnet_id = aws_subnet.public.id
user_data = <<-EOF
#!/bin/bash
echo "Hello, World!" > index.html
nohup busybox httpd -f -p 8080 &
EOF
tags = {
Name = "AppServer"
Environment = "production"
ManagedBy = "Terraform"
}
}resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "main-vpc"
}
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2a"
map_public_ip_on_launch = true
tags = {
Name = "public-subnet"
}
}
resource "aws_subnet" "private" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
availability_zone = "us-west-2a"
tags = {
Name = "private-subnet"
}
}resource "aws_s3_bucket" "data" {
bucket = "my-app-data-bucket-12345"
tags = {
Name = "Data Bucket"
Environment = "production"
}
}
resource "aws_s3_bucket_versioning" "data" {
bucket = aws_s3_bucket.data.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "data" {
bucket = aws_s3_bucket.data.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}resource "aws_security_group" "web" {
name = "web-sg"
description = "Security group for web servers"
vpc_id = aws_vpc.main.id
ingress {
description = "HTTP from anywhere"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTPS from anywhere"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "web-security-group"
}
}resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
lifecycle {
create_before_destroy = true
prevent_destroy = true
ignore_changes = [tags]
}
}create_before_destroyprevent_destroyignore_changesreplace_triggered_byresource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
lifecycle {
replace_triggered_by = [
aws_iam_policy.example.id
]
}
}data "resource_type" "name" {
# Query parameters
}# Query existing AWS resources
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
}
# Use data source in resource
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
}
# Query availability zones
data "aws_availability_zones" "available" {
state = "available"
}
# Query VPC
data "aws_vpc" "default" {
default = true
}
# Query remote state
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "terraform-state"
key = "network/terraform.tfstate"
region = "us-west-2"
}
}
# Use remote state outputs
resource "aws_instance" "app" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
subnet_id = data.terraform_remote_state.network.outputs.subnet_id
availability_zone = data.aws_availability_zones.available.names[0]
}# AWS Account ID
data "aws_caller_identity" "current" {}
output "account_id" {
value = data.aws_caller_identity.current.account_id
}
# AWS Region
data "aws_region" "current" {}
output "region" {
value = data.aws_region.current.name
}
# Route53 Zone
data "aws_route53_zone" "primary" {
name = "example.com"
}
# IAM Policy Document
data "aws_iam_policy_document" "assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}# Simple variable
variable "region" {
type = string
default = "us-west-2"
description = "AWS region for resources"
}
# Variable with validation
variable "instance_type" {
type = string
default = "t2.micro"
validation {
condition = contains(["t2.micro", "t2.small", "t2.medium"], var.instance_type)
error_message = "Instance type must be t2.micro, t2.small, or t2.medium."
}
}
# Complex type variable
variable "vpc_config" {
type = object({
cidr_block = string
azs = list(string)
private_subnets = list(string)
public_subnets = list(string)
})
default = {
cidr_block = "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"]
}
}# String variable
variable "environment" {
type = string
default = "development"
}
# Number variable
variable "instance_count" {
type = number
default = 3
}
# Boolean variable
variable "enable_monitoring" {
type = bool
default = true
}
# List variable
variable "availability_zones" {
type = list(string)
default = ["us-west-2a", "us-west-2b", "us-west-2c"]
}
# Map variable
variable "ami_ids" {
type = map(string)
default = {
us-west-2 = "ami-0c55b159cbfafe1f0"
us-east-1 = "ami-0b69ea66ff7391e80"
}
}
# Object variable
variable "database_config" {
type = object({
engine = string
engine_version = string
instance_class = string
allocated_storage = number
})
default = {
engine = "postgres"
engine_version = "13.7"
instance_class = "db.t3.micro"
allocated_storage = 20
}
}# Command line
terraform apply -var="region=us-east-1" -var="instance_count=5"
# Variable files
terraform apply -var-file="production.tfvars"
# Environment variables
export TF_VAR_region=us-east-1
terraform applyregion = "us-west-2"
instance_count = 3
environment = "production"# Simple output
output "instance_ip" {
value = aws_instance.web.public_ip
description = "The public IP of the web server"
}
# Output with sensitive data
output "database_password" {
value = aws_db_instance.main.password
sensitive = true
}
# Complex output
output "instance_details" {
value = {
id = aws_instance.web.id
public_ip = aws_instance.web.public_ip
private_ip = aws_instance.web.private_ip
arn = aws_instance.web.arn
}
description = "Complete instance information"
}
# Output with depends_on
output "vpc_ready" {
value = "VPC and subnets are ready"
depends_on = [
aws_vpc.main,
aws_subnet.private,
aws_subnet.public
]
}# Show all outputs
terraform output
# Get specific output value
terraform output instance_ip
# Output in JSON format
terraform output -json
# Use output in scripts
INSTANCE_IP=$(terraform output -raw instance_ip)
echo "Server IP: $INSTANCE_IP"locals {
common_tags = {
ManagedBy = "Terraform"
Environment = var.environment
Project = "MyApp"
}
name_prefix = "${var.project_name}-${var.environment}"
availability_zones = slice(data.aws_availability_zones.available.names, 0, 3)
}
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.instance_type
tags = merge(
local.common_tags,
{
Name = "${local.name_prefix}-web-server"
}
)
}modules/
└── vpc/
├── main.tf
├── variables.tf
├── outputs.tf
└── README.mdresource "aws_vpc" "main" {
cidr_block = var.cidr_block
enable_dns_hostnames = var.enable_dns_hostnames
enable_dns_support = var.enable_dns_support
tags = merge(
var.tags,
{
Name = var.name
}
)
}
resource "aws_subnet" "public" {
count = length(var.public_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = merge(
var.tags,
{
Name = "${var.name}-public-${count.index + 1}"
Type = "public"
}
)
}
resource "aws_subnet" "private" {
count = length(var.private_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.private_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
tags = merge(
var.tags,
{
Name = "${var.name}-private-${count.index + 1}"
Type = "private"
}
)
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = merge(
var.tags,
{
Name = "${var.name}-igw"
}
)
}variable "name" {
description = "Name prefix for VPC resources"
type = string
}
variable "cidr_block" {
description = "CIDR block for VPC"
type = string
}
variable "public_subnet_cidrs" {
description = "CIDR blocks for public subnets"
type = list(string)
}
variable "private_subnet_cidrs" {
description = "CIDR blocks for private subnets"
type = list(string)
}
variable "availability_zones" {
description = "Availability zones for subnets"
type = list(string)
}
variable "enable_dns_hostnames" {
description = "Enable DNS hostnames in VPC"
type = bool
default = true
}
variable "enable_dns_support" {
description = "Enable DNS support in VPC"
type = bool
default = true
}
variable "tags" {
description = "Tags to apply to resources"
type = map(string)
default = {}
}output "vpc_id" {
description = "ID of the VPC"
value = aws_vpc.main.id
}
output "vpc_cidr" {
description = "CIDR block of the VPC"
value = aws_vpc.main.cidr_block
}
output "public_subnet_ids" {
description = "IDs of public subnets"
value = aws_subnet.public[*].id
}
output "private_subnet_ids" {
description = "IDs of private subnets"
value = aws_subnet.private[*].id
}
output "internet_gateway_id" {
description = "ID of the internet gateway"
value = aws_internet_gateway.main.id
}# Using a module from local path
module "vpc" {
source = "./modules/vpc"
cidr_block = "10.0.0.0/16"
region = var.region
tags = {
Environment = "production"
ManagedBy = "terraform"
}
}
# Using a module from Terraform Registry
module "s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
version = "3.15.0"
bucket = "my-application-bucket"
acl = "private"
versioning = {
enabled = true
}
}
# Using module outputs
resource "aws_instance" "app" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
subnet_id = module.vpc.private_subnet_ids[0]
tags = {
Name = "App Server"
}
}
# Module with count
module "web_servers" {
count = 3
source = "./modules/web-server"
name = "web-${count.index}"
subnet = module.vpc.public_subnet_ids[count.index]
}
# Module with for_each
module "environments" {
for_each = toset(["dev", "staging", "prod"])
source = "./modules/environment"
env_name = each.key
vpc_cidr = "10.${index(["dev", "staging", "prod"], each.key)}.0.0/16"
}# Local path
module "vpc" {
source = "./modules/vpc"
}
# Terraform Registry
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.0"
}
# GitHub
module "vpc" {
source = "github.com/terraform-aws-modules/terraform-aws-vpc"
}
# GitHub with specific branch
module "vpc" {
source = "github.com/terraform-aws-modules/terraform-aws-vpc?ref=v5.1.0"
}
# Git
module "vpc" {
source = "git::https://github.com/terraform-aws-modules/terraform-aws-vpc.git"
}
# S3 bucket
module "vpc" {
source = "s3::https://s3.amazonaws.com/my-bucket/vpc-module.zip"
}module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0" # Any version 5.x
}
module "s3" {
source = "terraform-aws-modules/s3-bucket/aws"
version = ">= 3.0, < 4.0" # Between 3.0 and 4.0
}terraform.tfstateterraform {
backend "local" {
path = "terraform.tfstate"
}
}# S3 backend configuration
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "production/terraform.tfstate"
region = "us-west-2"
encrypt = true
dynamodb_table = "terraform-locks"
}
}resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
tags = {
Name = "Terraform State Lock Table"
}
}terraform {
backend "azurerm" {
resource_group_name = "terraform-rg"
storage_account_name = "tfstate"
container_name = "tfstate"
key = "prod.terraform.tfstate"
}
}terraform {
cloud {
organization = "my-organization"
workspaces {
name = "production"
}
}
}terraform {
backend "consul" {
address = "consul.example.com:8500"
scheme = "https"
path = "terraform/production"
}
}# List all resources in state
terraform state list
# Show specific resource details
terraform state show aws_instance.example
# Remove resource from state (doesn't destroy)
terraform state rm aws_instance.example
# Move resource to new address
terraform state mv aws_instance.example aws_instance.web_server
# Show current state
terraform show
# Show specific plan file
terraform show tfplan
# Output state in JSON format
terraform show -json > state.json
# Pull remote state
terraform state pull > terraform.tfstate
# Push local state to remote
terraform state push terraform.tfstateterraform {
backend "s3" {
bucket = "my-terraform-state"
key = "terraform.tfstate"
region = "us-west-2"
dynamodb_table = "terraform-state-lock"
encrypt = true
}
}# Import AWS instance
terraform import aws_instance.example i-0abc123def456
# Import with module
terraform import module.network.aws_vpc.main vpc-0123456789abcdefresource "aws_instance" "example" {
# Configuration will be populated after import
# Define the basic structure matching the resource
}# Initialize with backend
terraform init
# Migrate from local to remote
terraform init -migrate-state
# Backend configuration from CLI
terraform init -backend-config="bucket=my-other-bucket" \
-backend-config="key=my-state"# List workspaces
terraform workspace list
# Create new workspace
terraform workspace new production
# Switch to workspace
terraform workspace select staging
# Show current workspace
terraform workspace show
# Delete workspace
terraform workspace delete development# Example workflow:
terraform workspace new development
terraform apply # Creates resources in development workspace
terraform workspace new staging
terraform apply # Creates separate resources in staging workspace
terraform workspace new production
terraform apply # Creates separate resources in production workspacelocals {
environment = terraform.workspace
}
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = terraform.workspace == "production" ? "t3.large" : "t3.micro"
tags = {
Name = "web-${terraform.workspace}"
Environment = terraform.workspace
}
}
# Workspace-specific variables
variable "instance_counts" {
type = map(number)
default = {
development = 1
staging = 2
production = 5
}
}
resource "aws_instance" "app" {
count = var.instance_counts[terraform.workspace]
ami = var.ami_id
instance_type = "t3.micro"
tags = {
Name = "app-${terraform.workspace}-${count.index + 1}"
}
}terraform.tfstate.d/development/terraform.tfstateterraform.tfstate.d/staging/terraform.tfstateterraform.tfstate.d/production/terraform.tfstateterraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.region
default_tags {
tags = {
ManagedBy = "Terraform"
Project = "MyApp"
}
}
}provider "aws" {
alias = "east"
region = "us-east-1"
}
provider "aws" {
alias = "west"
region = "us-west-2"
}
resource "aws_instance" "east_server" {
provider = aws.east
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}
resource "aws_instance" "west_server" {
provider = aws.west
ami = "ami-0123456789abcdef"
instance_type = "t2.micro"
}terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
google = {
source = "hashicorp/google"
version = "~> 4.0"
}
}
}
provider "aws" {
region = "us-west-2"
}
provider "azurerm" {
features {}
subscription_id = var.azure_subscription_id
}
provider "google" {
project = var.gcp_project_id
region = "us-central1"
}
# AWS resources
resource "aws_s3_bucket" "data" {
bucket = "my-data-bucket"
}
# Azure resources
resource "azurerm_storage_account" "data" {
name = "mydatastorageacct"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
account_tier = "Standard"
account_replication_type = "LRS"
}
# GCP resources
resource "google_storage_bucket" "data" {
name = "my-data-bucket-gcp"
location = "US"
}# Root module
provider "aws" {
region = "us-west-2"
}
provider "aws" {
alias = "dr"
region = "us-east-1"
}
module "app" {
source = "./modules/app"
providers = {
aws = aws
aws.dr = aws.dr
}
}# modules/app/main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
configuration_aliases = [aws.dr]
}
}
}
resource "aws_instance" "primary" {
provider = aws
# ...
}
resource "aws_instance" "dr" {
provider = aws.dr
# ...
}variable "ingress_rules" {
type = list(object({
description = string
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
default = [
{
description = "HTTP"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
description = "HTTPS"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
]
}
resource "aws_security_group" "web" {
name = "web-sg"
description = "Security group for web servers"
vpc_id = aws_vpc.main.id
dynamic "ingress" {
for_each = var.ingress_rules
content {
description = ingress.value.description
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}resource "aws_s3_bucket" "buckets" {
for_each = toset(["logs", "data", "backups"])
bucket = "my-app-${each.key}"
tags = {
Purpose = each.key
}
}
# With map
variable "instances" {
type = map(object({
ami = string
instance_type = string
}))
default = {
web = {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
}
app = {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.small"
}
}
}
resource "aws_instance" "servers" {
for_each = var.instances
ami = each.value.ami
instance_type = each.value.instance_type
tags = {
Name = each.key
}
}resource "aws_instance" "servers" {
count = 3
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "server-${count.index}"
}
}
# Conditional resource creation
variable "create_db" {
type = bool
default = true
}
resource "aws_db_instance" "database" {
count = var.create_db ? 1 : 0
engine = "postgres"
instance_class = "db.t3.micro"
allocated_storage = 20
}resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.environment == "production" ? "t3.large" : "t3.micro"
tags = {
Name = var.environment == "production" ? "prod-web" : "dev-web"
}
}
locals {
instance_count = var.environment == "production" ? 5 : var.environment == "staging" ? 2 : 1
}locals {
# String functions
name_upper = upper(var.name)
name_lower = lower(var.name)
name_title = title(var.name)
# Collection functions
subnet_count = length(var.subnet_cidrs)
first_az = element(var.availability_zones, 0)
all_azs = join(",", var.availability_zones)
# Numeric functions
min_instances = min(var.instance_count, 10)
max_instances = max(var.instance_count, 1)
# Type conversion
instance_count_string = tostring(var.instance_count)
az_set = toset(var.availability_zones)
# Map functions
merged_tags = merge(var.common_tags, var.specific_tags)
# File functions
user_data = file("${path.module}/scripts/init.sh")
config = templatefile("${path.module}/templates/config.tpl", {
environment = var.environment
region = var.region
})
# Encoding functions
encoded = base64encode("secret data")
decoded = base64decode(var.encoded_data)
# Date/Time functions
timestamp = timestamp()
# IP Network functions
cidr_subnets = cidrsubnets("10.0.0.0/16", 8, 8, 8, 8)
}# Creating multiple subnets across availability zones
locals {
subnet_cidrs = cidrsubnets(var.vpc_cidr, 8, 8, 8, 8)
}
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_subnet" "public" {
count = 3
vpc_id = aws_vpc.main.id
cidr_block = local.subnet_cidrs[count.index]
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "public-${count.index + 1}"
}
}resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id # Implicit dependency
cidr_block = "10.0.1.0/24"
}
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t2.micro"
subnet_id = aws_subnet.public.id # Implicit dependency
}depends_onresource "aws_instance" "app" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
depends_on = [aws_db_instance.database]
}
resource "aws_db_instance" "database" {
engine = "postgres"
instance_class = "db.t3.micro"
allocated_storage = 20
}module "vpc" {
source = "./modules/vpc"
cidr_block = "10.0.0.0/16"
}
module "app" {
source = "./modules/app"
vpc_id = module.vpc.vpc_id
subnet_id = module.vpc.private_subnet_ids[0]
depends_on = [module.vpc]
}resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t2.micro"
provisioner "local-exec" {
command = "echo ${self.private_ip} >> private_ips.txt"
}
provisioner "local-exec" {
when = destroy
command = "echo 'Instance ${self.id} destroyed' >> destroy_log.txt"
}
}resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t2.micro"
key_name = var.key_name
connection {
type = "ssh"
user = "ubuntu"
private_key = file(var.private_key_path)
host = self.public_ip
}
provisioner "remote-exec" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx",
"sudo systemctl start nginx",
"sudo systemctl enable nginx"
]
}
}resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t2.micro"
key_name = var.key_name
connection {
type = "ssh"
user = "ubuntu"
private_key = file(var.private_key_path)
host = self.public_ip
}
provisioner "file" {
source = "scripts/init.sh"
destination = "/tmp/init.sh"
}
provisioner "remote-exec" {
inline = [
"chmod +x /tmp/init.sh",
"sudo /tmp/init.sh"
]
}
}resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t2.micro"
provisioner "local-exec" {
command = "./configure.sh"
on_failure = continue # or fail (default)
}
}project/
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ └── terraform.tfvars
│ ├── staging/
│ └── production/
├── modules/
│ ├── vpc/
│ ├── compute/
│ └── database/
└── global/
└── s3/# Use meaningful descriptions
variable "instance_type" {
description = "EC2 instance type for web servers"
type = string
default = "t3.micro"
}
# Use validation
variable "environment" {
description = "Deployment environment"
type = string
validation {
condition = contains(["dev", "staging", "production"], var.environment)
error_message = "Environment must be dev, staging, or production."
}
}# Resource naming
resource "aws_instance" "web_server" { # Use descriptive names
tags = {
Name = "${var.project}-${var.environment}-web-${count.index + 1}"
}
}
# Module naming
module "vpc_production" {
source = "./modules/vpc"
}# Use locals for repeated values
locals {
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "Terraform"
CostCenter = var.cost_center
}
}
resource "aws_instance" "web" {
tags = merge(
local.common_tags,
{
Name = "web-server"
Role = "webserver"
}
)
}# Never hardcode credentials
# BAD
provider "aws" {
access_key = "AKIAIOSFODNN7EXAMPLE"
secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
# GOOD - Use environment variables or IAM roles
provider "aws" {
# Credentials from environment or IAM role
}
# Use sensitive flag for outputs
output "database_password" {
value = aws_db_instance.main.password
sensitive = true
}
# Encrypt sensitive data
resource "aws_s3_bucket" "data" {
bucket = "my-data-bucket"
}
resource "aws_s3_bucket_server_side_encryption_configuration" "data" {
bucket = aws_s3_bucket.data.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}# Validate syntax
terraform validate
# Format code
terraform fmt -recursive
# Plan before apply
terraform plan -out=tfplan
# Review plan
terraform show tfplan# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.log
crash.*.log
# Exclude variable files that may contain sensitive data
*.tfvars
*.tfvars.json
# Ignore override files
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# CLI configuration files
.terraformrc
terraform.rc
# Plan files
*.tfplan# Module versioning
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0" # Pin to major version
}
# Module documentation
# Always include README.md with:
# - Description
# - Requirements
# - Providers
# - Inputs
# - Outputs
# - Example usage# Consistent tagging strategy
locals {
required_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "Terraform"
Owner = var.owner_email
CostCenter = var.cost_center
Compliance = var.compliance_level
}
}
# Use default tags at provider level
provider "aws" {
region = var.region
default_tags {
tags = local.required_tags
}
}name: Terraform
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
TF_VERSION: 1.5.0
jobs:
terraform:
name: Terraform
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Terraform Format
run: terraform fmt -check -recursive
- name: Terraform Init
run: terraform init
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Terraform Validate
run: terraform validate
- name: Terraform Plan
run: terraform plan -no-color
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Terraform Apply
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: terraform apply -auto-approve
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}image:
name: hashicorp/terraform:1.5.0
entrypoint: [""]
stages:
- validate
- plan
- apply
before_script:
- terraform init
validate:
stage: validate
script:
- terraform validate
- terraform fmt -check -recursive
plan:
stage: plan
script:
- terraform plan -out=tfplan
artifacts:
paths:
- tfplan
expire_in: 1 week
apply:
stage: apply
script:
- terraform apply -auto-approve tfplan
dependencies:
- plan
only:
- main
when: manualpipeline {
agent any
environment {
TF_VERSION = '1.5.0'
AWS_CREDENTIALS = credentials('aws-credentials')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Terraform Init') {
steps {
sh 'terraform init'
}
}
stage('Terraform Validate') {
steps {
sh 'terraform validate'
sh 'terraform fmt -check -recursive'
}
}
stage('Terraform Plan') {
steps {
sh 'terraform plan -out=tfplan'
}
}
stage('Terraform Apply') {
when {
branch 'main'
}
steps {
input message: 'Apply Terraform changes?', ok: 'Apply'
sh 'terraform apply -auto-approve tfplan'
}
}
}
post {
always {
cleanWs()
}
}
}trigger:
- main
pool:
vmImage: 'ubuntu-latest'
variables:
- name: terraformVersion
value: '1.5.0'
stages:
- stage: Validate
jobs:
- job: ValidateTerraform
steps:
- task: TerraformInstaller@0
inputs:
terraformVersion: $(terraformVersion)
- task: TerraformTaskV2@2
displayName: 'Terraform Init'
inputs:
provider: 'aws'
command: 'init'
backendServiceAWS: 'AWS-Connection'
- task: TerraformTaskV2@2
displayName: 'Terraform Validate'
inputs:
provider: 'aws'
command: 'validate'
- script: terraform fmt -check -recursive
displayName: 'Terraform Format Check'
- stage: Plan
dependsOn: Validate
jobs:
- job: PlanTerraform
steps:
- task: TerraformTaskV2@2
displayName: 'Terraform Plan'
inputs:
provider: 'aws'
command: 'plan'
commandOptions: '-out=tfplan'
- stage: Apply
dependsOn: Plan
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: ApplyTerraform
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- task: TerraformTaskV2@2
displayName: 'Terraform Apply'
inputs:
provider: 'aws'
command: 'apply'
commandOptions: '-auto-approve tfplan'repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.81.0
hooks:
- id: terraform_fmt
- id: terraform_validate
- id: terraform_docs
- id: terraform_tflint
- id: terraform_tfsec
- id: terraform_checkovpackage test
import (
"testing"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
)
func TestTerraformVPC(t *testing.T) {
terraformOptions := &terraform.Options{
TerraformDir: "../examples/vpc",
Vars: map[string]interface{}{
"cidr_block": "10.0.0.0/16",
"region": "us-west-2",
},
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
vpcId := terraform.Output(t, terraformOptions, "vpc_id")
assert.NotEmpty(t, vpcId)
}# Start console
terraform console
# Example session:
> aws_instance.example.public_ip
"54.183.22.100"
> var.region
"us-west-2"
> length(aws_instance.example.tags)
3# Generate graph in DOT format
terraform graph > graph.dot
# Convert to image with Graphviz
terraform graph | dot -Tpng > graph.png
# Generate graph for specific plan
terraform graph -type=plan > plan-graph.dot# Taint a resource (mark for recreation)
terraform taint aws_instance.example
# Untaint a resource
terraform untaint aws_instance.example# List required providers
terraform providers
# Show provider schemas
terraform providers schema -json > schemas.json
# Lock provider versions
terraform providers lock