Repeatable VPCs and Private Connectivity
Build the same isolated network contract in three AWS accounts before any Kubernetes workload exists.
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/modules/environment/main.tf
resource "aws_subnet" "private" {
count = 2
vpc_id = aws_vpc.this.id
availability_zone = local.azs[count.index]
cidr_block = cidrsubnet(var.vpc_cidr, 4, count.index + 8)
tags = merge(local.tags, {
"kubernetes.io/role/internal-elb" = "1"
})
}
Code to reality
- Declared intent
- Place environment workloads across two private availability-zone subnets.
- Interpreter
- Terraform and the AWS provider create subnets and label them for internal Kubernetes load balancers.
- Software effect
- EKS and its controllers discover private network placement from stable subnet IDs and tags.
- Hardware effect
- AWS allocates isolated address ranges and routes network interfaces across two physical zones.
- Observable evidence
- Terraform outputs, AWS subnet inventory, EKS node addresses, and a private connectivity probe agree.
Start with the people and the result they need
The source tables below remain the detailed contract. Begin with these customer paths:
- D21-UC-01
- Person: Platform engineer
- Job: Create equivalent private network foundations in three isolated accounts
- Observable result: Each plan contains non-overlapping public/private subnets, per-zone NAT, service endpoints, and private EKS placement
- D21-UC-02
- Person: Security reviewer
- Job: Review private enterprise connectivity without claiming unimplemented telemetry
- Observable result: Approved endpoint configuration is present and unsolicited ingress has no declared rule
Turn each customer job into a testable story
Now turn each customer job into a story with a result that an engineer can check:
- D21-US-01
- Story: As a platform engineer, I want one parameterised VPC module instantiated per account, so that fixes are reusable without sharing runtime state
- Observable acceptance: Plans use the same module version, distinct account aliases, CIDRs, state keys, and resource IDs
- D21-US-02
- Story: As a security reviewer, I want missing runtime reachability evidence to block approval, so that a green Terraform plan is not mistaken for a safe…
- Observable acceptance: Review names source, destination, protocol, expected result, current plan observation, account, time, run, and the missing Flow Log/runtime-proof…
Add real state and observable proof
Finally trace each story through the system that owns its state and the evidence that proves the outcome:
- D21-FLOW-01
- Trigger: Engineer proposes the network revision
- Responsible systems: Terraform CLI, AWS provider, VPC, subnets, route tables, NAT, endpoints, KMS
- Authoritative state: Reviewed Git revision plus one remote Terraform state per account/environment
- Owned record: NetworkStackRevision
- Observable evidence: Account ID, plan digest, state key, VPC/subnet/endpoint IDs, successful probe, environment, timestamp, and run ID
- Failure signal: Provider-account mismatch, CIDR collision, public worker placement, missing endpoint, or apply error
- D21-FLOW-02
- Trigger: Reviewer evaluates network evidence before any apply
- Responsible systems: IAM, security groups, route tables, endpoint declarations, policy gate
- Authoritative state: Terraform plan before apply; AWS network APIs only after approved deployment
- Owned record: ReachabilityEvidence
- Observable evidence: Plan fields, missing-runtime-proof finding, actor, resource, protocol, account, environment, time, and run ID
- Failure signal: Unexpected ingress rule, wrong endpoint target, or missing required runtime-proof control
The enterprise problem and today’s slice
Enterprise problem: A generated application can pass local tests yet leak data or become unreachable in AWS when routes, endpoints, and egress rules differ by environment. Whole-course context: The incoming evidence separates infrastructure state from workload state; today creates the network foundation consumed by every later EKS cluster. Today’s slice: We inspect the real reusable infra/modules/environment VPC declaration and instantiate it independently in dev, staging, and production AWS accounts. End-of-day evidence: Three reviewed plans identify account, VPC, public and private subnets, NAT routes, AWS service and optional enterprise connector endpoints, denied ingress expectation, environment, timestamp, and immutable run ID; VPC Flow Logs are explicitly a remaining gap because the current module does not declare them. Still unsolved: No AWS apply, EKS runtime, workload, public route, or Flow Log proof exists until owner-approved cloud execution.
Customer use cases
Networks fail customers when reachability is accidental, so the contract must prove both an intended path and a denial. A virtual private cloud (VPC) is an isolated AWS network boundary.
| Use case ID | Actor | Customer job | Success outcome | Denial or recovery evidence |
|---|---|---|---|---|
| D21-UC-01 | Platform engineer | Create equivalent private network foundations in three isolated accounts | Each plan contains non-overlapping public/private subnets, per-zone NAT, service endpoints, and private EKS placement | A plan with overlapping ranges or public worker placement is rejected |
| D21-UC-02 | Security reviewer | Review private enterprise connectivity without claiming unimplemented telemetry | Approved endpoint configuration is present and unsolicited ingress has no declared rule | Plan evidence records the denied design expectation and a production gap for runtime reachability/Flow Log proof |
Actor-centred user stories
Copying whole network stacks creates silent drift, while sharing one VPC collapses isolation. These stories require one module contract and three independent instances.
| Story ID | Use case IDs | User story | Observable acceptance conditions |
|---|---|---|---|
| D21-US-01 | D21-UC-01 | As a platform engineer, I want one parameterised VPC module instantiated per account, so that fixes are reusable without sharing runtime state | Plans use the same module version, distinct account aliases, CIDRs, state keys, and resource IDs |
| D21-US-02 | D21-UC-02 | As a security reviewer, I want missing runtime reachability evidence to block approval, so that a green Terraform plan is not mistaken for a safe network | Review names source, destination, protocol, expected result, current plan observation, account, time, run, and the missing Flow Log/runtime-proof control |
End-to-end product flows
A network plan is only an intermediate artifact; the customer-relevant result is a bounded private path that later workloads can use. The denied flow proves isolation rather than merely claiming it.
| Flow ID | Use case IDs | Path | Trigger | Numbered steps | Terminal evidence |
|---|---|---|---|---|---|
| D21-FLOW-01 | D21-UC-01 | Happy | Engineer proposes the network revision | 1. Assume the environment deployment role. 2. Select that environment’s remote state. 3. Plan the shared module with unique CIDR and Availability Zones. 4. Review routes, endpoints, encryption, and logs. 5. Apply only after approval. 6. Probe an approved AWS endpoint. | Account ID, plan digest, state key, VPC/subnet/endpoint IDs, successful probe, environment, timestamp, and run ID |
| D21-FLOW-02 | D21-UC-02 | Denied | Reviewer evaluates network evidence before any apply | 1. Resolve planned source and target. 2. Verify account/VPC variables. 3. Inspect planned routes, endpoints, and security groups. 4. Record that runtime denial and positive control are unavailable. 5. Block production acceptance. | Plan fields, missing-runtime-proof finding, actor, resource, protocol, account, environment, time, and run ID |
The engineer asks for private connectivity and finishes with a real network observation, not a plan exit code.
System design derived from the flows
A VPC module and its three instances solve different responsibilities: the module removes duplication, while each stack owns isolated addresses and state. AWS Identity and Access Management (IAM) limits which deployment role may change each account.
| Use case ID | Entry point | Responsible services | Authoritative store | Failure evidence |
|---|---|---|---|---|
| D21-UC-01 | infra/modules/environment consumed by infra/stacks/{dev,staging,prod} | Terraform CLI, AWS provider, VPC, subnets, route tables, NAT, endpoints, KMS | Reviewed Git revision plus one remote Terraform state per account/environment | Provider-account mismatch, CIDR collision, public worker placement, missing endpoint, or apply error |
| D21-UC-02 | Network plan review | IAM, security groups, route tables, endpoint declarations, policy gate | Terraform plan before apply; AWS network APIs only after approved deployment | Unexpected ingress rule, wrong endpoint target, or missing required runtime-proof control |
Terraform interprets the reusable declaration, but the AWS account owns the real routes and packets. The denial box adds the security consequence missing from a topology-only drawing.
DRY is the shared infra/modules/environment contract; SRP keeps each stack responsible for one account, while Terraform’s provider injection is IoC/DI at the infrastructure boundary. MVC and PubSub remain product-layer patterns and must not be invented inside the network module.
Data model and ownership
Network identity must remain independent in each account or deleting dev could mutate production. Durable infrastructure and evidence records are sufficient for this slice.
Generated-application database: Not created in this slice — network intent, AWS resource state, Terraform mappings, and reachability evidence are durable without generated-application domain data.
| Record or entity | Store and owner | Primary key | Foreign key or opaque reference | Tenant key | Material constraint | Lifecycle and deletion | Use case IDs |
|---|---|---|---|---|---|---|---|
| NetworkStackRevision | Git and remote Terraform backend, owned by Infrastructure Platform | environment plus commit SHA | Opaque AWS account and state object references | environment_id | One account, role, CIDR, and state key per environment; CIDRs never overlap | Plan, approve, apply, supersede; destroy only after cluster/data dependencies retire | D21-UC-01, D21-UC-02 |
| ReachabilityEvidence | Evidence Store, owned by Security Operations | evidence_id | Opaque planned VPC, source, target, and run references | environment_id | Expected and observed plan result plus explicit missing runtime proof are mandatory | Append plan review; replace gap only with real applied-path evidence; retain by audit policy | D21-UC-01, D21-UC-02 |
The same VPC now owns a lifecycle record, while successful and denied traffic converge on immutable evidence without transferring network authority to the evidence service.
Declare and inspect the network contract
An environment can silently target the wrong account, so validate caller identity before planning. Run this labelled snippet from the public zheta-kubernetes-lab monorepo; its stable inputs are infra/modules/environment and infra/stacks/dev (with sibling staging and prod stacks).
: "${AWS_PROFILE:?set the approved AWS profile}" "${AWS_REGION:?set the intended AWS region}" "${EXPECTED_ACCOUNT_ID:?set the intended 12-digit AWS account}"
actual_account="$(aws --profile "$AWS_PROFILE" --region "$AWS_REGION" sts get-caller-identity --query Account --output text)"
[ "$actual_account" = "$EXPECTED_ACCOUNT_ID" ] || { echo "AWS account mismatch" >&2; exit 1; }
aws --profile "$AWS_PROFILE" --region "$AWS_REGION" sts get-caller-identity # Prove which account the shell can change.
terraform -chdir=infra/stacks/dev init -reconfigure -backend-config=backend.hcl
terraform -chdir=infra/stacks/dev plan -out=plan.bin -var-file=environment.tfvars
terraform -chdir=infra/stacks/dev show -json plan.bin > plan.json
backend.hcl and environment.tfvars are intentionally operator-supplied and uncommitted; the committed examples do not grant AWS authority. The module’s current network is private EKS placement with NAT egress and selected endpoints, not a fully private cluster with no internet egress.
| Effect field | What happens |
|---|---|
| Declared intent | Create one dev network from the shared module without sharing staging or production state |
| Interpreter | Terraform reads configuration and the AWS provider calls account-scoped APIs |
| Software effect | The plan records proposed VPC, subnet, route, endpoint, encryption, and logging changes |
| Hardware effect | Planning creates none; apply later allocates provider network and logging capacity |
| Evidence | Caller account, plan digest, resource addresses, state key, and reviewed JSON |
Key takeaways
Private networking is a tested product boundary, not a collection of subnets.
- Reuse module code while isolating accounts, CIDRs, roles, and state.
- Prove both an approved path and a denied path.
- A local Kind network shares one laptop and does not prove AWS failure-domain isolation.
Checklist
The network foundation is ready only when all three stacks are reviewable and independent.
- [ ] Dev, staging, and production target distinct AWS accounts and state keys
- [ ] Private subnets have no accidental public ingress path
- [ ] Endpoint success and ingress denial evidence share one run window
- [ ] Destruction order protects clusters and managed data