20

Isolate AWS Accounts, IAM, State, and Guardrails

Give dev, staging, and production separate AWS accounts and EKS clusters while promoting the same immutable Zheta Forge artifact.

Run it in the public monorepo

This course is built around the public Zheta Kubernetes Lab monorepo. The excerpt below is runnable source, not pseudocode.

Source: infra/stacks/prod/main.tf

provider "aws" {
  region              = var.region
  allowed_account_ids = [var.account_id]
  assume_role {
    role_arn = var.deployer_role_arn
  }

Code to reality

Declared intent
Bind production infrastructure changes to one expected AWS account and an explicit deployer role.
Interpreter
Terraform configures the AWS provider, assumes the role, and rejects credentials for any other account.
Software effect
The plan is scoped to the production state and account boundary before resource reconciliation begins.
Hardware effect
Only an authorized apply can allocate billable AWS networking, control-plane, compute, and data resources.
Observable evidence
Caller identity, reviewed plan, remote state lock, and AWS resource tags agree on the production account.

Start with the people and the result they need

The source tables below remain the detailed contract. Begin with these customer paths:

  • D20-UC-01
    • Person: Release manager
    • Job: Promote one tested artifact digest from dev to staging to production
    • Observable result: Same Research Brief, Service Desk, or Field Inspection bytes cross explicit evidence gates into separate accounts and clusters
  • D20-UC-02
    • Person: Cloud security administrator
    • Job: Prevent a dev identity or state runner from reading or changing production
    • Observable result: Dev role cannot assume production execution, access production state, secrets, cluster API, or deletion operations

Turn each customer job into a testable story

Now turn each customer job into a story with a result that an engineer can check:

  • D20-US-01
    • Story: As a release manager, I want to promote the same immutable digest through isolated accounts, so that environment confidence grows without rebuilding…
    • Observable acceptance: Each gate records digest, source, tests, approver, account, cluster, expected and observed response, timestamp, run, and trace
  • D20-US-02
    • Story: As a cloud security administrator, I want environment roles and state access independently scoped, so that a dev compromise cannot become a…
    • Observable acceptance: Denied role assumption, state read, secret read, and cluster access are recorded while approved production runner and customer probe pass

Add real state and observable proof

Finally trace each story through the system that owns its state and the evidence that proves the outcome:

  • D20-FLOW-01
    • Trigger: Release manager approves a dev-tested artifact for staging
    • Responsible systems: Artifact Registry, evidence gate, environment-specific release roles, per-account EKS and runtime controllers, probe runner
    • Authoritative state: Release Store for promotion; digest registry for bytes; each cluster API for runtime
    • Owned record: EnvironmentInventory
    • Observable evidence: Actor, source, digest, registry scan, test evidence, account and cluster IDs, expected and observed results, approvals, environments, timestamps,…
    • Failure signal: Digest changed, evidence incomplete, wrong account role, cluster mismatch, or customer probe failed
  • D20-FLOW-02
    • Trigger: Dev runner attempts production role, state, secret, and cluster access
    • Responsible systems: IAM Identity Center or approved federation, Security Token Service, environment role policies, state bucket and key policies, secrets and EKS…
    • Authoritative state: AWS IAM and resource policies for authority; audit service for attempts
    • Owned record: EnvironmentRole
    • Observable evidence: Caller ARN, denied actions and resource ARNs, account IDs, audit event IDs, production read and response traces, environment, time, and run ID
    • Failure signal: Dev principal assumes production role, reads production backend, reaches cluster, reads secret, or audit event missing

The enterprise problem and today’s slice

Enterprise problem: If dev, staging, and production share an AWS account, Elastic Kubernetes Service (EKS) cluster, Terraform state, or broad administrator role, one mistaken variable or compromised runner can cross every environment and erase the evidence needed for recovery. Whole-course context: The incoming evidence is a Terraform change model with modules, locked state, physical effects, and guarded repair; today implements the maximum-isolation topology chosen for Zheta Forge. Today’s slice: We assign dedicated AWS accounts, Virtual Private Clouds (VPCs), EKS clusters, state backends, short-lived Identity and Access Management (IAM) roles, secrets, observability, backups, deletion authority, and promotion gates to dev, staging, and production while local remains non-cloud. End-of-day evidence: An environment matrix, role-denial test, backend separation proof, cluster identities, artifact-digest promotion chain, customer probes, unaffected control, timestamps, run, and trace IDs demonstrate isolation. Still unsolved: The next slice will bootstrap and reconcile workloads through Argo CD, a GitOps controller, inside each isolated environment.

Customer use cases

Environment names are labels unless credentials, failure domains, state, and deletion authority are actually separated. These use cases prove both intended promotion and blocked cross-environment access.

Use case IDActorCustomer jobSuccess outcomeDenial or recovery evidence
D20-UC-01Release managerPromote one tested artifact digest from dev to staging to productionSame Research Brief, Service Desk, or Field Inspection bytes cross explicit evidence gates into separate accounts and clustersStaging or production rejects an untested, rebuilt, unsigned, or differently digested artifact
D20-UC-02Cloud security administratorPrevent a dev identity or state runner from reading or changing productionDev role cannot assume production execution, access production state, secrets, cluster API, or deletion operationsAccess-denied records name principal, action, resource, account, environment, time, and immutable audit event; production positive control stays healthy

Actor-centred user stories

Separate accounts reduce blast radius only when trust policies and state access do not recreate a hidden bridge. These stories bind promotion and denial to observable account identities.

Story IDUse case IDsUser storyObservable acceptance conditions
D20-US-01D20-UC-01As a release manager, I want to promote the same immutable digest through isolated accounts, so that environment confidence grows without rebuilding customer codeEach gate records digest, source, tests, approver, account, cluster, expected and observed response, timestamp, run, and trace
D20-US-02D20-UC-02As a cloud security administrator, I want environment roles and state access independently scoped, so that a dev compromise cannot become a production mutationDenied role assumption, state read, secret read, and cluster access are recorded while approved production runner and customer probe pass

End-to-end product flows

Copying source into production and rebuilding there can change dependencies or policy inputs, so promotion must move immutable identity plus evidence. The denial flow tests real AWS authorization rather than trusting an architecture diagram.

Flow IDUse case IDsPathTriggerNumbered stepsTerminal evidence
D20-FLOW-01D20-UC-01HappyRelease manager approves a dev-tested artifact for staging1. Resolve immutable scanned ECR digest and dev evidence. 2. Evaluate staging policy. 3. Assume staging-only release role. 4. Deploy the same digest to staging cluster. 5. Run archetype and failure probes. 6. Repeat under a distinct production approval and role.Actor, source, digest, registry scan, test evidence, account and cluster IDs, expected and observed results, approvals, environments, timestamps, run, and trace IDs
D20-FLOW-02D20-UC-02DeniedDev runner attempts production role, state, secret, and cluster access1. Verify caller is the bounded dev role. 2. Attempt named production actions without mutation authority. 3. Capture explicit denials. 4. Run approved production read and customer probe as positive controls.Caller ARN, denied actions and resource ARNs, account IDs, audit event IDs, production read and response traces, environment, time, and run ID

The release manager promotes identity and evidence, not rebuilt source. The customer result remains the terminal proof after the artifact crosses environment boundaries.

System design derived from the flows

Maximum isolation requires more than separate namespaces because namespaces share account IAM, cluster control plane, network, state, and failure domains. Zheta Forge uses one AWS account and one EKS cluster for each cloud environment.

Use case IDEntry pointResponsible servicesAuthoritative storeFailure evidence
D20-UC-01Promotion approval workflowArtifact Registry, evidence gate, environment-specific release roles, per-account EKS and runtime controllers, probe runnerRelease Store for promotion; digest registry for bytes; each cluster API for runtimeDigest changed, evidence incomplete, wrong account role, cluster mismatch, or customer probe failed
D20-UC-02AWS authorization testIAM Identity Center or approved federation, Security Token Service, environment role policies, state bucket and key policies, secrets and EKS authorization, audit trailAWS IAM and resource policies for authority; audit service for attemptsDev principal assumes production role, reads production backend, reaches cluster, reads secret, or audit event missing

Each account owns its VPC, cluster, runtime identities, secrets, state, logs, backups, and deletion controls. Provider membership, hosted-runtime workload identity, and generated-app user authorization remain explicit, least-privilege, audited, and independently revocable.

Data model and ownership

An environment matrix kept only in documentation drifts from deployed accounts and roles. Durable inventory and evidence records constrain automation while AWS APIs remain authoritative for live resources.

Generated-application database: Not created in this slice — the slice creates isolated infrastructure, identity, state, and promotion records; generated-app databases are later deployed inside each runtime boundary and retain their own tenant authority.

Record or entityStore and ownerPrimary keyForeign key or opaque referenceTenant keyMaterial constraintLifecycle and deletionUse case IDs
EnvironmentInventoryPlatform repository, owned by Cloud Platformenvironment_idAWS account, VPC, EKS cluster, backend, observability, backup, and deletion role refsNone — environment root scopes many tenantsDev, staging, and prod account, cluster, backend, and execution roles are distinct and immutable after provisioningCreate through account vending, update by review, retire after workloads/export, retain audit metadataD20-UC-01, D20-UC-02
EnvironmentRoleAWS IAM, owned by Cloud Securityrole_arnOpaque identity-provider and policy refsAWS account and environment IDShort-lived assumption; no wildcard cross-environment trust; production needs separate approvalProvision, test, rotate policy, disable, delete after environment retirementD20-UC-01, D20-UC-02
StateBackendDedicated AWS state account resources, owned by IaC Platformbucket_arn plus environment_keyKey and lock-table refs scoped to one environmentAWS account and environment IDEncryption, version recovery, locking, audit, and role-scoped access are mandatoryProvision before roots, version on writes, retain through recovery window, delete lastD20-UC-02
PromotionReceiptControl-plane Release Store, owned by Promotion Servicepromotion_idOpaque artifact digest, source evidence, account, cluster, and deployment refsorganization_idSame digest crosses ordered gates; each environment has independent approval and resultCreate at dev, append immutable staging/prod decisions, retain through release auditD20-UC-01

The environment evidence drives either ordered promotion or explicit denial. The final proof combines artifact identity, AWS authorization, isolated cluster identity, and customer response without treating one as a substitute for another.

Name every environment owner

Isolation becomes actionable when each paid or sensitive resource has one accountable owner and deletion order. Local uses Kind and local secrets only; every cloud environment has its own account and cluster.

ConcernLocalDevStagingProduction
Account and clusterDeveloper machine and KindDedicated dev account and EKSDedicated staging account and EKSDedicated production account and EKS
NetworkDocker networkDev VPCStaging VPCProduction VPC
Terraform stateDisposable local backend onlyDev backend key and roleStaging backend key and roleProduction backend key and tightly approved role
SecretsLocal development providerDev secret store and workload rolesStaging secret store and workload rolesProduction secret store and workload roles
Git and promotionFeature revisionIntegration policyProduction-like verificationProtected immutable promotion
Observability and backupDisposable telemetryShorter retention and recovery rehearsalProduction-like recovery proofApproved retention, recovery objective, and deletion owner

Prove cross-account denial

Policy review cannot prove the deployed trust relationship, so use a bounded dev identity to request explicitly forbidden production access. These commands must run in an approved test harness and are read-only or expected to fail.

: "${AWS_REGION:?set the intended AWS region}" "${EXPECTED_DEV_ACCOUNT_ID:?}" "${EXPECTED_PROD_ACCOUNT_ID:?}"
dev_account="$(aws --profile "${DEV_PROFILE:?set the approved dev profile}" --region "$AWS_REGION" sts get-caller-identity --query Account --output text)"
[ "$dev_account" = "$EXPECTED_DEV_ACCOUNT_ID" ] || { echo "dev account mismatch" >&2; exit 1; }
aws --profile "$DEV_PROFILE" --region "$AWS_REGION" sts assume-role --role-arn "${PROD_TERRAFORM_ROLE_ARN:?}" --role-session-name denied-test
aws --profile "$DEV_PROFILE" --region "$AWS_REGION" s3api head-object --bucket "${PROD_STATE_BUCKET:?}" --key "${PROD_STATE_KEY:?use the approved backend.hcl key}"
prod_account="$(aws --profile "${PROD_READ_PROFILE:?}" --region "$AWS_REGION" sts get-caller-identity --query Account --output text)"
[ "$prod_account" = "$EXPECTED_PROD_ACCOUNT_ID" ] || { echo "prod account mismatch" >&2; exit 1; }
aws --profile "$PROD_READ_PROFILE" --region "$AWS_REGION" eks describe-cluster --name zheta-forge-prod --query 'cluster.arn'
test -n "${FORGE_URL:?set FORGE_URL to a reachable production control-plane URL}"
FORGE_URL="$FORGE_URL" FORGE_TOKEN="${FORGE_TOKEN:?set the explicit bearer token}" ./scripts/smoke-product.sh

The exact environment root is infra/stacks/prod, and the module derives cluster name zheta-forge-prod; backend profiles and keys remain operator-supplied through backend.hcl, so the snippet requires them explicitly. The smoke script defaults to localhost only for local Compose; cloud proof must set an explicit reachable FORGE_URL.

The two dev-to-production calls must return access denied; the approved production read-only identity and customer probe are positive controls. Never grant temporary production access merely to make the test convenient.

Effect fieldWhat happens
Declared intentDev authority must not cross into production state or Terraform execution
InterpreterAWS Security Token Service, object storage authorization, EKS API, and application endpoint evaluate independent requests
Software effectDenied calls change no state; audit and evidence records capture attempts
Hardware effectAWS control-plane and network capacity answer requests; no workload or infrastructure mutation occurs
EvidenceCaller and resource ARNs, explicit denials, audit event IDs, production cluster ARN, response trace, environment, and time

Key takeaways

Maximum isolation uses separate accounts, clusters, state, identities, and approvals while reusing modules and immutable artifacts.

  • Namespaces do not isolate AWS IAM, Terraform state, cluster control plane, or account blast radius.
  • Promotion moves a digest and evidence; it never rebuilds application bytes.
  • A real access-denied test plus positive control proves deployed guardrails.

Checklist

An AWS environment is isolated only when both intended access and denied cross-access are observable.

  • [ ] Dev, staging, and production have distinct accounts, VPCs, EKS clusters, state keys, and roles
  • [ ] No environment runner holds long-lived cloud or kubeconfig credentials
  • [ ] Promotion preserves one signed artifact digest through independent gates
  • [ ] Cross-account denials and approved production positive controls are recorded