Skip to content

Skills · infra-as-code

terraform-module

Generate a Terraform module from a described need, with main.tf, variables.tf, outputs.tf and versions.tf, typed and validated variables, secure defaults (encryption on, public access off, least-privilege IAM), a merged tags variable, a basic usage example and a README with inputs and outputs tables. Use when the user says "write a terraform module for", "create terraform for an S3 bucket / VPC / database", "turn this into a reusable module", or "scaffold infrastructure as code for".

Pack infra-as-codeTier standardJob Design the systemv0.1.1

npx skilldrop-cli install terraform-module
Other ways to install

With the sibling skills it hands off to:

npx skilldrop-cli install terraform-module --with-related

The whole pack it belongs to:

npx skilldrop-cli install --pack infra-as-code

By hand, from a clone of the repo:

cp -R packs/infra-as-code/skills/terraform-module ~/.claude/skills/

In Claude Code, with the infra-as-code plugin installed, invoke it as:

/infra-as-code:terraform-module

Try it

A realistic prompt from the skill's acceptance evals:

Write a Terraform module for Acme's Postgres database on AWS RDS. The payments team and the orders team will both call it. They need to pick instance class and storage size; everything else should be safe by default.

What a good result looks like

Example output

Worked example: an S3 bucket for access logs · the skill's own worked example, source

Input

"We need a Terraform module for a log bucket on AWS. Our S3 buckets send server access logs there, and we keep them a year. Several teams will call it."

All three inputs are present (need, cloud, callers), so no questions are asked.

Stated defaults and assumptions

  • Terraform >= 1.5.0; AWS provider >= 5.0, < 7.0.
  • SSE-S3 (AES256) by default, with SSE-KMS as an opt-in through kms_key_arn, because some AWS log delivery services do not support SSE-KMS on the destination bucket.
  • Retention 365 days, transition to STANDARD_IA at 30 days (the S3 minimum for that transition).
  • [assumption] Log sources are in the same account as the bucket. Cross-account delivery needs a wider policy and is left out.
Output

The module is in s3-log-bucket/:

FileWhat to look at
s3-log-bucket/versions.tfBounded provider constraint, Terraform floor
s3-log-bucket/variables.tfEvery input typed and described; validation on the bucket name, day counts, KMS ARN format and source bucket ARNs
s3-log-bucket/main.tfPublic access block, BucketOwnerEnforced, encryption, versioning, lifecycle, TLS-only policy, log delivery scoped by aws:SourceAccount from data.aws_caller_identity; a precondition checks retention is longer than the transition
s3-log-bucket/outputs.tfName, ARN and regional domain name, each described
s3-log-bucket/examples/basic/main.tfA caller with a provider block and placeholder names
s3-log-bucket/README.mdInputs and outputs tables matching the code

How it meets the quality bar:

  • Secure by default. A caller who sets only bucket_name gets a private, encrypted, versioned, TLS-only bucket that terraform destroy will not empty (force_destroy = false).
  • Least privilege. The only Allow statement is s3:PutObject for logging.s3.amazonaws.com, limited to this account and, when access_log_source_bucket_arns is set, to named source buckets.
  • Nothing hard-coded. The account ID and partition come from data sources; the region comes from the caller's provider.
  • One tags variable. merge(local.default_tags, var.tags) on the bucket. The bucket's sub-resources (public access block, policy, lifecycle and so on) do not take tags in the AWS provider.

Validation status

Run on 2026-10-01 with Terraform 1.16.4, on the module and on its examples/basic caller:

$ terraform fmt -recursive -check
$ terraform init -backend=false && terraform validate
Success! The configuration is valid.

fmt -check printed nothing and exited 0. validate passed with the AWS provider at 6.67.0 (the newest the >= 5.0, < 7.0 range allows), and again with the range pinned to 5.100.0 and to 5.0.0, so both ends of the stated range hold. validate checks syntax, types and references; it does not call AWS. Run terraform plan against a real account before relying on the module.

How it's checked

Measured against plain Claude Code (2026-10-23): it met 92% of its checks, against 77% without it, and a blind judge preferred it. How skills are checked

27 assertions across 4 acceptance evals, and 8 trigger queries. Every skill's checks

What the output must do
  • Produces versions.tf, variables.tf, main.tf, outputs.tf, a basic usage example and a README with inputs and outputs tables
  • versions.tf sets required_version and a provider version constraint with an upper bound on the major version
  • Every variable has a type and a description, and inputs such as instance class or storage size have validation blocks with fixable error messages
  • Defaults set storage_encrypted = true, publicly_accessible = false, deletion_protection = true and skip_final_snapshot = false
  • The master password is not a literal or a variable default; it is a sensitive variable, a managed password, or a secret-manager reference
  • A tags variable is merged with module defaults and applied to taggable resources
  • The module contains no provider block, no backend block, and no hard-coded account ID, region or ARN
  • Does not claim terraform validate or plan was run unless it was actually run
  • Uses labels with lowercase keys rather than AWS-style tags
  • Sets public_access_prevention to enforced and uniform_bucket_level_access to true by default
  • Grants access with a member-level IAM binding scoped to the named service account variable, not a project-wide or primitive role
  • Adds a lifecycle rule deleting objects after a retention variable defaulting to 90
  • Takes the project ID as a variable or data source rather than a literal
  • Ends with the terraform fmt, validate and plan commands for the user to run
  • Writes the module files under modules/archive-bucket/
  • The bucket is not publicly readable: public access is blocked and the website reads through the caller-supplied CloudFront distribution, and the reply explains how this meets both the website need and SEC-4
  • Flags that deleting noncurrent versions after 30 days conflicts with keeping replaced master versions for 7 years, and does not silently apply the 30-day deletion to masters
  • Flags that moving the JPEG derivatives the website shows to Glacier Deep Archive would stop them being served, rather than applying the 90-day transition to everything without comment
  • The version constraints accept AWS provider 4.x and Terraform 1.5 (nothing requires provider 5.x)
  • Encrypts with a customer-managed KMS key and applies the caller-supplied tags
  • Does not add requirements that are not in requirements.md (for example cross-region replication or access logging) unless marked as optional or as assumptions
  • Delivers a main.tf without stopping to ask questions first
  • The storage is private and encrypted by default, with public access blocked
  • Respects 'just the main file': gives one short file, or briefly explains if anything else is strictly needed
  • Notes that participants' answers are sensitive and suggests checking the university's data or ethics rules (for example on region or retention) rather than inventing them
  • Does not invent account IDs, retention periods, regions or other requirements as if they had been given
  • Includes a short plain-language explanation of how to use the file
When it should and shouldn't activate

Should activate:

  • Write a terraform module for a private S3 bucket with encryption
  • Create Terraform for an RDS Postgres instance that other teams can reuse
  • Turn these copy-pasted resource blocks into a reusable Terraform module with variables and outputs
  • Scaffold infrastructure as code for a GCS bucket with a README and inputs table

Should leave to another skill:

  • Review my terraform plan output before I apply it
  • Review this pull request that changes our Terraform files
  • Threat model the payments platform we are deploying to AWS
  • Estimate what this infrastructure will cost per month at 10k requests per second

Hands off to

Related skills

capacity-cost-model, migration-plan, pre-merge-review, threat-model

Source

SKILL.md · the whole folder on GitHub.