01

The App-Scoped Infrastructure Bottleneck

Application code ships in minutes through self-service GitOps; the cloud infrastructure that code depends on still crawls through a platform team's pull-request queue. This first day frames the two problems that motivate the whole course — a *velocity* problem (every app-scoped bucket, role, or identity is a platform round-trip) and a *security* problem (every service shares one cloud identity, so the blast radius is the whole fleet) — and the single idea that fixes both: let teams **claim** their own app-scoped cloud resources declaratively, and give each service its **own least-privilege cloud identity**.

The two-speed platform

On a mature Kubernetes platform, application delivery is already fast and self-service: a team merges a change, a GitOps engine reconciles it, and the workload rolls out without anyone filing a ticket. But the infrastructure that workload needs — a storage bucket, permission to read a queue, a cloud identity for a brand-new service — travels a slower, human-gated road. The team that knows what the service needs files a request; the platform team turns it into an infrastructure-as-code change only they can write and merge; the work waits until they reach it.

App code path:   write code → merge → GitOps reconciles → live      (minutes, self-service)
Infra path:      file ticket → platform writes IaC PR → review → apply   (days, gated)

The two lifecycles are out of step. This is not a tooling failure so much as an ownership mismatch: the people with the context are not the people with the credentials, so every app-scoped resource becomes a round-trip through a team that lacks the context and has its own backlog. Rule of thumb: when the thing blocking a deploy is a human in another team's queue rather than a check that must pass, you have a self-service gap, not a safety control — and the cost compounds with every new service.

The security half: one identity to rule them all

The velocity story has a quieter twin. Many platforms grow up with one shared cloud identity that every workload assumes — one GCP service account, or one AWS IAM role, wired to every pod. It is trivial to set up and disastrous under attack, because the shared identity necessarily holds the union of every service's permissions. Compromise any single workload and you inherit the access of all of them.

This is the second problem the course solves, and it turns out to have the same root cause: cloud identity is provisioned out-of-band from the app, so nobody mints a fresh identity per service — they just reuse the one that already exists. Rule of thumb: a shared workload identity makes the blast radius of any compromise equal to the most-privileged thing any workload can do; per-service identity shrinks each blast radius to one service's real needs.

Where this sits in a longer arc

Per-service identity usually arrives in two moves, and most platforms have only made the first. Move one is in-cluster identity separation: each workload gets its own Kubernetes ServiceAccount, often with a workload-identity binding (IRSA on AWS, Workload Identity on GCP) and a SPIFFE identity for mesh traffic. Move two is cloud-side identity separation: the cloud IAM principal behind that ServiceAccount also becomes per-service.

LayerCommon state todayWhat's still shared
Kubernetes ServiceAccountPer-service (delivered by an earlier identity project)
Mesh / SPIFFE identityPer-service
Cloud IAM identity (GCP SA / AWS role)One, shared by allThis is the gap

The binding mechanism from ServiceAccount to cloud identity already exists; it is simply pointed at one shared identity. Filling that binding with a distinct least-privilege identity per service is the concrete step that finishes the arc — and doing it through a self-service claim is what keeps it from re-creating the bottleneck. Rule of thumb: don't rebuild identity plumbing you already have; find the last shared link in the chain and make that per-service.

What "app-scoped" means (and what stays put)

The fix is emphatically not "let every team provision everything." It works only because cloud resources fall into two very different classes, and only one of them belongs in a self-service path.

ClassExamplesLifecycle & sharingProvisioned by
FoundationVPC, subnets, accounts/projects, DNS zones, shared keys, base IAMLong-lived, shared, part of the security perimeterTerraform (platform-owned, gated)
App-scopedA service's own bucket, its own IAM role/identity, its own cacheCreated & destroyed with one service, referenced only by itSelf-service claim (this course)

Terraform keeps the foundation; the self-service layer takes only the app-scoped tier. The two never own the same cloud object. That clean split is the precondition for everything that follows, and Day 03 turns it into an enforceable test. Rule of thumb: self-service is safe exactly where the resource's lifecycle matches one app's lifecycle; the moment a resource is shared or perimeter-level, it belongs back in the gated foundation.

The shape of the solution

The rest of the course builds one system that resolves both problems at once. An in-cluster controller watches for small, high-level claims committed in each service's own config; a platform-authored composition turns each claim into the correct provider-specific cloud resources — including a per-service least-privilege identity — with guardrails baked in; and the same GitOps flow that ships the app now ships its infrastructure.

Everything from here is detail on that picture: which controller to run and why (Day 02), where the Terraform boundary sits (Day 03), how to stand the controller up (Day 04), how to design the claim API (Days 05–06), how to make it safe in GitOps (Day 07), and how to roll it from a two-cluster pilot to production without a big-bang migration (Day 08). Rule of thumb: the win is not "Kubernetes provisions cloud resources" — plenty of tools do that; it is that a reviewed, allowlisted claim replaces both a ticket and a shared credential in one move.

Key takeaways

  • A modern platform is two-speed: app delivery is self-service and fast, app-scoped infrastructure is ticket-gated and slow; the gap is an ownership mismatch, not a tooling one.
  • The same out-of-band provisioning that causes the delay also leaves every service on one shared cloud identity, making the blast radius of any compromise the whole fleet.
  • Per-service identity is a two-move arc; most platforms have done the in-cluster move (per-service ServiceAccount) but not the cloud-side move — the shared cloud IAM identity is the last link.
  • Only app-scoped resources (single-app lifecycle) go self-service; foundation resources (shared, perimeter, long-lived) stay in gated Terraform, and the two never co-own an object.
  • The solution is a claim + composition model on a GitOps flow that fixes velocity and security together — a reviewed claim replaces both a ticket and a shared credential.

Checklist

  • [ ] I can describe the two-speed platform problem and say why it is an ownership mismatch rather than a tooling failure.
  • [ ] I can explain why a single shared cloud identity makes the blast radius of any compromise equal to the whole fleet.
  • [ ] I can name the two moves of the per-service identity arc and identify which one most platforms have skipped.
  • [ ] I can classify a resource as foundation vs app-scoped and say which provisioning path each takes.
  • [ ] I can sketch the claim → composition → cloud-resource flow and say what problem each part solves.