Ruslan Akchurin · Sydney
AboutWriting

Define tier membership

How tiers get populated.

The membership trap starts when workload boundaries collapse into one shared project.

I use GCP vocabulary in this part: projects, folders, service accounts, and IAM bindings. The same structure maps to AWS accounts and roles, other clouds, and self-managed platforms with their own isolation and deployment surfaces.

Teams reach for a shared project when cross-project IAM feels harder than putting services together.

That shape gets expensive after the second workload. Those objects change with the workload, but they are written into the same shared project. The environment tier starts carrying one binding per service and one temporary override per deployment. It stops being a landing zone and becomes the slowest workload catalogue in the system.

Shared project absorbs workload internals Environment tier carries workload-api service account, workload-api IAM binding, workload-worker service account, workload-worker IAM binding, api.example.com record, and worker.example.com record. Each workload tier holds only the service. The environment card is drawn with the accent border because it is the misplacement. SHARED PROJECT environment misplaced workload-api service account workload-api IAM binding workload-worker service account workload-worker IAM binding api.example.com record worker.example.com record workload-api service workload-worker service
Shared project absorbs workload internals

Everything looks close to the shared project, so environment absorbs it. The cost is : an application release needs environment review, and deleting one workload becomes a shared-project migration.

Workload boundary published as a project Organisation owns the workload folder, domain ownership, and DNS zone delegation. Environment owns the workload project, CI/CD OIDC trust, deploy identity, and deployment binding rules. Each workload tier owns its assigned project reference, service, application service account, workload-specific bindings, and delegated DNS records. OWNERSHIP MAP organisation workload folder domain ownership DNS zone delegation environment workload project CI/CD OIDC trust deploy identity deployment binding rules workload-api assigned project reference service application service account workload-specific bindings delegated DNS records workload-worker assigned project reference service application service account workload-specific bindings delegated DNS records
Workload boundary published as a project

Organisation owns the logical container before any environment exists: the GCP folder where workload projects will live. It also owns domain ownership and DNS zone delegation, the record that says which lower layer is authoritative for a zone.

Environment provisions a workload project inside that folder and attaches it to the environment boundary. It creates the deployment access path into that project: CI/CD OIDC trust, the deploy identity, and the IAM binding rules that identity is allowed to apply. Then it publishes the .

The workload consumes that reference and creates the application service account, workload-specific bindings, delegated DNS records, and release resources inside the assigned project.

If the workload creates the project, its deploy identity needs authority over the environment or organisation layer. If environment has to discover workload-created projects after the fact, environment now depends on workload-published identity: an output, a label, an inventory query, or a registry entry. All of those shapes reverse the graph. The lower tier must publish the project reference; the higher tier consumes it.

Environment is the catalogue of assigned workload projects. The drift starts when that catalogue also becomes the place for workload internals: application service accounts, local IAM bindings, release resources, and delegated records. Once the project exists, those members belong with the workload and move with the application.

A shared environment is a poor default when cross-project IAM feels hard. With a proper tiering model, delegating a whole workload project is simpler than proving safe separation inside a shared one.

Members are lifecycle units

The provider's noun does not decide the tier; the owning lifecycle does. A member is any object a tier owns as part of that lifecycle: a cloud resource, a DNS zone, or a SaaS configuration object.

Environment owns the network because the network exists before workloads and many workloads attach to it. Its contract should expose the usable shape of that network: private subnets, intended routes, and allowed attachment surfaces. It should not enumerate every service that might consume it.

Identity follows the same rule. Organisation owns identity roots and broad policy boundaries. Environment owns federated access for deploys into that environment. An application service account belongs with the workload when its lifecycle follows the application. The workload carries that service account and declares the access it needs; IAM bindings against organisation or environment surfaces stay with the tier that owns that surface.

Population tests

The membership decision has to protect the graph before it optimises for convenience:

  • Which owner applies and repairs this without pulling unrelated releases into the same change?
  • Which surface does this object mutate?
  • Is the consumer declaring a need, or trying to apply a change outside its boundary?
  • Would this object still exist if the consumer that named it disappeared?
  • Does placing it here force a lower tier to read higher-tier outputs?
  • If removal is destructive, which tier stages consumer detachment first?

Leaving the decision implicit lets provider APIs choose the ownership boundary by default.

A lower tier should know the classes of consumers it supports, not maintain a growing hand-authored inventory of every consumer instance. When environment contains bespoke bindings, records, and listener rules for each service, environment review widens with every release and environment apply carries workload-specific failure modes.

Declarations belong higher when the workload owns the change. The mutation stays lower when the lower tier owns the shared surface, and the contract has to say how consumers attach and detach safely.

Resolver and binding rules need to be designed before stacks depend on each other through outputs. The next part moves from membership to enforcement: where the release path refuses invalid composition before any provider call runs.

In the companion repoGitHubenvironment/ (opens in new tab)