ADR-002: Terraform Registry Structure (Monorepo vs Multi-Repo)
Status: Accepted Date: 2026-02-25 Deciders: @cloud-architect, @product-owner, HITL/Manager
Context
The Terraform Registry (https://developer.hashicorp.com/terraform/registry/modules/publish)
requires each PUBLISHED module to be a separate GitHub repository with the naming convention
terraform-<PROVIDER>-<NAME>. This is a hard registry constraint.
However, the development organisation and the consumer-facing published artefacts are separate concerns. The question is: how do we organise the source code?
Options evaluated:
- One repository per module (pure multi-repo):
terraform-aws-iam-identity-center,terraform-aws-ecs-platform,terraform-aws-web - Monorepo with sub-modules published via Terraform Registry module paths
- Monorepo (
terraform-aws) with registry workaround via GitHub sub-directory tags
The Registry supports sub-directory module publishing since 2023:
- A single repo
terraform-awsCAN publish multiple modules IF each has its ownmodules/<name>/directory and the Registry module source is specified asgithub.com/nnthanh101/terraform-aws//modules/iam-identity-center?ref=vX.Y.Z(double-slash notation per https://developer.hashicorp.com/terraform/language/modules/sources#modules-in-package-sub-directories) - However, the PUBLIC Terraform Registry (
registry.terraform.io) requires one repo per published namespace/module. The sub-directory approach works for PRIVATE registries (Terraform Cloud, HCP Terraform) and direct GitHub sources, but NOT for the public Registry.
Conclusion from registry constraint analysis:
- Public Registry: ONE repo per module (mandatory)
- GitHub direct source / private registry: monorepo works
The manager's stated goal is "Terraform Registry-publishable" — this means public Registry.
Therefore the repository terraform-aws becomes the MONOREPO SOURCE, and each domain
publishes to a SEPARATE repository that is the thin Registry wrapper.
Decision
Adopt a Monorepo Source + Thin Wrapper Multi-Repo Publishing pattern:
SOURCE (private, development):
github.com/nnthanh101/terraform-aws/ ← monorepo
├── modules/
│ ├── iam-identity-center/ ← full source
│ ├── ecs-platform/ ← full source
│ └── web/ ← full source
├── examples/
├── tests/
└── docs/
PUBLISHED (public registry, thin wrappers):
github.com/nnthanh101/terraform-aws-iam-identity-center/ ← registry repo
github.com/nnthanh101/terraform-aws-ecs-platform/ ← registry repo
github.com/nnthanh101/terraform-aws-web/ ← registry repo
Each registry repo contains only:
main.tf(module call pointing to monorepo source at pinned tag)variables.tf(pass-through)outputs.tf(pass-through)versions.tfREADME.mdexamples/
Synchronisation mechanism:
GitHub Actions workflow in the monorepo: on tag push to modules/iam-identity-center/**,
trigger a workflow that copies updated files to the thin wrapper repo and creates a new tag.
Near-term pragmatic option (Phase 1): Publish directly from monorepo sub-directories using GitHub source until Registry publishing is set up. Consumer source:
module "iam_identity_center" {
source = "github.com/nnthanh101/terraform-aws//modules/iam-identity-center?ref=v1.1.0"
}
Phase 2: Migrate to public Registry thin wrappers. Registry module addresses will be:
oceansoft/iam-identity-center/awsoceansoft/ecs-platform/awsoceansoft/web/aws
Consequences
Positive
- Single source of truth for all module code (DRY principle)
- Shared test infrastructure, shared CI/CD
- Cross-module integration examples in one place (web depends on ecs-platform)
- Consistent versioning cadence across domains
Negative
- Synchronisation automation between monorepo and thin wrappers adds CI/CD complexity
- Consumers using Registry addresses must update when Phase 2 launches
- Module versions may diverge if sync automation fails
Blocking Risks
| Risk | Probability | Impact | Mitigation |
|---|---|---|---|
| Sync automation fails silently; Registry wrappers fall behind monorepo | M | H | GitHub Actions with mandatory status checks; release-please bot for automated changelog/tag management |
Alternatives Considered
- Pure multi-repo from day one: Rejected — cross-module examples (web) cannot easily reference sibling modules without published versions; development iteration is slow
- HCP Terraform Private Registry only: Rejected — public Registry visibility is a stated requirement for community and enterprise discoverability
- Terraform Stacks (HCP): Deferred to Phase 3 — requires HCP Terraform subscription; adds operational complexity before modules are stable
Related ADRs
- ADR-001: Module Naming Convention — Defines the kebab-case module directory names used in the monorepo structure above
- ADR-007: Upstream Dependency Strategy — Governs how upstream terraform-aws-modules versions are pinned and updated in the thin wrappers
References
- Terraform Registry Publishing: https://developer.hashicorp.com/terraform/registry/modules/publish
- Module Sources Sub-directory: https://developer.hashicorp.com/terraform/language/modules/sources
- GitHub Actions for Terraform: https://github.com/hashicorp/setup-terraform
Coordination Evidence
Consolidated from .adlc/projects/terraform-aws/ as part of ADR-001→019 SSOT consolidation (2026-02-27).