Skip to main content

ADR-023: Derived Module Pattern (Clone + Value-Add)

  • Status: Accepted
  • Date: 2026-03-08
  • Deciders: HITL/Manager, Product Owner, Cloud Architect
  • Supersedes: CLAUDE.md Architecture note ("Wrapper Pattern" — corrected to "Derived Module Pattern")

Context​

As the terraform-aws monorepo grew from 2 modules (sso, ecs) to 8 modules (sso, ecs, web, alb, acm, cloudfront, s3, vpc), a consistent pattern for sourcing upstream infrastructure code became necessary. Three strategies were considered:

StrategyDescriptionRisk
Thin wrappermodule { source = "terraform-aws-modules/vpc/aws" } — consume upstream directly at call timeRuntime dependency on public registry; upstream breaking changes affect production immediately; no ability to enforce oceansoft security defaults at source
Derived (clone + value-add)Clone upstream source into modules/<name>/, apply oceansoft value-adds, publish independentlyManual upstream tracking burden; fully independent publication; security defaults enforced at source
Custom from scratchWrite all resources directly without upstream baselineFull control; highest maintenance burden; reinvents well-tested upstream code

All active modules (alb, acm, cloudfront, s3, vpc) were cloned from terraform-aws-modules/<name> under Apache-2.0. The sso module was cloned from aws-ia/terraform-aws-sso (ADR-007). The ecs module was cloned from terraform-aws-modules/ecs. The web module is an exception — it is a composition layer (ADR-024) with no direct upstream equivalent.

The thin-wrapper pattern was initially documented in CLAUDE.md but was not the implementation reality. This ADR formalizes the pattern as "derived module" to match the actual codebase.


Decision​

All modules in this monorepo use the derived module pattern:

  1. Clone the upstream terraform-aws-modules/<name> source into modules/<name>/
  2. Apply oceansoft value-adds (enumerated below)
  3. Maintain independent semver per module (ADR-025)
  4. Publish to app.terraform.io/app/oceansoft/ TFC private registry

Oceansoft Value-Adds Applied to Every Derived Module​

Value-AddLocationPurpose
Copyright headerAll .tf filesApache-2.0 attribution: platform@oceansoft.io
NOTICE.txtModule rootCredits upstream source per Apache-2.0 Section 4(d)
Provider constraintsversions.tfaws >= 6.28, < 7.0; terraform >= 1.11.0 (ADR-003)
provider_meta "aws"versions.tfuser_agent for TFC registry tracking
Security defaultsModule variables/resourcesTLS 1.3 minimum, HTTPS-only listeners, deletion protection enabled
APRA CPS 234 complianceVariables, examplesdata_classification tag, audit trail, Compliance = "APRA-CPS234"
FOCUS 1.2+ tagsExample versions.tf / default_tagsx_cost_center, x_environment, x_project, x_service_name
Tier 1 snapshot teststests/*.tftest.hclFree 2-3s validation, no AWS credentials (ADR-004)
example namingexamples/ subdirsmvp-, poc-, production- prefix (ADR-005)

What Is NOT Changed​

  • Core resource logic from upstream (subnet calculations, route table associations, etc.)
  • provider_meta "aws" { user_agent } — updated to reflect oceansoft lineage, not deleted
  • Upstream variable names and types — preserved for operator familiarity

Consequences​

Positive​

  1. No runtime dependency on public Terraform Registry — production deployments are air-gap safe
  2. Security defaults (TLS 1.3, deletion protection) are enforced at source, not at call site
  3. APRA CPS 234 and FOCUS 1.2+ compliance is built into the module, not left to consumers
  4. Each module is independently publishable with a credible output surface (full ARNs, IDs)
  5. Upstream module APIs are well-tested by the community before we adopt them

Negative​

  1. Upstream security patches must be tracked manually — quarterly diff review against upstream tags
  2. Merge conflict surface when upstream makes structural changes (variable renames, resource restructures)
  3. Each cloned module inherits upstream's line count (vpc: 4,059 lines) — not suitable for rapid internal modules

Risks​

IDRiskProbabilityImpactMitigation
RISK-023-001Upstream CVE fix not backported within SLAMEDIUMHIGHQuarterly upstream diff review; task security:trivy in CI gates each release
RISK-023-002Provider version conflict between cloned modulesLOWMEDIUMAll modules share ADR-003 constraint (>= 6.28, < 7.0); single lockfile per project
RISK-023-003Upstream module deprecation (e.g., terraform-aws-modules/vpc v6)LOWMEDIUMPinned provider_meta user_agent enables usage tracking; ADR review on major upstream bump

Upstream Tracking Schedule​

FrequencyAction
Quarterlygit diff upstream/<module>/vX.Y.Z against the version we cloned from
On CVEImmediate patch backport via fix: commit → release-please PATCH bump
On major upstream releaseCloud Architect assessment → new ADR if structural change required

Alternatives Considered​

  1. Thin wrapper (module { source = "..." }): Rejected — runtime public registry dependency; cannot enforce security defaults at source; upstream breaking change propagates immediately to all consumers without a review gate.

  2. Custom from scratch: Rejected for foundational modules (vpc, alb, acm, etc.) — reinvents 3,000-5,000 lines of community-tested code. Acceptable for composition layers with no upstream equivalent (see ADR-024 for the web module).

  3. Git submodule pointing to upstream: Rejected — submodule checkout pinned to upstream commit does not permit value-add modifications; publishing a submodule reference to TFC is unsupported.


Applicability​

ModuleUpstream SourceDerived?
ssoaws-ia/terraform-aws-ssoYes (ADR-007)
ecsterraform-aws-modules/ecsYes
albterraform-aws-modules/albYes
acmterraform-aws-modules/acmYes
cloudfrontterraform-aws-modules/cloudfrontYes
s3terraform-aws-modules/s3Yes
vpcterraform-aws-modules/vpcYes
webNone (composition layer)No — see ADR-024

  • ADR-001: Module naming (kebab-case)
  • ADR-002: Registry structure
  • ADR-003: Provider constraints
  • ADR-007: IAM Identity Center upstream strategy (first instance of this pattern)
  • ADR-024: Composition layer (web module exception)
  • ADR-025: Per-component semver

Coordination Evidence​

  • Product Owner log: tmp/terraform-aws/coordination-logs/product-owner-2026-03-08.json
  • Cloud Architect log: tmp/terraform-aws/coordination-logs/cloud-architect-2026-03-08.json
  • Architecture decisions: tmp/terraform-aws/architecture-decisions/ADR-023-derived-module-pattern-2026-03-08.md