TerraShelf

Standards

Contributing to TerraShelf

TerraShelf is an offline library of reusable AWS modules. A change is ready when scripts/check_all.sh passes with no AWS credentials in the environment.

Module layout

Each module lives in modules/<name>/ and contains main.tf, variables.tf, outputs.tf, versions.tf, README.md, CHANGELOG.md, examples/basic/, and tests/<name>.tftest.hcl. Some modules also have examples/complete/.

Do not put a provider block in a module. Pin Terraform in versions.tf:

terraform {
  required_version = ">= 1.7.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.70.0"
    }
  }
}

Commit .terraform.lock.hcl only under examples/.

Inputs, outputs, and tags

Every variable has type and description. Add validation when the value is a CIDR, an ARN, an enum, or a bounded number. Every output has description.

Accept tags as map(string) and merge it onto every taggable resource together with ManagedBy = "terraform" and Module = "<name>".

Do not hardcode a region, a 12-digit account id, an AMI id, or the arn:aws: prefix. Read the partition, account, and region from data sources, or take them as variables.

Defaults stay closed: encryption on, public access off, least privilege, and a finite log retention period. Name resources from name or name_prefix. Do not append an environment inside the module unless the module's contract is specifically about env, as rds-postgres is.

Tests

tests/<name>.tftest.hcl uses mock_provider "aws" and at least one override_data. Every run uses command = plan. Include at least five run blocks and at least one expect_failures block. Assert ManagedBy and Module on every taggable resource.

The archive provider used by lambda-function may run for real, because it only writes a local zip.

Semantic versioning

Tag a release as <module>/vX.Y.Z. The catalog version matches the newest CHANGELOG.md heading.

Record every release in CHANGELOG.md using Keep a Changelog.

Renames

When a resource address changes but the object should stay, add a moved block in the same release that changes the address:

moved {
  from = aws_s3_bucket.logs
  to   = aws_s3_bucket.access_logs
}

A rename without moved is a breaking change and needs a major version.

Documentation

Leave the <!-- BEGIN_TF_DOCS --> markers in README.md. terraform-docs fills them. The prose above the marker explains the contract in sentences. Do not claim that a module is published to the Terraform Registry. OWNER in the git source string is a placeholder.

What this repository does not do

Do not add a real remote backend, do not call the AWS API, and do not submit a change that requires credentials. GuardDuty and Security Hub in account-baseline stay off unless the caller sets the switch, because those services are billable. Example budget amounts are placeholders, not recommendations.

test_standards checks