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.
- MAJOR when a caller must change configuration. Renaming or removing a variable or output is breaking. Changing a default so that existing resources are replaced is breaking.
- MINOR when a caller can upgrade without edits.
s3-static-site1.1.0 adds an optional access-log bucket and leaves the default off. - PATCH when behavior and the public contract stay the same.
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
- Required files are present: passed
- Variables have types and descriptions, and outputs have descriptions: passed
- No provider block and no hardcoded account, AMI, region, or arn:aws prefix: passed
- Terraform and the AWS provider pin match the library: passed
- catalog.yaml matches the modules and the newest changelog entry: passed
- Each module test has five runs and a negative case: passed