Cloud Architecture

Designing Secure Multi-Environment Infrastructure

Isolate blast radius with accounts, identities, state, and promotion of the same artifact—not a shared admin role with three VPC names.

An environment is an isolation boundary

Calling three VPCs “dev,” “staging,” and “prod” does not make them environments if they share an account, a Terraform state file, and a human administrator role. An environment is a blast-radius decision: what a leaked credential, a bad apply, or a compromised pipeline can reach.

This guide uses a hypothetical AWS organization (the same isolation ideas apply to Azure management groups and subscriptions). It is not a client landing-zone design.

Isolate the things that can destroy production

Prefer account or subscription separation for at least production versus everything else. Stronger layouts add a shared-services account, a security/logging account, and per-team development accounts. The cost is more identity and networking work. The benefit is that a development apply cannot delete the production database.

HCL · EDUCATIONAL EXAMPLE
# Educational remote-state layout. One backend per environment.
terraform {
  backend "s3" {
    bucket         = "example-terraform-state-prod"
    key            = "app/terraform.tfstate"
    region         = "eu-west-1"
    dynamodb_table = "example-terraform-locks-prod"
    encrypt        = true
  }
}

Do not put every environment in one state key. A single terraform destroy typo should not be able to walk the company.

State backends, KMS keys, and lock tables are production privileges. Restrict who can read state: it often contains secret material even when you tried to avoid that.

Identity beats CIDR

Private subnets do not help if a CI role can create IAM users or read every secret. Map:

  • Who can assume the deploy role, from which repository and ref.
  • Who can change DNS, routing, and identity.
  • Who has break-glass, for how long, and how it is logged.
JSON · EDUCATIONAL EXAMPLE
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Federated": "arn:aws:iam::111122223333:oidc-provider/token.actions.githubusercontent.com" },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
          "token.actions.githubusercontent.com:sub": "repo:example/api:environment:production"
        }
      }
    }
  ]
}

This is an educational trust-policy sketch. Account ids and repo names are placeholders. The important part is the sub restriction: a pull request from a fork should not assume the production role.

Azure equivalent: federated credentials on a workload identity, scoped to the pipeline and environment—not a client secret in a variable group that every branch can use.

Promote artifacts, not rebuilds

Development, staging, and production should run the same image digest and a reviewed infrastructure plan aimed at that environment. Rebuilding for production invents a new artifact. Copy-paste of terraform.tfvars between environments is how a development CIDR lands in production.

Configuration differences (URLs, replica counts, log levels) belong in environment-scoped variables or parameter stores. Secrets belong in a manager, injected with the environment’s identity. They do not belong in Git, AMI images, or a shared “prod-and-dev” secret.

Network and data

Public load balancers, private application subnets, private data stores. Security groups from the load balancer to the app, not 0.0.0.0/0 on the data tier “because the app is private.”

Production data should not be copied to development laptops or to a shared staging database without masking and a recorded purpose. Restore tests belong in an isolated account.

What to verify before calling it multi-environment

  • A development apply cannot touch production state or identity.
  • Production deploy identity cannot be assumed from an unprotected branch.
  • Rollback or roll-forward is defined per environment.
  • Restore has been practiced somewhere that is not production.
  • Logs and access records from break-glass exist and someone reads them.

Common failure modes

  • One AWS account, three VPC names, one admin role.
  • One Terraform state for the company.
  • Long-lived access keys shared across environments.
  • Staging that is a permanent fork of production configuration.
  • “Temporary” public databases.

Production-ready AWS with Terraform covers modules, plans, and recovery. Supply-chain security covers the pipeline identity that crosses these boundaries. See Cloud Infrastructure, DevSecOps, and DevOps consulting. The architecture lab is a single hypothetical VPC—use it as vocabulary, not as a landing zone.

Related technical guides

LET’S BUILD WHAT’S NEXT

Good engineering starts with a conversation.

Let’s connect