Skip to main content

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:

  1. One repository per module (pure multi-repo): terraform-aws-iam-identity-center, terraform-aws-ecs-platform, terraform-aws-web
  2. Monorepo with sub-modules published via Terraform Registry module paths
  3. Monorepo (terraform-aws) with registry workaround via GitHub sub-directory tags

The Registry supports sub-directory module publishing since 2023:

  • A single repo terraform-aws CAN publish multiple modules IF each has its own modules/<name>/ directory and the Registry module source is specified as github.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.tf
  • README.md
  • examples/

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/aws
  • oceansoft/ecs-platform/aws
  • oceansoft/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

RiskProbabilityImpactMitigation
Sync automation fails silently; Registry wrappers fall behind monorepoMHGitHub Actions with mandatory status checks; release-please bot for automated changelog/tag management

Alternatives Considered

  1. Pure multi-repo from day one: Rejected — cross-module examples (web) cannot easily reference sibling modules without published versions; development iteration is slow
  2. HCP Terraform Private Registry only: Rejected — public Registry visibility is a stated requirement for community and enterprise discoverability
  3. Terraform Stacks (HCP): Deferred to Phase 3 — requires HCP Terraform subscription; adds operational complexity before modules are stable

References

Coordination Evidence

Consolidated from .adlc/projects/terraform-aws/ as part of ADR-001→019 SSOT consolidation (2026-02-27).