Agent Skills: Terraform / OpenTofu

Creates and manages Infrastructure as Code using Terraform/OpenTofu for cloud resource provisioning. Trigger keywords: terraform, opentofu, tofu, infrastructure, IaC, provision, cloud, aws, azure, gcp, module.

terraforminfrastructure-as-codemulti-cloudawsgcp
deployID: cosmix/claude-loom/terraform

Skill Files

Browse the full folder contents for terraform.

Download Skill

Loading file tree…

skills/terraform/SKILL.md

Skill Metadata

Name
terraform
Description
Creates and manages Infrastructure as Code using Terraform/OpenTofu for cloud resource provisioning. Trigger keywords: terraform, opentofu, tofu, infrastructure, IaC, provision, cloud, aws, azure, gcp, module.

Terraform / OpenTofu

Overview

This skill covers Terraform and OpenTofu configuration for infrastructure provisioning across cloud providers. It includes module design, state management, and infrastructure best practices.

Preferred Tool: OpenTofu - OpenTofu is the open-source fork of Terraform maintained by the Linux Foundation. Prefer tofu commands over terraform when available. The syntax and configuration are fully compatible.

Instructions

1. Plan Infrastructure

  • Define resource requirements
  • Plan network topology
  • Identify dependencies
  • Consider multi-region/AZ design

2. Write Terraform Configuration

  • Structure code in modules
  • Define variables and outputs
  • Configure providers
  • Set up state backend

3. Implement Best Practices

  • Use consistent naming
  • Tag all resources
  • Implement least privilege
  • Enable encryption

4. Manage State

  • Configure remote state
  • Use state locking
  • Plan state migrations
  • Handle secrets properly

Best Practices

  1. Use Modules: Reusable, versioned components
  2. Remote State: Store state in S3/GCS with locking
  3. Variables: Parameterize everything
  4. Workspaces: Separate environments
  5. Formatting: Use tofu fmt (or terraform fmt)
  6. Validation: Use tofu validate (or terraform validate)
  7. Plan Before Apply: Always review changes with tofu plan

OpenTofu vs Terraform Commands

| Terraform | OpenTofu (Preferred) | | -------------------- | -------------------- | | terraform init | tofu init | | terraform plan | tofu plan | | terraform apply | tofu apply | | terraform destroy | tofu destroy | | terraform fmt | tofu fmt | | terraform validate | tofu validate |

Examples

Example 1: AWS VPC Module

# modules/vpc/main.tf
terraform {
  required_version = ">= 1.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

variable "name" {
  description = "Name prefix for resources"
  type        = string
}

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

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

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

locals {
  common_tags = merge(var.tags, {
    Terraform   = "true"
    Module      = "vpc"
  })
}

resource "aws_vpc" "main" {
  cidr_block           = var.cidr_block
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = merge(local.common_tags, {
    Name = "${var.name}-vpc"
  })
}

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

  tags = merge(local.common_tags, {
    Name = "${var.name}-igw"
  })
}

resource "aws_subnet" "public" {
  count = length(var.availability_zones)

  vpc_id                  = aws_vpc.main.id
  cidr_block              = cidrsubnet(var.cidr_block, 4, count.index)
  availability_zone       = var.availability_zones[count.index]
  map_public_ip_on_launch = true

  tags = merge(local.common_tags, {
    Name = "${var.name}-public-${var.availability_zones[count.index]}"
    Tier = "public"
  })
}

resource "aws_subnet" "private" {
  count = length(var.availability_zones)

  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(var.cidr_block, 4, count.index + length(var.availability_zones))
  availability_zone = var.availability_zones[count.index]

  tags = merge(local.common_tags, {
    Name = "${var.name}-private-${var.availability_zones[count.index]}"
    Tier = "private"
  })
}

resource "aws_eip" "nat" {
  count  = length(var.availability_zones)
  domain = "vpc"

  tags = merge(local.common_tags, {
    Name = "${var.name}-nat-eip-${count.index}"
  })
}

resource "aws_nat_gateway" "main" {
  count = length(var.availability_zones)

  allocation_id = aws_eip.nat[count.index].id
  subnet_id     = aws_subnet.public[count.index].id

  tags = merge(local.common_tags, {
    Name = "${var.name}-nat-${count.index}"
  })

  depends_on = [aws_internet_gateway.main]
}

output "vpc_id" {
  description = "VPC ID"
  value       = aws_vpc.main.id
}

output "public_subnet_ids" {
  description = "Public subnet IDs"
  value       = aws_subnet.public[*].id
}

output "private_subnet_ids" {
  description = "Private subnet IDs"
  value       = aws_subnet.private[*].id
}

Example 2: EKS Cluster Configuration

# main.tf
terraform {
  required_version = ">= 1.0"

  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "eks/terraform.tfstate"
    region         = "us-west-2"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

provider "aws" {
  region = var.region

  default_tags {
    tags = {
      Environment = var.environment
      Project     = var.project
      ManagedBy   = "terraform"
    }
  }
}

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

  name               = "${var.project}-${var.environment}"
  cidr_block         = var.vpc_cidr
  availability_zones = var.availability_zones
  tags               = var.tags
}

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 19.0"

  cluster_name    = "${var.project}-${var.environment}"
  cluster_version = "1.28"

  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnet_ids

  cluster_endpoint_public_access = true

  eks_managed_node_groups = {
    general = {
      desired_size = 2
      min_size     = 1
      max_size     = 5

      instance_types = ["t3.medium"]
      capacity_type  = "ON_DEMAND"

      labels = {
        role = "general"
      }
    }
  }

  tags = var.tags
}

Example 3: Variables and Outputs

# variables.tf
variable "region" {
  description = "AWS region"
  type        = string
  default     = "us-west-2"
}

variable "environment" {
  description = "Environment name"
  type        = string
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment must be dev, staging, or prod."
  }
}

variable "project" {
  description = "Project name"
  type        = string
}

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

variable "availability_zones" {
  description = "List of availability zones"
  type        = list(string)
  default     = ["us-west-2a", "us-west-2b", "us-west-2c"]
}

variable "tags" {
  description = "Additional tags"
  type        = map(string)
  default     = {}
}

# outputs.tf
output "vpc_id" {
  description = "VPC ID"
  value       = module.vpc.vpc_id
}

output "eks_cluster_endpoint" {
  description = "EKS cluster endpoint"
  value       = module.eks.cluster_endpoint
  sensitive   = true
}

output "eks_cluster_name" {
  description = "EKS cluster name"
  value       = module.eks.cluster_name
}