SA Pro Lab 05 — Enforce a team tag, then read the cost back
Scaffold: 2/5. The provisioner role and its allow policy are built. You write the guardrail from a requirement: a managed policy whose two Deny statements make the team tag unavoidable.
The scenario
An estate’s bill is one number, and finance wants it to be a number per team.
The mechanism is boring and well-known: every resource carries a team tag,
the tag is activated as a cost allocation tag, and Cost Explorer groups by it.
The hard part is not the mechanism, it’s the discipline: the day one team’s
pipeline creates resources untagged, the “unallocated” bucket starts growing
and never stops.
So the discipline gets automated. A guardrail refuses to create a resource unless the request carries the tag, and refuses to remove the tag afterwards. In this lab the guardrail is an IAM deny policy on a provisioner role, which works in any standalone account. In an AWS Organization the same policy document, attached to an OU as a Service Control Policy, enforces the rule across every account and every principal at once — that’s the promotion path, not a different design.
The requirement
Assuming the provisioner role: creating an SQS queue without a team tag is
refused; creating one tagged team=checkout succeeds; removing the team tag
from that queue is refused.
What’s provided
src/template.yaml— the provisioner role, its allow policy (create, tag, inspect, and delete queues within the lab’s name prefix), and aTODOblock where the guardrail goes.solution/template.yaml— the reference answer.scripts/— deploy, test, and teardown. The test assumes the role and pushes on the guardrail from the inside.
Your task
Add an AWS::IAM::ManagedPolicy attached to the role, with two Deny
statements:
- Deny
sqs:CreateQueuewhen the request carries noteamtag. “No team tag in the request” is theNullcondition operator onaws:RequestTag/team(quote'Null'in YAML, or it parses as an actual null). - Deny
sqs:UntagQueuewhen the keys being removed includeteam(ForAnyValue:StringEqualsonaws:TagKeys). Without this the first statement is theatre: create tagged, untag a second later.
An explicit Deny always beats the role’s Allow — the same evaluation logic that makes SCPs work.
Run it
# Defaults: stack sa-pro-lab-05, region ap-southeast-2.
./scripts/deploy.sh # deploys src/template.yaml
./scripts/test.sh # assumes the role and pushes on the guardrail
./scripts/teardown.sh # deletes everything
Nothing in this lab bills meaningfully (a role, a policy, and an empty queue that lives for seconds), but teardown keeps the account clean and the deploy tags the stack for the lab reaper as a backstop.
What success looks like
./scripts/test.sh prints:
1. Creating sa-pro-lab-05-t12345 with NO tags (this should be refused) ...
refused. No team tag, no queue.
2. Creating sa-pro-lab-05-t12345 tagged team=checkout (this should succeed) ...
created: https://sqs.../sa-pro-lab-05-t12345
3. Removing the team tag (this should be refused) ...
refused. The tag stays.
PASS: untagged create refused, tagged create allowed, tag removal refused.
Reveal the solution
SRC=solution ./scripts/deploy.sh && ./scripts/test.sh
The cost half
The guardrail proves in seconds; the payoff lands on the bill, on billing’s
timetable. In the payer account, activate team as a cost allocation tag
(Billing console → Cost allocation tags). From the next day’s data onward, Cost
Explorer and the Cost and Usage Report can group spend by team, and the
guardrail you built is what makes that grouping trustworthy: nothing exists
that isn’t attributed.
What you just learned
- Cost allocation is a tagging problem, and tagging is an enforcement problem. Reports are only as good as the worst-tagged resource in them.
aws:RequestTagconditions evaluate at create time, which is the only moment you can refuse a resource cheaply. Finding untagged resources later means detective controls and cleanup toil.- Guardrails need both halves: require the tag on create, and protect it from removal. One without the other doesn’t survive contact with a deadline.
- The IAM deny on a role and an SCP on an OU are the same policy document with different blast radius. Prove the rule small, then promote it.
Next
The rest of the SA Pro lab track is listed in labs/README-sa-pro.md.