Exam Room · Advanced DevOps Engineer

Structuring Templates for Twelve Teams

May 22, 2027 · 24 min read

DevOps Engineering · part of The Exam Room

The situation

A retail platform has twelve product teams, each owning one or more AWS accounts, and a platform team of four. Every team deploys with CloudFormation from their own pipeline.

Each team has written its own networking template, its own logging configuration, and its own baseline of IAM roles. They started from a copy of a template the platform team wrote in 2024 and each has diverged. Two teams have subnets in two Availability Zones rather than three. Four have flow logs disabled because an early cost review flagged them. One has a NAT gateway per zone and the rest share one. Nobody can answer “what does a compliant account look like” without reading twelve repositories.

The platform team’s proposal is to own the shared pieces centrally. The product teams’ objection is that the platform team is four people and they do not want to raise a ticket to add a subnet. Both positions are reasonable and the argument has been running for a quarter.

What actually matters

The first thing that matters is that this is a coupling decision rather than a templating one. Every mechanism on the table is a different answer to “when the platform team fixes something, how does that reach twelve accounts, and what can a team change without asking”. Picking a mechanism without answering that produces the same divergence in a new format.

The second is that not all the shared pieces want the same treatment. A logging configuration that must be identical everywhere and that teams have no legitimate reason to vary is a different problem from a VPC where the address range and zone count genuinely differ by workload. Applying one mechanism to both produces either a bottleneck or a fiction.

The third is the difference between distributing a definition and enforcing an outcome. A shared template gives teams the right shape; it does not stop a team changing it afterwards. A guardrail that detects or prevents non-compliance does not give teams anything to build from. Most estates need both, and conflating them is why “we standardised the template” and “accounts are compliant” keep turning out to be different statements.

The fourth is that the platform team is four people, which is a design input rather than a complaint. A mechanism requiring the platform team to be in the path of every change will fail, not because it is wrong in principle but because four people cannot serve twelve teams synchronously.

Underneath it, the divergence happened because copying was the only distribution mechanism available. Whatever replaces it has to make the shared version easier to use than a copy.

What we’ll filter on

  1. Who can change the shared definition, and who can change an instance of it?
  2. How does a fix reach twelve accounts, and how long does it take?
  3. Can a team vary the parts that legitimately vary, without asking?
  4. Does it distribute a definition, enforce an outcome, or both?
  5. Is the platform team in the path of a routine change?
  6. What happens when a team needs something the shared version does not support?

The landscape

Nested stacks. A parent template references child templates stored in S3. The child is versioned by its S3 location, so a team pins a version by referencing it and upgrades by changing the reference. Updates to the parent cascade to children. It couples the pieces into one stack operation, which means one failure rolls back the lot, and the composition is visible in the account.

CloudFormation modules. Registered in the CloudFormation registry per account and region, a module packages a fragment of a template as a reusable resource type. Teams reference the module type in their templates and CloudFormation expands it at deployment. Because the module is registered centrally and versioned, the platform team publishes a new version and teams adopt it by changing a version reference. The expansion is visible in the processed template, which keeps debugging possible.

StackSets. The platform team deploys a stack directly into many accounts from one operation, with service-managed permissions and automatic deployment to accounts joining a target organisational unit. Teams do not deploy it and cannot easily change it, which is both what makes it useful and what teams object to. Right for things that must be identical and that teams should not vary.

Service Catalog. The platform team publishes products with constraints, and teams launch them into their own accounts with a limited set of parameters. It gives teams self-service within a boundary the platform team defines, without the platform team being in the path of a launch, and it adds an approval and versioning story on top.

CDK constructs. A shared library, versioned and published to an internal package repository, that teams depend on like any other package. Teams upgrade on their own schedule by bumping a version, the platform team ships fixes as releases, and the abstraction can expose exactly the parameters that should vary. It requires teams to work in CDK and it inherits every dependency-management problem a package ecosystem has.

AWS Config conformance packs. Enforcement rather than distribution. Rules evaluate every account continuously against a baseline, with remediation attached. This is what answers “are accounts compliant” independently of how their resources were created.

Service control policies. Prevention. An SCP denying the creation of a VPC without flow logs is not expressible directly, but denying specific actions, or denying resource creation outside a region, is. SCPs handle the coarse absolutes.

CloudFormation hooks. Evaluate a template before a stack operation proceeds and block it if it violates a rule. This is preventive at deployment time rather than detective afterwards, and it applies regardless of which team wrote the template.

Evaluation

Side by side

Mechanism Teams self-serve Platform in the path Fix propagation Team can vary Enforces
Copy a template Never ✓ anything
Nested stacks On reference change Parameters
CloudFormation modules Publishing only On version bump Parameters
StackSets ✓ for changes Automatic Partly
Service Catalog ✓ within limits Publishing only On version Allowed parameters Partly
CDK constructs Publishing only On version bump Whatever is exposed
Conformance packs n/a Immediate n/a ✓ detects
SCPs and hooks n/a Immediate n/a ✓ prevents

The table splits into two halves that answer different questions, and the argument has been running for a quarter because both sides were arguing about the top half while the disagreement was really about the bottom. Distribution mechanisms give teams a good starting point; only the bottom three make a statement about what accounts actually look like.

The solution

Split the shared pieces by whether teams have a legitimate reason to vary them, distribute with modules for the ones they do and StackSets for the ones they don’t, and put enforcement underneath both.

Sort the pieces first, because that is the decision everything else follows from. Logging configuration, the organisation trail, the security baseline roles and the Config recorder are things teams have no legitimate reason to vary; they go to StackSets with service-managed permissions and automatic deployment, so an account joining the organisational unit gets them without anyone acting. Networking, where address ranges and zone counts genuinely differ, goes to a distribution mechanism teams drive themselves.

For that half, publish CloudFormation modules from the platform team’s pipeline into every account and region. A team writes a template referencing the networking module with their own parameters, deploys it from their own pipeline, and never raises a ticket. The platform team publishes a new module version when something needs fixing, and teams adopt on a version bump, which means the platform team is in the publishing path and not in the deployment path. That is the resolution to the bottleneck objection, and it is what makes the shared version easier to use than a copy.

Where teams need something the module does not support, the answer is a pull request to the module rather than a copy of it. Making that route work, with the platform team reviewing rather than writing, is what stops the divergence recurring. If the platform team is a bottleneck on reviews instead of on deployments, the arrangement has failed differently.

Then put the enforcement underneath, because distribution alone does not make a statement about compliance. A conformance pack across the organisation covering the rules that matter, flow logs enabled among them, evaluates every account continuously regardless of how its resources were created. A team that stops using the module, or edits around it, shows up in the compliance report rather than in an incident.

Add CloudFormation hooks for the absolutes that should never deploy at all, so a template creating a resource the organisation forbids is blocked before it becomes a resource to remediate. Keep this set small; a hook is a deployment-time failure for every team, and a long list of them turns into a support burden.

Then fix the twelve existing accounts, which is its own project. Bring each onto the module version by version rather than all at once, and use the compliance report to sequence: the four accounts with flow logs disabled first, because that is a real gap rather than a stylistic one.

Why not StackSets for everything. It is the platform team’s instinct, and it puts them in the path of every networking change for twelve teams, which is exactly the objection the product teams raised and which four people cannot serve.

Why not just conformance packs and let teams write what they like. It measures compliance and gives nobody anything to build from, so every team keeps writing their own VPC and the report tells you which ones got it wrong.

Worked example

The sorting exercise takes a workshop and produces less argument than expected once the question is put as “is there a legitimate reason for this to differ between teams”. Logging, the trail, the Config recorder, the baseline roles and the guardrail SCPs go to the enforced half. Networking, compute baselines and the deployment roles go to the self-service half.

The StackSet half deploys in a week and closes four findings immediately, because the teams with flow logs disabled now have them enabled by something they cannot turn off. One team objects on cost, and the conversation ends with a per-team cost report showing flow logs at a fraction of their NAT gateway spend.

The networking module takes six weeks, most of it spent deciding what to expose as parameters. The first version exposes too little and two teams cannot express their layout; the second exposes too much and the module stops being a standard. The third settles at CIDR, zone count, and a flag for whether NAT is per-zone or shared.

Adoption is voluntary and takes four months. Nine teams move within six weeks because the module is less work than maintaining their own. Two move after the compliance report starts being read in a monthly forum. The twelfth has a genuinely unusual layout, submits a pull request to the module adding a parameter, and it is merged. That pull request is the moment the arrangement proves itself: the divergence that would previously have been a copy became a contribution.

The hooks list ends at three rules, all of them things that would be an incident rather than a style disagreement.

What’s worth remembering

  1. This is a coupling decision, not a templating one: every mechanism is a different answer to who can change what and how a central fix reaches many accounts.
  2. Sort the shared pieces by whether teams have a legitimate reason to vary them. Things that must be identical go to StackSets; things that genuinely differ go to a mechanism teams drive themselves.
  3. Distribution and enforcement are separate: a shared template gives teams the right shape and does not stop them changing it, and a conformance pack measures compliance without giving anyone anything to build from. Most estates need both.
  4. CloudFormation modules and Service Catalog keep the platform team in the publishing path and out of the deployment path, which is what makes a small platform team viable against many product teams.
  5. StackSets with service-managed permissions deploy automatically to accounts joining a target OU, so a new account arrives compliant rather than needing onboarding.
  6. Make contributing to the shared definition easier than copying it, or the divergence recurs in whatever format you chose.

These posts are LLM-aided. Backbone, original writing, and structure by Craig. Research and editing by Craig + LLM. Proof-reading by Craig.