Cheat notes · Module 09 · Safe change
Capstone: onboard team-payments on your own
A five-minute review for when you have done this module and want it back in your head. Open the full module for the steps and the lab, or the full text to print.
The idea
Onboarding a team that needs something new means extending the module with an optional input whose default keeps today's behaviour, proving it with a test and an empty plan for every existing team, then adding the calls dev first. Retiring a team is forgetting in Terraform, then deleting by hand.
Remember
extra_viewers = map(string), default{}: existing callers change nothing.for_each = local.viewerswith static keys (team,audit); the team's own binding keeps the nameteam-view.moved { from = kubernetes_role_binding_v1.view to = kubernetes_role_binding_v1.view["team"] }in the module, so every caller gets it.- Order: module and test, empty plans, the calls, the import, then prod, in one pull request.
removed { from = module.reporting lifecycle { destroy = false } }: out of state, still running; thenkubectl delete namespaceas a deliberate step.
Pitfalls
- Adding
for_eachwithoutmoved: every team's binding is destroyed and recreated, prod too. - Deleting a team's module block to retire it:
prevent_destroyleaves with the block, and the plan deletes the namespace with no error. Useremovedwithdestroy = false. - Removing
prevent_destroyfrom the module to retire one team: every team loses the guard. - Making the new input required: every caller must change, and the change is no longer additive.
Commands
terraform test | Five runs, all pass |
kubectl auth can-i list pods -n payments-prod --as=x --as-group=team-finance-audit | yes; for checkout-prod, no |
terraform state list | No module.reporting left |
modules/team-namespace/rbac.tf (the view binding)
resource "kubernetes_role_binding_v1" "view" {
for_each = local.viewers
metadata {
name = each.key == "team" ? "team-view" : "view-${each.key}"
namespace = kubernetes_namespace_v1.this.metadata[0].name
}
role_ref {
api_group = "rbac.authorization.k8s.io"
kind = "ClusterRole"
name = "view"
}
subject {
api_group = "rbac.authorization.k8s.io"
kind = "Group"
name = each.value
}
}
Test yourself
Answer in your head first, then open the answer.
What is the bar for a module change?
An empty plan (or only has moved to) for every existing caller.
Why are the for_each keys team and audit, not the group names?
Keys are part of the address; names you write stay stable and known at plan time.
What does removed with destroy = false do on apply?
Takes the objects out of state and leaves them running.
Where module 09 leaves you: payments-dev and payments-prod have the full kit plus a second read-only binding for team-finance-audit. No other team's objects were recreated. The module has an extra_viewers input, a moved block and five tests. docs/onboarding.md says how to do it again. team-reporting is out of Terraform's state and its namespace is gone.