Die Oberfläche ist lokalisiert; Skill-Beschreibungen und -Dokumente werden per KI automatisch in Ihre Sprache übersetzt. Code, Befehle, Pfade und Fachbegriffe bleiben im Original, maßgeblich ist das Dokument des Autors.
Skills entdecken
Bereitstellung und Betrieb · SKILL.md

terraform

terraform

Use this skill when provisioning infrastructure with Terraform — module structure, state management, remote backends, workspaces, CI/CD integration, policy as code. This skill enforces: environment directory structure, remote backends with state locking, semantic versioning for modules, no hardcoded values, sensitive output marking, plan review before apply…

Independent GitHub snapshot · Automated classification · Original documentation governs

Skill-Dokumentation

GitHub

name: terraform description: > Use this skill when provisioning infrastructure with Terraform — module structure, state management, remote backends, workspaces, CI/CD integration, policy as code. This skill enforces: environment directory structure, remote backends with state locking, semantic versioning for modules, no hardcoded values, sensitive output marking, plan review before apply. Do NOT use for: configuration management (use Ansible), Kubernetes application deployment (use Helm), non-HashiCorp IaC tools. version: "1.0.0" author: "j4flmao" license: "MIT" compatibility: claude-code: true cursor: true codex: true windsurf: true tags: [devops, terraform, phase-5]

Terraform Patterns

Purpose

Define and enforce Terraform infrastructure provisioning patterns with module design, state management, and CI/CD integration.

Agent Protocol

Trigger

User request includes: terraform, tf, iac, infrastructure as code, hcl, terraform module, terraform state, terraform workspace, terraform backend, terragrunt.

Input Context

  • Cloud provider (AWS, GCP, Azure)
  • Current IaC state (if any)
  • Team structure (who manages infrastructure)
  • State storage requirements
  • Compliance requirements

Output Artifact

A markdown document containing:

  • Module structure (directory layout, naming)
  • State management strategy (remote backend, locking)
  • Workspace/environment strategy
  • Module design (input/output conventions, versioning)
  • CI/CD pipeline integration
  • Policy as code (Sentinel, OPA/Rego)
  • Secret management

Response Format

Produce the artifact directly. No preamble, no postamble, no explanations. No filler, no hedging, no transitions. Strip articles a/an/the where unambiguous. Compress output — why use many token when few do trick.

Completion Criteria

  • Module directory structure documented
  • Backend configuration with state locking
  • Workspace or directory strategy per environment
  • Module input/output conventions defined
  • CI/CD pipeline steps for plan/apply

Max Response Length

4096 tokens

Decision Tree: Directory Structure vs Workspaces vs Terragrunt

StrategyWhenProsCons
Directory per envProduction workloads, different configsClear separation, parallel appliesConfig duplication
WorkspacesDev/test, same configDRY, minimal filesAccidental cross-env changes
TerragruntMulti-module dependenciesDRY, dependency resolutionAdditional tooling, complexity

Rule: Prefer directory per environment for production. Workspaces for dev/test only.

Workflow

Step 1: Set Up Repository Structure

terraform/
├── environments/
│   ├── production/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── outputs.tf
│   │   ├── terraform.tfvars
│   │   ├── backend.hcl
│   │   └── provider.tf
│   ├── staging/
│   │   └── ...
│   └── development/
│       └── ...
├── modules/
│   ├── eks/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── outputs.tf
│   │   └── README.md
│   ├── rds/
│   │   └── ...
│   ├── vpc/
│   │   └── ...
│   └── iam/
│       └── ...
├── policies/
│   ├── required_tags.rego
│   ├── allowed_instance_types.rego
│   └── enforce_encryption.rego
├── scripts/
│   └── plan-apply.sh
├── versions.tf
├── terragrunt.hcl
└── README.md

Step 2: Configure Backend

# environments/production/backend.hcl
bucket         = "tf-state-prod"
key            = "production/terraform.tfstate"
region         = "us-east-1"
encrypt        = true
dynamodb_table = "tf-state-lock"

# environments/production/main.tf
terraform {
  backend "s3" {
    # Use -backend-config=backend.hcl for partial config
  }
  required_version = ">= 1.5"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "~> 2.0"
    }
  }
}

Step 3: Backend Selection Comparison

BackendState LockingEncryptionBest For
S3 + DynamoDBYes (DynamoDB)SSE-S3/KMSAWS-native teams
GCSYes (native)AES256/CMEKGCP-native teams
AzureRMYes (Blob lease)SSEAzure-native teams
Terraform CloudYes (native)At rest + in transitTeam collaboration, remote runs
ConsulYes (session)TLSSelf-hosted, simple
localNoNoPersonal projects only

Step 4: Design Modules — VPC Example

# modules/vpc/main.tf
variable "name" {
  description = "VPC name"
  type        = string
}

variable "cidr_block" {
  description = "CIDR block for VPC"
  type        = string
  validation {
    condition     = can(cidrsubnet(var.cidr_block, 0, 0))
    error_message = "Must be a valid CIDR block."
  }
}

variable "azs" {
  description = "Availability zones"
  type        = list(string)
}

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

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

resource "aws_vpc" "this" {
  cidr_block           = var.cidr_block
  enable_dns_hostnames = true
  enable_dns_support   = true
  tags = merge(var.tags, {
    Name = var.name
  })
}

resource "aws_subnet" "public" {
  count             = length(var.azs)
  vpc_id            = aws_vpc.this.id
  cidr_block        = cidrsubnet(var.cidr_block, 8, count.index)
  availability_zone = var.azs[count.index]
  map_public_ip_on_launch = true
  tags = merge(var.tags, {
    Name = "${var.name}-public-${count.index}"
    Tier = "public"
  })
}

resource "aws_subnet" "private" {
  count             = length(var.azs)
  vpc_id            = aws_vpc.this.id
  cidr_block        = cidrsubnet(var.cidr_block, 8, count.index + 100)
  availability_zone = var.azs[count.index]
  tags = merge(var.tags, {
    Name = "${var.name}-private-${count.index}"
    Tier = "private"
  })
}

output "vpc_id" {
  description = "VPC ID"
  value       = aws_vpc.this.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
}

Step 5: Module Usage and Composition

# environments/production/main.tf
module "vpc" {
  source          = "../../modules/vpc"
  version         = "1.2.0"  # Module version pinning
  name            = "prod"
  cidr_block      = "10.0.0.0/16"
  azs             = ["us-east-1a", "us-east-1b", "us-east-1c"]
  enable_nat_gateway = true
  tags = {
    Environment = "production"
    ManagedBy   = "terraform"
  }
}

module "eks" {
  source       = "../../modules/eks"
  cluster_name = "prod-cluster"
  subnet_ids   = module.vpc.private_subnet_ids
  node_groups = {
    general = {
      instance_types = ["t3.medium"]
      min_size       = 2
      max_size       = 10
      desired_size   = 3
      disk_size      = 50
    }
    gpu = {
      instance_types = ["g5.xlarge"]
      min_size       = 0
      max_size       = 5
      desired_size   = 0
      disk_size      = 200
    }
  }
  tags = {
    Environment = "production"
  }
}

Step 6: CI/CD Pipeline with Plan Approval

name: Terraform
on:
  pull_request:
    paths: ['terraform/**']
  push:
    branches: [main]
    paths: ['terraform/**']

env:
  TF_VERSION: 1.7.0
  ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: hashicorp/setup-terraform@v3
      with:
        terraform_version: ${{ env.TF_VERSION }}
    - name: Init
      run: |
        terraform init \
          -backend-config=environments/${{ env.ENV }}/backend.hcl \
          -reconfigure
      working-directory: terraform/environments/${{ env.ENV }}
    - name: Validate
      run: terraform validate
      working-directory: terraform/environments/${{ env.ENV }}
    - name: Check formatting
      run: terraform fmt -check -recursive
    - name: Security scan
      uses: aquasecurity/tfsec-action@v1.0.0
      with:
        working_directory: terraform

  plan:
    needs: validate
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Init
      run: |
        terraform init \
          -backend-config=environments/${{ env.ENV }}/backend.hcl
      working-directory: terraform/environments/${{ env.ENV }}
    - name: Plan
      id: plan
      run: |
        terraform plan \
          -var-file=terraform.tfvars \
          -out=tfplan \
          -no-color
      working-directory: terraform/environments/${{ env.ENV }}
    - name: Post plan comment (PR only)
      if: github.event_name == 'pull_request'
      uses: actions/github-script@v7
      with:
        script: |
          const output = `${{ steps.plan.outputs.stdout }}`;
          github.rest.issues.createComment({
            issue_number: context.issue.number,
            owner: context.repo.owner,
            repo: context.repo.repo,
            body: `## Terraform Plan (${process.env.ENV}) \n\`\`\`\n${output}\n\`\`\``
          })

  apply:
    needs: plan
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production  # Requires approval in GitHub Environments
    steps:
    - uses: actions/checkout@v4
    - name: Init
      run: |
        terraform init \
          -backend-config=environments/${{ env.ENV }}/backend.hcl
      working-directory: terraform/environments/${{ env.ENV }}
    - name: Apply
      run: terraform apply tfplan
      working-directory: terraform/environments/${{ env.ENV }}

Step 7: Policy as Code — OPA/Rego

# policies/enforce_encryption.rego
package terraform

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_s3_bucket"
  not resource.change.after.server_side_encryption_configuration
  msg := sprintf("%s (%s) must have encryption enabled", [resource.type, resource.address])
}

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_db_instance"
  not resource.change.after.storage_encrypted
  msg := sprintf("%s (%s) must have storage encrypted", [resource.type, resource.address])
}

deny[msg] {
  resource := input.resource_changes[_]
  resource.type in ["aws_security_group", "aws_security_group_rule"]
  resource.change.after.from_port == 0
  resource.change.after.to_port == 65535
  resource.change.after.cidr_blocks[_] == "0.0.0.0/0"
  msg := sprintf("%s (%s) has overly permissive ingress from 0.0.0.0/0", [resource.type, resource.address])
}

Step 8: Secret Management

# Option 1: Terraform sensitive variables
variable "db_password" {
  description = "Database admin password"
  type        = string
  sensitive   = true
}

# Option 2: Vault data source
data "vault_kv_secret_v2" "db" {
  mount = "secret"
  name  = "production/database"
}

resource "aws_db_instance" "main" {
  password = data.vault_kv_secret_v2.db.data["password"]
}

# Option 3: AWS Secrets Manager
data "aws_secretsmanager_secret_version" "db" {
  secret_id = "production/database"
}

resource "aws_db_instance" "main" {
  password = jsondecode(data.aws_secretsmanager_secret_version.db.secret_string)["password"]
}

# Option 4: sops-encrypted vars
# Use `sops` to encrypt terraform.tfvars.sops
# Decode in pipeline: sops -d terraform.tfvars.sops > terraform.tfvars

Step 9: State Operations

# Import existing resources
terraform import aws_s3_bucket.my_bucket my-bucket-name
terraform import module.vpc.aws_vpc.this vpc-12345

# State list and show
terraform state list
terraform state show aws_s3_bucket.my_bucket

# State move (rename resource)
terraform state mv aws_s3_bucket.old aws_s3_bucket.new

# State removal
terraform state rm aws_s3_bucket.deleted

# State recovery
terraform state pull > backup.tfstate
# Edit manually, then:
terraform state push backup.tfstate

# Force unlock (DANGER: only when lock is orphaned)
terraform force-unlock LOCK_ID

Step 10: Advanced Module Patterns

# modules/rds/main.tf — Multi-engine RDS
variable "engine" {
  description = "Database engine"
  type        = string
  validation {
    condition     = contains(["postgres", "mysql", "aurora"], var.engine)
    error_message = "Engine must be postgres, mysql, or aurora."
  }
}

variable "performance_insights_enabled" {
  description = "Enable Performance Insights"
  type        = bool
  default     = true
}

locals {
  engine_port = {
    postgres = 5432
    mysql    = 3306
    aurora   = 3306
  }
  port = local.engine_port[var.engine]
}

resource "aws_db_instance" "this" {
  engine         = var.engine
  port           = local.port
  identifier     = "${var.name}-${var.engine}"
  performance_insights_enabled = var.performance_insights_enabled
  performance_insights_retention_period = var.performance_insights_enabled ? 7 : 0
  enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
  backup_retention_period = var.environment == "production" ? 30 : 7
}

Rules

  • State files NEVER in Git. Remote backend + locking required.
  • All modules published with semantic versioning tag.
  • Every module has README with usage, inputs, outputs.
  • terraform plan output must be reviewed before apply in CI.
  • No hardcoded values in modules — all configurable via variables.
  • Sensitive outputs marked with sensitive = true.
  • Directory per environment for production; workspaces for development/testing.
  • Always pin provider versions in required_providers.

Production Considerations

  • Use Terraform Cloud or Atlantis for collaborative plan/apply workflow.
  • Set up state file versioning (S3 versioning, GCS object versioning).
  • Run terraform validate and tflint in CI before plan.
  • Use Terraform workspaces only for dev/test, not production.
  • Tag all resources with ManagedBy: terraform for cost tracking.
  • Use prevent_destroy lifecycle on critical resources.
  • Set create_before_destroy for zero-downtime replacements.
  • Use terraform plan -out=tfplan to lock the plan for apply.
  • Run cost estimation tools (Infracost) in CI.

Anti-Patterns

  • State in Git — plaintext secrets, merge conflicts, corruption risk.
  • No state locking — concurrent applies corrupt state.
  • Hardcoded values in modules — not reusable across environments.
  • No provider version pinning — unexpected provider changes break infrastructure.
  • One monolithic state file for everything — slow, high blast radius.
  • terraform apply without plan — uncontrolled changes.
  • Not using sensitive flag for passwords — secrets in plaintext output.
  • ignore_changes as a crutch — masks real drift.
  • Overusing count instead of for_each — index-based references break on reorder.
  • Manual state edits without backup — corrupt state, data loss.

References

  • references/modules-composition.md — Terraform Modules and Composition
  • references/state-management.md — Terraform State Management
  • references/terraform-advanced.md — Terraform Advanced
  • references/terraform-ecosystem.md — Terraform Ecosystem
  • references/terraform-fundamentals.md — Terraform Fundamentals
  • references/terraform-modules.md — Terraform Module Design
  • references/terraform-state.md — Terraform State Management

Handoff

Hand off to devops/helm-patterns/SKILL.md for K8s application deployment. Hand off to devops/ansible/SKILL.md for post-provisioning configuration.

Gespeicherte Skills