Skip to content
Back to skills

Terraform Modules

ASecurity

Design reusable, well-tested Terraform modules with cloud-agnostic interfaces and safe state management.

  • 19 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 19, 2026
ai-agentsbashawsterraformtestingdebuggingrefactoringbackendci/cdsecurity

Security analysis

A100/100

Scanned October 19, 2026

npx -y skills add sawrus/agent-guides --skill terraform-modules --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Terraform Modules?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Terraform Modules
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/sawrus-terraform-modules/badge)](https://www.skillsdirectory.com/skills/sawrus-terraform-modules)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: terraform-modules
type: skill
description: Design reusable, well-tested Terraform modules with cloud-agnostic interfaces and safe state management.
related-rules:
  - iac-standards.md
  - state-management.md
  - secret-hygiene.md
allowed-tools: Read, Write, Edit, Bash
---

# Skill: Terraform Modules

> **Expertise:** Reusable module design, for_each patterns, remote state, data sources, module testing with Terratest.

## When to load

When writing new Terraform, reviewing IaC PRs, designing module interfaces, or debugging plan/apply failures.

## Module Interface Design

```hcl
# variables.tf — define a clean, minimal interface
variable "project" {
  description = "Project name used in resource naming and tags"
  type        = string
}

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

variable "instance_count" {
  description = "Number of instances to create"
  type        = number
  default     = 1
}

# outputs.tf — expose only what callers need
output "instance_ids" {
  description = "List of created instance IDs"
  value       = aws_instance.this[*].id
}

output "private_ips" {
  description = "Private IP addresses"
  value       = aws_instance.this[*].private_ip
  sensitive   = false
}
```

## for_each vs count

```hcl
# ✅ for_each — stable keys, safe to add/remove
resource "aws_security_group_rule" "allow" {
  for_each    = var.allowed_ports   # map: { "http" = 80, "https" = 443 }
  type        = "ingress"
  from_port   = each.value
  to_port     = each.value
  protocol    = "tcp"
  cidr_blocks = ["0.0.0.0/0"]
}

# ❌ count — index-based, removing item N shifts all subsequent items
resource "aws_instance" "this" {
  count = var.instance_count   # removing instance 0 destroys ALL and recreates
}

# ✅ for_each with map for instances
resource "aws_instance" "this" {
  for_each      = var.instances   # map: { "web-1" = {...}, "web-2" = {...} }
  instance_type = each.value.instance_type
}
```

## Dynamic Blocks

```hcl
resource "aws_security_group" "this" {
  name = "${var.project}-${var.environment}-sg"

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

## Data Sources (cloud-agnostic patterns)

```hcl
# Latest Ubuntu 22.04 AMI (AWS)
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]  # Canonical
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }
}

# Cross-stack reference via SSM (avoid terraform_remote_state across envs)
data "aws_ssm_parameter" "vpc_id" {
  name = "/${var.environment}/network/vpc_id"
}
```

## Locals for DRY Code

```hcl
locals {
  name_prefix = "${var.project}-${var.environment}"
  common_tags = {
    Project     = var.project
    Environment = var.environment
    ManagedBy   = "terraform"
    Owner       = var.owner
  }
}

resource "aws_s3_bucket" "this" {
  bucket = "${local.name_prefix}-assets-${random_id.suffix.hex}"
  tags   = local.common_tags
}
```

## moved Block (safe refactoring)

```hcl
# When renaming a resource — prevents destroy+create
moved {
  from = aws_instance.web
  to   = aws_instance.this["web-1"]
}
```

## CI/CD Integration

```bash
# Standard pipeline steps
terraform init -backend-config=environments/${ENV}/backend.hcl
terraform validate
terraform fmt -check -recursive
terraform plan -var-file=environments/${ENV}/terraform.tfvars -out=tfplan
# After approval:
terraform apply tfplan
```

## Anti-Patterns

| Anti-pattern | Fix |
|:---|:---|
| `count` for multi-instance | Use `for_each` with map keys |
| Hardcoded region/AZ | Use `data` source or variable |
| `?ref=main` module source | Pin to version tag |
| Provider config inside module | Provider in root module only |
| `terraform_remote_state` across envs | SSM / Consul KV for cross-stack values |
| Sensitive values in outputs without `sensitive=true` | Mark all secret outputs as sensitive |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.