Patterns and best practices for AWS infrastructure as code with Terraform. Use when the user asks about Terraform module structure, naming conventions, state management, IAM policies (least privilege, OIDC), CI/CD pipelines for infrastructure (GitHub Actions, OIDC authentication), security scanning (Checkov, CKV_AWS checks), secrets management, KMS key policies, confused deputy prevention, Lambda function URL auth, API Gateway WAF/logging, or general IaC architecture decisions. Triggers on: Terraform, OpenTofu, IaC, modules, tfstate, remote state, OIDC, IAM, least privilege, GitHub Actions, CI/CD, infrastructure pipeline, AWS provider, Checkov, static analysis, IaC scanning, confused deputy, source ARN, KMS, CMK, secrets in state, ephemeral resources, Lambda function URL, API Gateway WAF.
100
Quality
100%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advisory
Suggest reviewing before use
| Topic | Reference File | Key Insight |
|---|---|---|
| Module structure | references/terraform-structure.md | Don't wrap single resources in modules |
| Naming conventions | references/terraform-structure.md | Consistent name_prefix locals pattern |
| State management | references/terraform-structure.md | S3 native locking (Terraform 1.10+) — DynamoDB lock is deprecated |
| State backend bootstrap | references/terraform-structure.md | Separate bootstrap project with local state, SSE-KMS, CI/CD access policy |
| IAM & security | references/security-iam.md | LeadingKeys for multi-tenant, policy composition, confused deputy, KMS key policy |
| Checkov (DynamoDB, Lambda, API GW) | references/security-iam.md | CKV_AWS_28/119 (DynamoDB), CKV_AWS_258 (Lambda URL), CKV_AWS_76/CKV2_AWS_29 (API GW) |
| Secrets in Terraform state | references/security-iam.md | Use ephemeral resource (Terraform 1.10+); protect state with SSE-KMS S3 backend |
| CI/CD OIDC federation | references/security-iam.md + references/cicd-patterns.md | aws_iam_openid_connect_provider + sts:AssumeRoleWithWebIdentity — no static keys |
| CI/CD pipelines | references/cicd-patterns.md | Use OIDC — never store AWS keys as secrets |
| Multi-env pipelines | references/cicd-patterns.md | Per-branch backend keys, env resolver, ephemeral cleanup |
| Pipeline safety | references/cicd-patterns.md | Concurrency per env, environment protection gates, drift detection |
Resource: "*" in IAM policies — scope to specific ARNs with conditionsAdministratorAccess to Lambda execution rolessource_arn on aws_lambda_permission when principal is a service — leaves the function open to confused deputy attacks across accounts* — pre-create aws_cloudwatch_log_group in Terraform and scope to that log group ARN; eliminates logs:CreateLogGroup on wildcardsqs:* on * for Lambda SQS consumers — only three actions are required: sqs:ReceiveMessage, sqs:DeleteMessage, sqs:GetQueueAttributes, scoped to the specific queue ARNserver_side_encryption { enabled = true } alone for DynamoDB — no kms_key_arn uses AWS-owned key and fails Checkov CKV_AWS_119kms:* on the account principal delegates access to any IAM role with kms:*; use explicit per-role statementsauthorization_type = "NONE" on aws_lambda_function_url — publicly invocable without auth; fires Checkov CKV_AWS_258data "aws_secretsmanager_secret_version" in Terraform ≥ 1.10 — use ephemeral to avoid writing the secret value to stateterraform apply directly on push to main — require plan review in PRcancel-in-progress: true on apply/destroy jobs — cancelling mid-apply corrupts statedefault_tags with Branch tag — orphaned resources become untrackableenvironment: ${{ needs.x.outputs.y }} — must use object format environment: name:environment: name: without pre-creating the environment in repo settings — non-existent names silently create unprotected environments with no reviewers| Create a Module | Use Resource Directly |
|---|---|
| 3+ resources always deployed together | Single resource with no dependencies |
| Reused across 2+ environments or repos | One-off resource |
| Encapsulates non-obvious wiring (IAM + resource) | Simple resource with standard config |
When the user asks about cost impact, pricing, or cost optimisation for Terraform-managed infrastructure, direct them to install the AWS Pricing MCP Server. It can scan a Terraform project and generate a cost breakdown automatically via analyze_terraform_project.
Prerequisites: uv package manager, Python 3.10+, AWS credentials with pricing:* permissions.
macOS / Linux:
{
"mcpServers": {
"awslabs.aws-pricing-mcp-server": {
"command": "uvx",
"args": ["awslabs.aws-pricing-mcp-server@latest"],
"env": {
"FASTMCP_LOG_LEVEL": "ERROR",
"AWS_PROFILE": "your-aws-profile",
"AWS_REGION": "us-east-1"
}
}
}
}Windows:
{
"mcpServers": {
"awslabs.aws-pricing-mcp-server": {
"command": "uvx",
"args": [
"--from", "awslabs.aws-pricing-mcp-server@latest",
"awslabs.aws-pricing-mcp-server.exe"
],
"env": {
"FASTMCP_LOG_LEVEL": "ERROR",
"AWS_PROFILE": "your-aws-profile",
"AWS_REGION": "us-east-1"
}
}
}
}Add the above to ~/.claude/claude_desktop_config.json (Claude Desktop) or .claude/mcp.json (Claude Code) under mcpServers.
Load only the reference file that matches the user's question. Never bulk-load all three.
| User is asking about | Load |
|---|---|
| Module structure, naming, state, backend buckets | references/terraform-structure.md (184 lines) |
| IAM policies, least privilege, role design, SCPs, Checkov checks, KMS, secrets in state, OIDC | references/security-iam.md (327 lines) |
| CI/CD, GitHub Actions, OIDC, pipelines, ephemeral envs, drift | references/cicd-patterns.md (642 lines) |
For infrastructure reviews: load terraform-structure.md + security-iam.md (511 lines). Only add cicd-patterns.md if the review scope includes pipeline config.
f296935
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.