Claude Code Plugins

Community-maintained marketplace

Feedback

terraform-infrastructure

@manutej/luxor-claude-marketplace
6
0

Comprehensive Terraform infrastructure-as-code skill covering providers, resources, modules, state management, and enterprise patterns for multi-cloud infrastructure

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name terraform-infrastructure
description Comprehensive Terraform infrastructure-as-code skill covering providers, resources, modules, state management, and enterprise patterns for multi-cloud infrastructure

Terraform Infrastructure-as-Code

A comprehensive skill for building, managing, and scaling cloud infrastructure using Terraform. Master declarative infrastructure, multi-cloud deployments, state management, module composition, and enterprise-grade patterns for AWS, Azure, GCP, and other providers.

When to Use This Skill

Use this skill when:

  • Provisioning cloud infrastructure across AWS, Azure, GCP, or multi-cloud environments
  • Building reusable infrastructure modules for teams and organizations
  • Managing infrastructure state across multiple environments (dev, staging, production)
  • Implementing infrastructure as code (IaC) best practices and governance
  • Migrating from manual infrastructure to automated, version-controlled deployments
  • Creating repeatable, testable infrastructure configurations
  • Orchestrating complex multi-tier application architectures
  • Managing Kubernetes clusters, databases, networks, and compute resources
  • Implementing disaster recovery and multi-region deployments
  • Collaborating on infrastructure changes with teams using GitOps workflows

Core Concepts

Infrastructure as Code Philosophy

Terraform enables declarative infrastructure management:

  • Declarative Configuration: Define desired state, Terraform handles execution
  • Immutable Infrastructure: Replace rather than modify infrastructure
  • Version Control: Track infrastructure changes like application code
  • Plan Before Apply: Preview changes before execution
  • Resource Graph: Automatic dependency resolution and parallel execution
  • State Management: Track real-world resources and their configuration

Key Terraform Components

  1. Providers: Plugins for infrastructure platforms (AWS, Azure, GCP, Kubernetes, etc.)
  2. Resources: Infrastructure objects (VMs, networks, databases, storage)
  3. Data Sources: Query existing infrastructure or external data
  4. Variables: Parameterize configurations for reusability
  5. Outputs: Export values for consumption by other configurations
  6. Modules: Reusable, composable infrastructure components
  7. State: JSON file tracking managed infrastructure
  8. Workspaces: Manage multiple instances of infrastructure

Terraform Workflow

Write → Init → Plan → Apply → Destroy
  ↓       ↓       ↓       ↓       ↓
 .tf    Download Review  Execute Remove
files   providers changes changes resources

Terraform Language (HCL)

Basic Syntax

HCL (HashiCorp Configuration Language) is declarative and human-readable:

# Block structure
block_type "block_label" "block_name" {
  argument_name = argument_value

  nested_block {
    nested_argument = value
  }
}

# Example: EC2 instance resource
resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  tags = {
    Name = "WebServer"
    Environment = "production"
  }
}

Variables and Types

Terraform supports rich type system:

# String variable
variable "region" {
  type        = string
  description = "AWS region for resources"
  default     = "us-east-1"
}

# 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-east-1a", "us-east-1b", "us-east-1c"]
}

# Map variable
variable "instance_tags" {
  type = map(string)
  default = {
    Environment = "production"
    Project     = "web-app"
  }
}

# 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
  }
}

# Set variable
variable "allowed_cidr_blocks" {
  type = set(string)
  default = ["10.0.0.0/8", "172.16.0.0/12"]
}

# Tuple variable
variable "server_config" {
  type = tuple([string, number, bool])
  default = ["t3.micro", 2, true]
}

Variable Validation

Add custom validation rules:

variable "instance_type" {
  type        = string
  description = "EC2 instance type"

  validation {
    condition     = contains(["t3.micro", "t3.small", "t3.medium"], var.instance_type)
    error_message = "Instance type must be t3.micro, t3.small, or t3.medium."
  }
}

variable "environment" {
  type = string

  validation {
    condition     = can(regex("^(dev|staging|prod)$", var.environment))
    error_message = "Environment must be dev, staging, or prod."
  }
}

variable "cidr_block" {
  type = string

  validation {
    condition     = can(cidrhost(var.cidr_block, 0))
    error_message = "Must be a valid IPv4 CIDR block."
  }
}

Locals and Expressions

Locals compute values once and reuse them:

locals {
  # Simple local
  environment = terraform.workspace

  # Computed local
  common_tags = {
    Environment = local.environment
    ManagedBy   = "Terraform"
    Project     = var.project_name
  }

  # Conditional local
  instance_count = var.environment == "prod" ? 5 : 2

  # List manipulation
  all_subnets = concat(var.public_subnets, var.private_subnets)

  # Map merging
  merged_tags = merge(
    local.common_tags,
    var.additional_tags
  )

  # String interpolation
  bucket_name = "${var.project_name}-${local.environment}-data"

  # For expression
  subnet_ids = [for subnet in aws_subnet.private : subnet.id]

  # For expression with filtering
  prod_instances = [
    for instance in aws_instance.app :
    instance.id if instance.tags["Environment"] == "prod"
  ]

  # Map transformation
  instance_map = {
    for idx, instance in aws_instance.app :
    instance.tags["Name"] => instance.id
  }
}

Functions

Terraform provides built-in functions:

# String functions
upper("hello")                    # "HELLO"
lower("WORLD")                    # "world"
title("hello world")              # "Hello World"
trim("  spaces  ")                # "spaces"
trimprefix("prefix-value", "prefix-") # "value"
format("Server %03d", 1)          # "Server 001"
join("-", ["a", "b", "c"])        # "a-b-c"
split("-", "a-b-c")               # ["a", "b", "c"]
substr("hello", 0, 3)             # "hel"
replace("hello", "l", "r")        # "herro"

# Numeric functions
max(5, 12, 9)                     # 12
min(5, 12, 9)                     # 5
ceil(5.1)                         # 6
floor(5.9)                        # 5
parseint("100", 10)               # 100

# Collection functions
length([1, 2, 3])                 # 3
element(["a", "b", "c"], 1)       # "b"
concat([1, 2], [3, 4])            # [1, 2, 3, 4]
contains(["a", "b"], "a")         # true
distinct([1, 2, 2, 3])            # [1, 2, 3]
flatten([[1, 2], [3, 4]])         # [1, 2, 3, 4]
keys({a = 1, b = 2})              # ["a", "b"]
values({a = 1, b = 2})            # [1, 2]
lookup({a = 1, b = 2}, "a", 0)    # 1
merge({a = 1}, {b = 2})           # {a = 1, b = 2}
reverse([1, 2, 3])                # [3, 2, 1]
slice([1, 2, 3, 4], 1, 3)         # [2, 3]
sort(["c", "a", "b"])             # ["a", "b", "c"]

# Encoding functions
base64encode("hello")             # "aGVsbG8="
base64decode("aGVsbG8=")          # "hello"
jsonencode({key = "value"})       # "{\"key\":\"value\"}"
jsondecode("{\"key\":\"value\"}")  # {key = "value"}
yamlencode({key = "value"})       # "key: value\n"
yamldecode("key: value")          # {key = "value"}

# Filesystem functions
file("path/to/file.txt")          # Read file content
templatefile("template.tpl", {    # Render template
  var1 = "value1"
})

# Date/time functions
timestamp()                       # "2024-01-15T12:30:45Z"
formatdate("DD MMM YYYY", timestamp()) # "15 Jan 2024"

# Network functions
cidrhost("10.0.0.0/24", 5)        # "10.0.0.5"
cidrnetmask("10.0.0.0/24")        # "255.255.255.0"
cidrsubnet("10.0.0.0/16", 8, 2)   # "10.0.2.0/24"

# Type conversion
tostring(42)                      # "42"
tonumber("42")                    # 42
tobool("true")                    # true
tolist([1, 2, 3])                 # [1, 2, 3]
toset([1, 2, 2, 3])              # [1, 2, 3]
tomap({a = 1})                    # {a = 1}

# Conditional functions
can(regex("^[a-z]+$", var.name))  # true if valid
try(var.optional_value, "default") # Return first valid value

Conditional Expressions

# Ternary operator
instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"

# With count for conditional resources
resource "aws_instance" "web" {
  count = var.create_instance ? 1 : 0
  # ... configuration
}

# Dynamic blocks
resource "aws_security_group" "example" {
  name = "example"

  dynamic "ingress" {
    for_each = var.ingress_rules
    content {
      from_port   = ingress.value.from_port
      to_port     = ingress.value.to_port
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
    }
  }
}

Meta-Arguments

Resources support special arguments:

# depends_on: Explicit dependencies
resource "aws_instance" "web" {
  depends_on = [aws_security_group.web_sg]
  # ...
}

# count: Create multiple instances
resource "aws_instance" "web" {
  count         = 3
  ami           = var.ami_id
  instance_type = "t3.micro"

  tags = {
    Name = "web-server-${count.index}"
  }
}

# for_each: Create from map or set
resource "aws_instance" "servers" {
  for_each = var.servers  # map or set

  ami           = each.value.ami
  instance_type = each.value.type

  tags = {
    Name = each.key
  }
}

# provider: Specify provider configuration
resource "aws_instance" "replica" {
  provider = aws.us-west-2
  # ...
}

# lifecycle: Control resource behavior
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = "t3.micro"

  lifecycle {
    create_before_destroy = true  # Create new before destroying old
    prevent_destroy       = true  # Prevent accidental deletion
    ignore_changes        = [      # Ignore specific changes
      tags,
      user_data
    ]
  }
}

Providers

Provider Configuration

Configure infrastructure platforms:

# AWS Provider
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region

  default_tags {
    tags = {
      ManagedBy = "Terraform"
      Project   = var.project_name
    }
  }
}

# Azure Provider
provider "azurerm" {
  features {
    resource_group {
      prevent_deletion_if_contains_resources = true
    }
  }
}

# GCP Provider
provider "google" {
  project = var.gcp_project_id
  region  = var.gcp_region
}

# Kubernetes Provider
provider "kubernetes" {
  config_path = "~/.kube/config"
}

# Multiple provider configurations (aliases)
provider "aws" {
  alias  = "us_east_1"
  region = "us-east-1"
}

provider "aws" {
  alias  = "us_west_2"
  region = "us-west-2"
}

# Use aliased provider
resource "aws_instance" "east" {
  provider = aws.us_east_1
  # ...
}

Provider Authentication

Secure authentication methods:

# AWS - Environment variables (recommended)
# export AWS_ACCESS_KEY_ID="..."
# export AWS_SECRET_ACCESS_KEY="..."
# export AWS_SESSION_TOKEN="..."  # for temporary credentials

provider "aws" {
  region = "us-east-1"
  # Credentials from environment or ~/.aws/credentials
}

# AWS - Assume role
provider "aws" {
  region = "us-east-1"

  assume_role {
    role_arn     = "arn:aws:iam::123456789012:role/TerraformRole"
    session_name = "terraform-session"
    external_id  = "unique-id"
  }
}

# Azure - Service principal
provider "azurerm" {
  features {}

  client_id       = var.azure_client_id
  client_secret   = var.azure_client_secret
  tenant_id       = var.azure_tenant_id
  subscription_id = var.azure_subscription_id
}

# Azure - Managed identity
provider "azurerm" {
  features {}
  use_msi = true
}

# GCP - Service account
provider "google" {
  credentials = file("path/to/service-account-key.json")
  project     = var.gcp_project_id
  region      = var.gcp_region
}

Resources

Resource Declaration

Define infrastructure components:

resource "resource_type" "resource_name" {
  argument_name = argument_value
}

# Example: S3 bucket
resource "aws_s3_bucket" "data" {
  bucket = "my-application-data"

  tags = {
    Name        = "Data Bucket"
    Environment = "production"
  }
}

# Reference resource attributes
resource "aws_s3_bucket_versioning" "data" {
  bucket = aws_s3_bucket.data.id  # Reference bucket ID

  versioning_configuration {
    status = "Enabled"
  }
}

Resource Lifecycle

# Create, update, destroy lifecycle
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = var.instance_type

  # Lifecycle customization
  lifecycle {
    # Create replacement before destroying
    create_before_destroy = true

    # Prevent destruction
    prevent_destroy = false

    # Ignore changes to specific attributes
    ignore_changes = [
      tags["LastModified"],
      user_data
    ]

    # Replace if specific attributes change
    replace_triggered_by = [
      aws_security_group.web.id
    ]
  }
}

Data Sources

Query existing infrastructure or external data:

# AWS AMI lookup
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]  # Canonical

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

# Use data source in resource
resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"
}

# VPC lookup
data "aws_vpc" "default" {
  default = true
}

# Availability zones
data "aws_availability_zones" "available" {
  state = "available"
}

# Current region
data "aws_region" "current" {}

# Current account
data "aws_caller_identity" "current" {}

# External data source
data "external" "example" {
  program = ["python", "${path.module}/script.py"]

  query = {
    key = "value"
  }
}

# HTTP data source
data "http" "ip" {
  url = "https://ifconfig.me"
}

# Template file (deprecated, use templatefile())
data "template_file" "user_data" {
  template = file("${path.module}/user-data.sh")

  vars = {
    server_port = 8080
    db_address  = aws_db_instance.main.address
  }
}

Modules

Module Structure

Organize code into reusable modules:

modules/
├── vpc/
│   ├── main.tf       # Resources
│   ├── variables.tf  # Input variables
│   ├── outputs.tf    # Output values
│   └── README.md     # Documentation
├── compute/
│   ├── main.tf
│   ├── variables.tf
│   ├── outputs.tf
│   └── versions.tf   # Provider requirements
└── database/
    ├── main.tf
    ├── variables.tf
    └── outputs.tf

Creating a Module

# modules/vpc/variables.tf
variable "vpc_name" {
  type        = string
  description = "Name of the VPC"
}

variable "vpc_cidr" {
  type        = string
  description = "CIDR block for VPC"
  default     = "10.0.0.0/16"
}

variable "availability_zones" {
  type        = list(string)
  description = "List of availability zones"
}

variable "public_subnet_cidrs" {
  type        = list(string)
  description = "CIDR blocks for public subnets"
}

variable "private_subnet_cidrs" {
  type        = list(string)
  description = "CIDR blocks for private subnets"
}

variable "enable_nat_gateway" {
  type        = bool
  description = "Enable NAT gateway for private subnets"
  default     = true
}

variable "tags" {
  type        = map(string)
  description = "Tags to apply to resources"
  default     = {}
}

# modules/vpc/main.tf
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = merge(
    var.tags,
    {
      Name = var.vpc_name
    }
  )
}

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = merge(
    var.tags,
    {
      Name = "${var.vpc_name}-igw"
    }
  )
}

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.vpc_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.vpc_name}-private-${count.index + 1}"
      Type = "private"
    }
  )
}

resource "aws_eip" "nat" {
  count  = var.enable_nat_gateway ? length(var.public_subnet_cidrs) : 0
  domain = "vpc"

  tags = merge(
    var.tags,
    {
      Name = "${var.vpc_name}-nat-eip-${count.index + 1}"
    }
  )
}

resource "aws_nat_gateway" "main" {
  count         = var.enable_nat_gateway ? length(var.public_subnet_cidrs) : 0
  allocation_id = aws_eip.nat[count.index].id
  subnet_id     = aws_subnet.public[count.index].id

  tags = merge(
    var.tags,
    {
      Name = "${var.vpc_name}-nat-${count.index + 1}"
    }
  )

  depends_on = [aws_internet_gateway.main]
}

# modules/vpc/outputs.tf
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 "nat_gateway_ids" {
  description = "IDs of NAT gateways"
  value       = aws_nat_gateway.main[*].id
}

output "internet_gateway_id" {
  description = "ID of the internet gateway"
  value       = aws_internet_gateway.main.id
}

Using Modules

# Root main.tf
module "vpc" {
  source = "./modules/vpc"

  vpc_name               = "production-vpc"
  vpc_cidr               = "10.0.0.0/16"
  availability_zones     = ["us-east-1a", "us-east-1b", "us-east-1c"]
  public_subnet_cidrs    = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  private_subnet_cidrs   = ["10.0.11.0/24", "10.0.12.0/24", "10.0.13.0/24"]
  enable_nat_gateway     = true

  tags = {
    Environment = "production"
    ManagedBy   = "Terraform"
  }
}

# Reference module outputs
resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"
  subnet_id     = module.vpc.public_subnet_ids[0]

  tags = {
    Name = "web-server"
  }
}

# Use remote 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
  }
}

# Use module from GitHub
module "consul" {
  source = "github.com/hashicorp/consul//terraform/aws"

  servers = 3
}

# Use module from Git with specific branch
module "vpc" {
  source = "git::https://github.com/organization/terraform-modules.git//vpc?ref=v1.2.0"

  # ...
}

Module Versioning

# In module source (versions.tf)
terraform {
  required_version = ">= 1.5.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# Using versioned modules
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"  # Allow minor and patch updates

  # ...
}

State Management

Local State

Default state stored locally:

# terraform.tfstate (automatically created)
{
  "version": 4,
  "terraform_version": "1.5.0",
  "resources": [
    {
      "mode": "managed",
      "type": "aws_instance",
      "name": "web",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "attributes": {
            "id": "i-1234567890abcdef0",
            "ami": "ami-0c55b159cbfafe1f0",
            "instance_type": "t3.micro"
          }
        }
      ]
    }
  ]
}

Remote State - S3 Backend

Store state in S3 for team collaboration:

# backend.tf
terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "production/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"

    # Optional: KMS encryption
    kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
  }
}

# Create S3 bucket and DynamoDB table for state
resource "aws_s3_bucket" "terraform_state" {
  bucket = "my-terraform-state"

  lifecycle {
    prevent_destroy = true
  }
}

resource "aws_s3_bucket_versioning" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id

  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }
}

Remote State - Azure Backend

terraform {
  backend "azurerm" {
    resource_group_name  = "terraform-state-rg"
    storage_account_name = "terraformstate12345"
    container_name       = "tfstate"
    key                  = "production.terraform.tfstate"
  }
}

Remote State - GCS Backend

terraform {
  backend "gcs" {
    bucket = "my-terraform-state"
    prefix = "production"
  }
}

Remote State - Terraform Cloud

terraform {
  cloud {
    organization = "my-organization"

    workspaces {
      name = "production-infrastructure"
    }
  }
}

Remote State Data Source

Read state from another configuration:

data "terraform_remote_state" "vpc" {
  backend = "s3"

  config = {
    bucket = "my-terraform-state"
    key    = "vpc/terraform.tfstate"
    region = "us-east-1"
  }
}

# Use outputs from remote state
resource "aws_instance" "web" {
  subnet_id = data.terraform_remote_state.vpc.outputs.public_subnet_ids[0]
  # ...
}

Workspaces

Manage multiple environments:

# List workspaces
terraform workspace list

# Create new workspace
terraform workspace new staging
terraform workspace new production

# Select workspace
terraform workspace select staging

# Show current workspace
terraform workspace show

# Delete workspace
terraform workspace delete staging

Workspace-Based Configuration

# Use workspace in resource naming
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = terraform.workspace == "prod" ? "t3.large" : "t3.micro"

  tags = {
    Name        = "web-${terraform.workspace}"
    Environment = terraform.workspace
  }
}

# Workspace-specific variables
locals {
  env_config = {
    dev = {
      instance_count = 1
      instance_type  = "t3.micro"
    }
    staging = {
      instance_count = 2
      instance_type  = "t3.small"
    }
    prod = {
      instance_count = 5
      instance_type  = "t3.large"
    }
  }

  current_env = local.env_config[terraform.workspace]
}

resource "aws_instance" "app" {
  count         = local.current_env.instance_count
  instance_type = local.current_env.instance_type
  # ...
}

Best Practices

Code Organization

terraform-project/
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── terraform.tfvars
│   │   └── backend.tf
│   ├── staging/
│   │   └── ...
│   └── production/
│       └── ...
├── modules/
│   ├── vpc/
│   ├── compute/
│   └── database/
├── global/
│   ├── iam/
│   └── route53/
└── README.md

Naming Conventions

# Resource naming: <resource_type>_<name>_<purpose>
resource "aws_security_group" "web_server_public" { }
resource "aws_instance" "web_server_primary" { }

# Variable naming: descriptive and specific
variable "vpc_cidr_block" { }
variable "database_instance_class" { }
variable "enable_auto_scaling" { }

# Tags: consistent and comprehensive
tags = {
  Name        = "resource-name"
  Environment = var.environment
  Project     = var.project_name
  ManagedBy   = "Terraform"
  Owner       = "team-name"
  CostCenter  = "engineering"
}

Security Best Practices

# Never hardcode credentials
# BAD
provider "aws" {
  access_key = "AKIAIOSFODNN7EXAMPLE"
  secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}

# GOOD - Use environment variables or IAM roles
provider "aws" {
  region = "us-east-1"
}

# Use sensitive flag for secrets
variable "database_password" {
  type      = string
  sensitive = true
}

# Encrypt state files
terraform {
  backend "s3" {
    bucket  = "terraform-state"
    key     = "terraform.tfstate"
    encrypt = true
  }
}

# Use .gitignore
# .gitignore
*.tfstate
*.tfstate.*
.terraform/
*.tfvars  # if contains secrets
crash.log
override.tf
override.tf.json

DRY (Don't Repeat Yourself)

# Use locals for repeated values
locals {
  common_tags = {
    Environment = var.environment
    ManagedBy   = "Terraform"
    Project     = var.project_name
  }
}

resource "aws_instance" "web" {
  tags = local.common_tags
}

resource "aws_s3_bucket" "data" {
  tags = local.common_tags
}

# Use modules for reusable infrastructure
module "web_server" {
  source = "./modules/ec2-instance"

  instance_type = "t3.micro"
  tags          = local.common_tags
}

# Use for_each to avoid duplication
resource "aws_instance" "servers" {
  for_each = var.servers

  ami           = each.value.ami
  instance_type = each.value.type

  tags = merge(
    local.common_tags,
    {
      Name = each.key
    }
  )
}

Documentation

# Document variables
variable "vpc_cidr" {
  type        = string
  description = "CIDR block for VPC. Must not overlap with existing VPCs."
  default     = "10.0.0.0/16"

  validation {
    condition     = can(cidrhost(var.vpc_cidr, 0))
    error_message = "Must be a valid IPv4 CIDR block."
  }
}

# Document outputs
output "vpc_id" {
  description = "ID of the VPC. Use this to reference the VPC in other configurations."
  value       = aws_vpc.main.id
}

# Add README.md to modules
# modules/vpc/README.md
# VPC Module

Creates a VPC with public and private subnets across multiple AZs.

## Usage

```hcl
module "vpc" {
  source = "./modules/vpc"

  vpc_name             = "my-vpc"
  vpc_cidr             = "10.0.0.0/16"
  availability_zones   = ["us-east-1a", "us-east-1b"]
  public_subnet_cidrs  = ["10.0.1.0/24", "10.0.2.0/24"]
  private_subnet_cidrs = ["10.0.11.0/24", "10.0.12.0/24"]
}

Inputs

Name Description Type Default Required
vpc_name Name of the VPC string - yes
vpc_cidr CIDR block for VPC string 10.0.0.0/16 no

Outputs

Name Description
vpc_id ID of the VPC
public_subnet_ids IDs of public subnets

### Testing Infrastructure

```hcl
# Use terraform validate
terraform validate

# Use terraform plan
terraform plan -out=tfplan

# Use terraform fmt for consistent formatting
terraform fmt -recursive

# Use external tools
# tflint - Terraform linter
# checkov - Security scanner
# terraform-docs - Generate documentation
# terrascan - Policy scanner

Advanced Patterns

Dynamic Backend Configuration

# backend-config-dev.hcl
bucket = "terraform-state-dev"
key    = "dev/terraform.tfstate"
region = "us-east-1"

# backend-config-prod.hcl
bucket = "terraform-state-prod"
key    = "prod/terraform.tfstate"
region = "us-east-1"

# Initialize with backend config
# terraform init -backend-config=backend-config-dev.hcl

Conditional Resource Creation

# Create resource only in production
resource "aws_cloudwatch_alarm" "high_cpu" {
  count = var.environment == "prod" ? 1 : 0

  alarm_name          = "high-cpu-utilization"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 2
  metric_name         = "CPUUtilization"
  namespace           = "AWS/EC2"
  period              = 300
  statistic           = "Average"
  threshold           = 80
}

Zero-Downtime Deployments

# Blue-Green deployment with create_before_destroy
resource "aws_autoscaling_group" "app" {
  name                = "${var.app_name}-${var.version}"
  launch_configuration = aws_launch_configuration.app.name
  min_size            = var.min_size
  max_size            = var.max_size

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_launch_configuration" "app" {
  name_prefix   = "${var.app_name}-"
  image_id      = var.ami_id
  instance_type = var.instance_type

  lifecycle {
    create_before_destroy = true
  }
}

Moved Blocks for Refactoring

# Refactor without destroying resources
moved {
  from = aws_instance.web
  to   = module.compute.aws_instance.web
}

moved {
  from = aws_security_group.web[0]
  to   = aws_security_group.web["primary"]
}

Import Existing Resources

# Import existing resource into Terraform state
terraform import aws_instance.web i-1234567890abcdef0

# Import with for_each
terraform import 'aws_instance.servers["web-1"]' i-1234567890abcdef0

Skill Version: 1.0.0 Last Updated: October 2025 Skill Category: Infrastructure as Code, Cloud Engineering, DevOps Compatible With: AWS, Azure, GCP, Kubernetes, Terraform Cloud