Terraform Module Output Contracts Prompt
Design stable, well-typed module outputs that downstream modules can depend on — output naming, sensitive marking, object vs flat outputs, and versioning the contract so consumers don't break on every refactor.
- Target user
- Module authors building reusable internal Terraform modules
- Difficulty
- Intermediate
- Tools
- Claude, ChatGPT
The prompt
You are a Terraform module author who treats a module's outputs as a public API contract: once a downstream root module wires `module.vpc.private_subnet_ids` into a security group, you can't rename it without breaking every consumer. I will provide: - The module's purpose and current `outputs.tf` - Known consumers (other modules / root configs) - Whether the module is published internally or to a registry - Pain points (consumers reaching into internals, churny outputs, missing values) Your job: 1. **Outputs are an API** — frame every output as a promise. Identify which current outputs are stable contracts vs accidental leaks of internal resource attributes that should never have been exposed. 2. **Naming and shape** — recommend a consistent scheme: plural collection names (`subnet_ids`), singular for scalars (`vpc_id`), `_arn`/`_id`/`_name` suffixes. Decide between many flat outputs vs a single typed object output, and give the tradeoff: objects are easier to pass whole but harder to evolve field-by-field. 3. **Type the outputs** — add explicit object/list types where it clarifies the contract, and explain how typing catches consumer mistakes at plan time instead of apply time. 4. **Sensitive handling** — mark outputs `sensitive = true` where they carry secrets, and explain how sensitivity propagates to consumers (and the `nonsensitive()` escape hatch's risks). 5. **Don't leak internals** — flag outputs that expose implementation details (a specific resource's raw attribute) that lock you into that implementation. Propose a stable abstraction instead so you can refactor internals freely. 6. **Avoiding consumer reach-around** — note that consumers can't access un-exported resources, so a missing output forces forks or duplication. Identify values consumers genuinely need but can't currently get. 7. **Versioning the contract** — define what counts as a breaking change (removing/renaming an output, changing its type, adding sensitivity) vs additive (new output). Map these to semver and recommend a deprecation path (keep old output aliasing new one for one minor). 8. **Documentation** — generate the output table (name, type, sensitive, description) suitable for terraform-docs. Output: (a) refactored `outputs.tf` with types and descriptions, (b) a deprecation/aliasing plan for any renames, (c) a semver impact note per change, (d) the terraform-docs output table. Bias toward: stable contracts, explicit types, and never exposing an internal you'd regret being unable to change.