devops-terraform-patterns
Skill2 starsChanged 10 months agoMITView on GitHub
Terraform infrastructure-as-code patterns covering module design, state management, resource lifecycle, variables, CI/CD integration, and migration strategies for AWS and GCP.
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 20
Use for_each over count when resources need stable identity.
- Distinctive: line 108
Use typed variables with validation blocks.
- Very specific: line 163
Import existing resources into Terraform management.
- Very specific: line 182
After import, run terraform plan to verify no diff.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Terraform Patterns
Core Principles
- Infrastructure as Code — All infrastructure is declared in version-controlled
.tffiles. Manual changes are prohibited; every resource has a single source of truth in code. - State Is Sacred — Remote state with locking prevents corruption. Never edit state files by hand. Use
terraform statecommands for manipulation and always back up before operations. - Modular Design — Reusable modules encapsulate logical groupings of resources. Root modules compose child modules. Pin module versions to avoid surprises.
- Immutable Infrastructure — Prefer replacing resources over mutating them. Use
create_before_destroylifecycle rules for zero-downtime deployments. - Least Privilege Plans — CI pipelines run
planon every PR andapplyonly on merge to main. Human review of plan output is mandatory before any production apply.
Resource and Data Source Patterns
Use for_each over count when resources need stable identity. Data sources fetch existing infrastructure without managing it.
# Fetch existing VPC, create subnets with for_each for stable keys
data "aws_vpc" "main" {
filter {
name = "tag:Environment"
values = [var.environment]
}
}
resource "aws_subnet" "private" {
for_each = var.private_subnets
vpc_id = data.aws_vpc.main.id
cidr_block = each.value.cidr
availability_zone = each.value.az
tags = merge(var.common_tags, {
Name = "${var.project}-private-${each.key}"
Tier = "private"
})
}See resource-patterns for: lifecycle rules, depends_on usage, count vs for_each trade-offs, dynamic blocks, and provisioner patterns.
Module Design
Modules accept typed inputs, expose outputs, and compose into larger stacks. Keep modules focused on a single concern.
# Root module composing child modules
module "networking" {
source = "git::https://github.com/org/terraform-modules.git//networking?ref=v2.3.1"
environment = var.environment
vpc_cidr = var.vpc_cidr
private_subnets = var.private_subnets
public_subnets = var.public_subnets
}
module "database" {
source = "./modules/rds"
subnet_ids = module.networking.private_subnet_ids
security_group_ids = [module.networking.db_security_group_id]
instance_class = var.db_instance_class
engine_version = var.db_engine_version
}
output "db_endpoint" {
description = "RDS instance endpoint"
value = module.database.endpoint
sensitive = true
}See module-design-patterns for: module structure conventions, input validation, output design, versioning strategies, and registry publishing.
State Management
Store state remotely with locking. Use workspaces or directory-based separation for environments.
# S3 backend with DynamoDB locking
terraform {
backend "s3" {
bucket = "myorg-terraform-state"
key = "prod/networking/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
kms_key_id = "alias/terraform-state"
}
}See state-management for: remote backend configuration (S3, GCS), locking mechanisms, workspace strategies, state migration, and disaster recovery.
Variables and Locals
Use typed variables with validation blocks. Locals reduce repetition and compute derived values.
variable "instance_type" {
description = "EC2 instance type for the application tier"
type = string
default = "t3.medium"
validation {
condition = can(regex("^t3\\.", var.instance_type))
error_message = "Only t3 instance types are allowed in this environment."
}
}
locals {
name_prefix = "${var.project}-${var.environment}"
common_tags = {
Project = var.project
Environment = var.environment
ManagedBy = "terraform"
CostCenter = var.cost_center
}
}See variable-patterns for: complex type constraints, validation rules, locals patterns, conditional resource creation, and tfvars file organization.
Plan/Apply Workflow
Separate plan and apply stages. Save plan files for deterministic applies. Integrate with CI pipelines for automated review.
#!/usr/bin/env bash
set -euo pipefail
# Plan stage: generate and save binary plan
terraform init -backend-config="env/${ENVIRONMENT}.hcl"
terraform plan \
-var-file="env/${ENVIRONMENT}.tfvars" \
-out="tfplan-${ENVIRONMENT}-$(date +%s)" \
-detailed-exitcode
# Exit code 2 means changes detected
# Apply stage: use saved plan for deterministic apply
terraform apply "tfplan-${ENVIRONMENT}-${PLAN_TIMESTAMP}"See ci-integration for: GitHub Actions workflows, GitLab CI pipelines, plan artifact storage, auto-apply strategies, and Atlantis configuration.
Import and Migration Patterns
Import existing resources into Terraform management. Use import blocks (Terraform 1.5+) for declarative imports.
# Declarative import (Terraform 1.5+)
import {
to = aws_s3_bucket.legacy_data
id = "my-legacy-bucket-name"
}
resource "aws_s3_bucket" "legacy_data" {
bucket = "my-legacy-bucket-name"
tags = {
ManagedBy = "terraform"
Imported = "true"
}
}After import, run terraform plan to verify no diff. Adjust the resource configuration until the plan shows no changes. For bulk imports, generate configuration with terraform plan -generate-config-out=generated.tf.
Anti-Patterns
| Avoid | Use Instead |
count with maps or objects that may reorder | for_each with a stable map key |
Hardcoded provider credentials in .tf files | Environment variables or IAM roles |
| Monolithic root module with hundreds of resources | Smaller modules composed in a root module |
terraform apply -auto-approve in production | Saved plan file reviewed then applied |
| Storing state locally or in version control | Remote backend with encryption and locking |
Using terraform taint (deprecated) | terraform apply -replace=RESOURCE |
| Wildcard provider version constraints | Pinned versions with pessimistic constraint ~> |
| Nested provider blocks inside modules | Pass providers from root module via providers argument |
Running terraform destroy without targeted plan | terraform plan -destroy reviewed first |
| String interpolation for simple references | Direct reference var.name instead of "${var.name}" |
Performance
- Parallelism — Increase with
terraform apply -parallelism=20for large stacks. Default is 10. Monitor API rate limits. - Targeted applies — Use
-target=module.networkingduring development to reduce plan/apply time. Never rely on targets in production. - State splitting — Break monolithic state into smaller states per service or layer (networking, compute, data). Use
terraform_remote_statedata source for cross-stack references. - Provider caching — Configure
plugin_cache_dirin~/.terraformrcto avoid re-downloading providers across workspaces. - Plan file caching — In CI, cache
.terraformdirectory between runs. Useterraform providers lockto generate lock files for consistent installs. - Refresh skipping — Use
terraform plan -refresh=falsewhen you know infrastructure has not changed externally. Reduces API calls significantly on large stacks. - Data source minimization — Avoid unnecessary data source lookups in tight loops. Cache results in locals when the same data source is referenced multiple times.
source: Terraform documentation, HashiCorp best practices, AWS/GCP provider guides
In this repository38 other files it gives its agents.
- Data Engineering Cloud InfrastructureSkill
- data-eng-data-qualitySkill
- data-eng-streaming-patternsSkill
- data-eng-testing-patternsSkill
- data-eng-warehouse-patternsSkill
- DevOps CI/CD PatternsSkill
- devops-docker-patternsSkill
- devops-monitoring-observabilitySkill
- git-advanced-operationsSkill
- git-workflow-patternsSkill
- modern-python-patternsSkill
- python-api-developmentSkill
- python-async-patternsSkill
- python-data-pipelinesSkill
- python-data-wranglingSkill
- python-devops-automationSkill
- python-error-handlingSkill
- python-package-developmentSkill
- python-performanceSkill
- python-testingSkill
- customizing-vectors-rSkill
- designing-oop-rSkill
- developing-packages-rSkill
- metaprogramming-rlangSkill
- optimizing-rSkill
- writing-tidyverse-rSkill
- sql-advanced-featuresSkill
- sql-analytics-patternsSkill
- sql-data-modelingSkill
- SQL Integration PatternsSkill
- sql-query-fundamentalsSkill
- sql-query-optimizationSkill
- sql-transformationsSkill
- API DesignSkill
- webdev-html-css-patternsSkill
- webdev-javascript-fundamentalsSkill
- webdev-react-patternsSkill
- webdev-typescript-patternsSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

