# Infrastructure Design: [Platform or workload name]

> Complete infrastructure design for a workload built and run with
> Infrastructure as Code: environments, network, compute, storage,
> managed services, repository and module conventions, state, CI/CD for
> infrastructure, secrets, identity and access, tagging and cost
> allocation, scaling, backup and disaster recovery, observability,
> security baselines with policy as code, change management and a cost
> estimate. Based on common industry practice for cloud infrastructure
> design. Replace every `[bracketed]` value and every row marked
> *Example*. Worked examples use a web application on a public cloud with
> Terraform or OpenTofu.

<!--
Guidance: How to use this template
- Decide environments (section 2), network ranges (section 3) and the
  account or subscription layout first. They are expensive to change once
  workloads are running.
- Everything in this document should exist as code. If a resource is
  created by hand, record it in section 18 as a known exception with a
  plan to import it.
- Keep provider-specific names in brackets so the design can be read by
  people who use a different cloud.
- Pair this document with the security architecture and the system design;
  link rather than repeat.
-->

## Document control

<!-- Guidance: Identify the platform, the cloud accounts it covers and who approves infrastructure changes. -->

| Field | Value |
|---|---|
| Document ID | [INFRA-DES-WEBAPP-001] |
| Platform / workload | [Customer portal] |
| Cloud provider(s) | [Provider] |
| Accounts / subscriptions / projects | [portal-dev, portal-stg, portal-prod, shared-network, security-logs] |
| Version | [0.1] |
| Status | [Draft / In review / Approved / Superseded] |
| Owner | [@platform-lead] |
| Classification | [Internal / Confidential] |
| Last updated | YYYY-MM-DD |
| Related | [System design], [Security architecture], [IaC repository URL] |

### Approvals

| Role | Name | Decision | Date |
|---|---|---|---|
| Platform lead | [@name] | [Approved / Changes requested] | YYYY-MM-DD |
| Application tech lead | [@name] | [Decision] | YYYY-MM-DD |
| Security | [@name] | [Decision] | YYYY-MM-DD |
| FinOps / budget owner | [@name] | [Decision] | YYYY-MM-DD |

## Revision history

<!-- Guidance: Record design-level changes and the IaC release that implements them. -->

| Version | Date | Author | Change summary |
|---|---|---|---|
| 0.1 | YYYY-MM-DD | [@name] | First draft |
| 0.2 | YYYY-MM-DD | [@name] | *Example:* Added second region for disaster recovery |
| 1.0 | YYYY-MM-DD | [@name] | *Example:* Approved; implemented in IaC release v1.0.0 |

## Contents

<!-- Guidance: Keep this list aligned with the headings. -->

1. [Introduction](#1-introduction)
2. [Environments and account structure](#2-environments-and-account-structure)
3. [Network design](#3-network-design)
4. [Compute](#4-compute)
5. [Storage and data services](#5-storage-and-data-services)
6. [Managed and shared services](#6-managed-and-shared-services)
7. [IaC repository structure and module conventions](#7-iac-repository-structure-and-module-conventions)
8. [State management](#8-state-management)
9. [CI/CD for infrastructure](#9-cicd-for-infrastructure)
10. [Identity and access management](#10-identity-and-access-management)
11. [Secrets management](#11-secrets-management)
12. [Tagging and cost allocation](#12-tagging-and-cost-allocation)
13. [Scaling](#13-scaling)
14. [Backup and disaster recovery](#14-backup-and-disaster-recovery)
15. [Observability](#15-observability)
16. [Security baselines and policy as code](#16-security-baselines-and-policy-as-code)
17. [Change management](#17-change-management)
18. [Cost estimate](#18-cost-estimate)
19. [Appendices](#19-appendices)

## 1. Introduction

<!-- Guidance: What the infrastructure supports, its requirements and the decisions already taken. -->

### 1.1 Purpose and scope

[What this design covers. Example: "Infrastructure for the customer
portal: networking, compute, data stores and supporting services in
three environments, provisioned entirely from the `infra-portal`
repository."]

### 1.2 Requirements driving the design

| ID | Requirement | Source | Design response |
|---|---|---|---|
| IR-01 | *Example:* 99.9% monthly availability | System design NFR-02 | Three availability zones; managed database with standby |
| IR-02 | *Example:* RPO 15 min, RTO 4 hours | Business continuity plan | Cross-region backups; scripted rebuild in DR region |
| IR-03 | *Example:* Personal data stays in the EU | Data protection assessment | EU regions only; policy blocks other regions |
| IR-04 | *Example:* Monthly run cost under EUR 12 000 in production | Budget | Right-sizing, commitments, non-production schedules |
| [IR-nn] | [Requirement] | [Source] | [Response] |

### 1.3 Key decisions

| ID | Decision | Alternatives | Rationale | ADR |
|---|---|---|---|---|
| ID-01 | *Example:* Terraform-compatible IaC (OpenTofu) | Provider-native templates, Pulumi | Team skills; multi-provider modules | [ADR-012] |
| ID-02 | *Example:* Managed Kubernetes for services | Serverless containers, VMs | Existing platform tooling | [ADR-013] |
| [ID-nn] | [Decision] | [Alternatives] | [Rationale] | [Link] |

## 2. Environments and account structure

<!-- Guidance: List every environment, what it is for, its isolation boundary and how close to production it is. -->

| Environment | Account / project | Region(s) | Purpose | Data | Parity with production |
|---|---|---|---|---|---|
| dev | `portal-dev` | [eu-west-1] | Feature development, ephemeral stacks | Synthetic | Reduced sizes, single AZ |
| staging | `portal-stg` | [eu-west-1] | Pre-production tests, load tests | Masked copy | Same topology, smaller sizes |
| production | `portal-prod` | [eu-west-1] primary, [eu-central-1] DR | Live service | Live | n/a |
| shared-network | `shared-network` | Both | Transit gateway, DNS, egress | None | n/a |
| security-logs | `security-logs` | [eu-west-1] | Central immutable logs | Logs | n/a |

Each environment is a separate account so that a mistake in one cannot
change another. Ephemeral dev stacks are named `dev-<branch>` and
destroyed after [72 hours] of inactivity.

## 3. Network design

<!-- Guidance: Address plan, subnets, routing, connectivity and traffic controls. Plan ranges for growth and avoid overlap with on-premises and partner networks. -->

### 3.1 Address plan

| Network | Environment | CIDR | Usable addresses | Notes |
|---|---|---|---|---|
| Corporate (reserved) | n/a | 10.0.0.0/12 | n/a | Do not use in cloud |
| VPC portal-prod | production | 10.32.0.0/16 | 65 536 | Primary region |
| VPC portal-prod-dr | production DR | 10.33.0.0/16 | 65 536 | DR region |
| VPC portal-stg | staging | 10.34.0.0/16 | 65 536 | |
| VPC portal-dev | dev | 10.35.0.0/16 | 65 536 | |
| [VPC] | [Environment] | [CIDR] | [n] | [Notes] |

### 3.2 Subnets (production primary region)

| Subnet | Tier | AZ | CIDR | Addresses | Route to internet |
|---|---|---|---|---|---|
| public-a | Public (load balancers, NAT) | a | 10.32.0.0/24 | 256 | Internet gateway |
| public-b | Public | b | 10.32.1.0/24 | 256 | Internet gateway |
| public-c | Public | c | 10.32.2.0/24 | 256 | Internet gateway |
| app-a | Private (compute) | a | 10.32.16.0/20 | 4 096 | NAT in same AZ |
| app-b | Private | b | 10.32.32.0/20 | 4 096 | NAT in same AZ |
| app-c | Private | c | 10.32.48.0/20 | 4 096 | NAT in same AZ |
| data-a | Isolated (databases) | a | 10.32.64.0/24 | 256 | None |
| data-b | Isolated | b | 10.32.65.0/24 | 256 | None |
| data-c | Isolated | c | 10.32.66.0/24 | 256 | None |

Application subnets are sized for pod networking. With $p$ pods per node
and $n$ nodes per zone at peak, each zone needs at least:

$$
N_{addr} \ge 1.5 \times n \times (p + 1)
$$

For $n = 20$ and $p = 58$, $N_{addr} \ge 1\,770$, so a /20 (4 096
addresses) leaves room for growth.

### 3.3 Topology and traffic flow

The diagram shows the production topology in one region.

```mermaid
flowchart TB
  internet([Internet]) --> waf["WAF and CDN"]
  waf --> alb["Application load balancer (public subnets)"]
  subgraph VPC["VPC 10.32.0.0/16"]
    alb --> k8s["Kubernetes nodes (app subnets, 3 AZ)"]
    k8s --> db[("Managed PostgreSQL (data subnets)")]
    k8s --> cache[("Managed Redis (data subnets)")]
    k8s --> nat["NAT gateways (one per AZ)"]
    k8s --> ep["Private endpoints: object storage, secrets, registry"]
  end
  nat --> egress([Allow-listed external APIs])
  VPC <--> tgw["Transit gateway (shared-network)"]
  tgw <--> onprem["Corporate network via VPN"]
```

### 3.4 Connectivity and controls

| Control | Design |
|---|---|
| Ingress | Only through WAF and load balancer on 443 |
| East-west | Security groups by tier; Kubernetes network policies default deny |
| Egress | NAT per AZ; DNS firewall allow-list for external domains |
| Private access to provider services | Private endpoints for storage, secrets, registry, logs |
| Hybrid | Site-to-site VPN [and dedicated link] via transit gateway |
| DNS | Private hosted zone `portal.internal`; public zone `portal.example.com` |

## 4. Compute

<!-- Guidance: What runs where, sizes, images and how nodes are patched. -->

| Workload | Platform | Instance / size | Min / max | Image / AMI source | Patching |
|---|---|---|---|---|---|
| Web and API services | Managed Kubernetes [version] | *Example:* 4 vCPU / 16 GB, ARM | 6 / 60 nodes | Provider-optimised node image | Node image rolled monthly |
| Background jobs | Same cluster, separate node pool | 8 vCPU / 32 GB, spot | 0 / 20 | Same | Same |
| Scheduled reports | Serverless functions | 1 GB memory | n/a | Managed runtime | Provider |
| [Workload] | [Platform] | [Size] | [Min / max] | [Image] | [Process] |

## 5. Storage and data services

<!-- Guidance: Every data store with its configuration, encryption, replication and lifecycle. -->

| Store | Service | Size / class | HA | Encryption | Lifecycle |
|---|---|---|---|---|---|
| Application database | Managed PostgreSQL [version] | *Example:* 8 vCPU / 32 GB, 500 GB gp3 | Multi-AZ standby | Customer-managed key | PITR 14 days |
| Cache | Managed Redis | 2 nodes, 13 GB | Replica in second AZ | In transit and at rest | n/a |
| User uploads | Object storage bucket | Standard, then infrequent access after 30 days | Regional | Customer-managed key | Delete after 2 years |
| Logs archive | Object storage, `security-logs` account | Archive class after 90 days | Regional, replicated | Customer-managed key, object lock | Retain 1 year |
| [Store] | [Service] | [Size] | [HA] | [Encryption] | [Lifecycle] |

## 6. Managed and shared services

<!-- Guidance: Platform services the workload relies on, who owns them and whether they are shared. -->

| Service | Purpose | Owned by | Shared with |
|---|---|---|---|
| Container registry | Images, signed | Platform team | All workloads |
| Secrets manager | Application secrets | This workload | none |
| Key management service | Encryption keys | Security team | All workloads |
| DNS | Public and private zones | Platform team | All workloads |
| Certificate manager | TLS certificates | This workload | none |
| Email sending service | Transactional email | This workload | none |
| [Service] | [Purpose] | [Owner] | [Sharing] |

## 7. IaC repository structure and module conventions

<!-- Guidance: How the code is organised so that environments are consistent and changes are small and reviewable. -->

### 7.1 Repository layout

Reusable modules are separated from the root modules that instantiate
them per environment and layer:

```text
infra-portal/
  modules/
    network/          # VPC, subnets, routes, endpoints
    eks-cluster/      # cluster, node pools, add-ons
    postgres/         # database, parameter group, alarms
    bucket/           # object storage with policy and lifecycle
  live/
    dev/
      network/        # root module: calls modules/network
      cluster/
      data/
    stg/
    prod/
    prod-dr/
  policies/           # policy-as-code rules (section 16)
  .ci/                # pipeline definitions
```

### 7.2 Conventions

| Topic | Convention |
|---|---|
| Tool and version | *Example:* OpenTofu [1.x], pinned in `.tool-versions`; provider versions pinned with lock file committed |
| Module sources | Internal modules by Git tag (`?ref=v1.4.2`); public modules only from an approved list, pinned to exact versions |
| Root modules | One per environment and layer (network, cluster, data); small blast radius |
| Variables | Typed, with descriptions and validation blocks; no defaults for environment-specific values |
| Outputs | Only what other layers need; read through remote state or parameter store |
| Naming | `<app>-<env>-<component>[-<az>]`, for example `portal-prod-db` |
| Formatting and linting | `fmt`, `validate` and a linter run in CI and pre-commit |
| Documentation | Each module has a README generated from variables and outputs |
| Module versioning | Semantic versioning; breaking changes in a major version with upgrade notes |

## 8. State management

<!-- Guidance: Where state lives, how it is locked, protected and recovered. State files can contain secrets. -->

| Topic | Design |
|---|---|
| Backend | *Example:* Object storage bucket per environment in a dedicated state account |
| Locking | Native lock file or lock table |
| Encryption | Bucket encryption with customer-managed key; TLS only |
| Access | Pipeline role read and write; engineers read-only for plan in non-production only |
| Versioning | Object versioning on; 90 days of history |
| Key layout | `<env>/<layer>/terraform.tfstate` |
| Drift detection | Scheduled plan every [6 hours]; drift raises a ticket |
| Recovery | Restore previous object version; documented in runbook |

## 9. CI/CD for infrastructure

<!-- Guidance: How a change goes from pull request to applied infrastructure, and who approves. -->

The pipeline below runs for every pull request and every merge to the
main branch.

```mermaid
flowchart LR
  pr["Pull request"] --> fmt["Format, validate, lint"]
  fmt --> sec["Static security scan and policy checks"]
  sec --> plan["Plan per affected root module"]
  plan --> review["Review with plan output and cost diff"]
  review --> merge["Merge to main"]
  merge --> applyDev["Apply dev and staging"]
  applyDev --> gate{"Manual approval"}
  gate --> applyProd["Apply production"]
  applyProd --> verify["Smoke tests and drift check"]
```

| Stage | Tooling | Gate |
|---|---|---|
| Static checks | [fmt, validate, linter] | Must pass |
| Security and policy | [IaC scanner], policy engine (section 16) | High findings block |
| Plan | Pipeline with read-only role | Plan attached to pull request |
| Cost diff | [Cost estimation tool] | Increases above [EUR 500 per month] need budget owner approval |
| Apply non-production | Pipeline role per environment | Automatic after merge |
| Apply production | Pipeline role, OIDC federation | Approval by platform lead; change window rules in section 17 |

## 10. Identity and access management

<!-- Guidance: Who and what can change infrastructure and read data. No long-lived keys for pipelines. -->

| Principal | Access | Environments | Mechanism |
|---|---|---|---|
| CI pipeline (plan) | Read-only | All | OIDC federation, short-lived role |
| CI pipeline (apply) | Admin on workload resources, no IAM on organisation | Per environment | OIDC, separate role per environment |
| Platform engineers | Read-only console; write via pipeline | All | SSO groups |
| Platform engineers (break-glass) | Admin, time-limited | Production | Just-in-time elevation, approval and alert |
| Application workloads | Least privilege per service | All | Workload identity (service account to cloud role) |
| [Principal] | [Access] | [Environments] | [Mechanism] |

## 11. Secrets management

<!-- Guidance: Where secrets are stored, how they reach workloads, rotation and how IaC avoids writing secrets into state. -->

| Topic | Design |
|---|---|
| Store | Secrets manager in each environment account |
| Creation | IaC creates the secret container; values set by rotation function or by an operator, never in code |
| Delivery to workloads | CSI driver or external secrets operator; mounted as files |
| Rotation | Database credentials every [30 days] automatically; third-party keys every [90 days] |
| State hygiene | Use data sources and generated passwords marked sensitive; state encrypted (section 8) |
| Detection | Secret scanning on the IaC repository in CI |

## 12. Tagging and cost allocation

<!-- Guidance: Mandatory tags make cost reports, ownership and automation work. Enforce them with policy. -->

| Tag key | Required | Allowed values | Example | Used for |
|---|---|---|---|---|
| `app` | Yes | Registered application IDs | `portal` | Cost allocation |
| `env` | Yes | `dev`, `stg`, `prod`, `prod-dr` | `prod` | Cost, policy scope |
| `owner` | Yes | Team handle | `team-portal` | Contact |
| `cost-centre` | Yes | Finance codes | `CC-4120` | Chargeback |
| `data-class` | Yes for data stores | `public`, `internal`, `confidential`, `personal` | `personal` | Security policy |
| `managed-by` | Yes | `iac`, `manual` | `iac` | Drift and exception reports |
| `schedule` | No | `office-hours`, `always-on` | `office-hours` | Automated shutdown |

Default tags are applied at provider level in every root module. Policy
rejects resources missing required tags; shared costs (NAT, transit,
logging) are allocated by [traffic share / fixed split] each month.

## 13. Scaling

<!-- Guidance: How each tier scales, on what signal and with what limits. Note quotas that must be raised before peaks. -->

| Tier | Mechanism | Signal | Limits | Quota to watch |
|---|---|---|---|---|
| Pods | Horizontal autoscaler | CPU 60%, requests per pod | 3 to 120 per service | n/a |
| Nodes | Cluster autoscaler / node provisioner | Pending pods | 6 to 60 nodes | vCPU quota per region |
| Database | Vertical, planned; read replicas | CPU above 70% for a week | Largest class [x] | Storage max |
| Cache | Add shard | Memory above 75% | [n] shards | n/a |
| [Tier] | [Mechanism] | [Signal] | [Limits] | [Quota] |

## 14. Backup and disaster recovery

<!-- Guidance: Tie backups and DR to RPO and RTO, and prove them with drills. -->

| Component | Backup method | Frequency | Retention | Copy to DR region | RPO | RTO |
|---|---|---|---|---|---|---|
| PostgreSQL | Automated snapshots and PITR | Continuous | 14 days PITR, 12 monthly | Yes, every 15 min | 15 min | 2 hours |
| Object storage | Versioning and cross-region replication | Continuous | 30 days of versions | Yes | Minutes | 1 hour |
| Kubernetes workloads | Rebuilt from IaC and GitOps manifests | n/a | n/a | Images replicated | 0 | 3 hours |
| IaC state | Object versioning | Every change | 90 days | Yes | 0 | 30 min |
| [Component] | [Method] | [Frequency] | [Retention] | [Y / N] | [RPO] | [RTO] |

DR strategy: [Example: "Pilot light. Network, cluster control plane and
database replica exist in the DR region; node pools scale from zero
during failover."] Last DR drill: YYYY-MM-DD, measured RTO [n] hours.

## 15. Observability

<!-- Guidance: Infrastructure signals, where they go and which alerts page someone. -->

| Signal | Source | Destination | Retention | Alert example |
|---|---|---|---|---|
| Metrics | Cloud metrics, node exporter, cluster metrics | [Monitoring system] | 13 months | Node CPU above 85% for 15 min |
| Logs | Control plane, load balancer, VPC flow logs, application | Central log store | 30 days hot, 1 year archive | Spike in 5xx at load balancer |
| Audit | Cloud audit trail, Kubernetes audit | `security-logs` account | 1 year, immutable | IAM change outside pipeline |
| Cost | Billing export | Cost dashboard | 24 months | Daily spend above forecast by 20% |
| Synthetic | External probes | [Monitoring system] | 13 months | Login page down from 2 locations |

## 16. Security baselines and policy as code

<!-- Guidance: The baseline every resource must meet and the automated rules that enforce it before and after deployment. -->

| ID | Baseline control | Enforced by | Stage |
|---|---|---|---|
| PC-01 | No public object storage buckets | Policy engine rule; account-level public access block | Plan and runtime |
| PC-02 | All storage and databases encrypted with customer-managed keys | Policy rule | Plan |
| PC-03 | No security group allows 0.0.0.0/0 except on load balancers port 443 | Policy rule | Plan |
| PC-04 | Required tags present (section 12) | Policy rule | Plan |
| PC-05 | Only approved regions | Organisation-level guardrail | Runtime |
| PC-06 | Audit logging cannot be disabled | Organisation-level guardrail | Runtime |
| PC-07 | Container images signed and scanned | Admission controller | Deploy |
| PC-08 | Benchmark compliance (for example CIS) monitored | Cloud security posture tool | Runtime |
| [PC-nn] | [Control] | [Mechanism] | [Stage] |

Policy rules live in `policies/`, are versioned with the IaC and have
unit tests. Exceptions require an entry in 19.2 with an expiry date.

## 17. Change management

<!-- Guidance: Which changes need which approvals and when they may run. Match the organisation's change process without blocking routine work. -->

| Change type | Examples | Approval | Window | Rollback |
|---|---|---|---|---|
| Standard | Scaling limits, tags, new alarms | Pull request review | Any time | Revert commit and apply |
| Normal | New data store, network rule change | Pull request plus platform lead | Business hours, not Friday afternoon | Documented in pull request |
| High risk | Database major version, CIDR change, cluster upgrade | Change advisory review | Agreed window with on-call present | Tested rollback plan |
| Emergency | Fix for active incident | Incident commander | Any time | Retrospective review within 2 days |

Freeze periods: [peak trading weeks, financial year end].

## 18. Cost estimate

<!-- Guidance: Show units and unit prices so the estimate can be updated. Note the pricing date and the discounts assumed. -->

Prices as of YYYY-MM-DD, [region], on-demand unless stated.

| Item | Unit | Unit price (EUR) | Quantity | Monthly cost (EUR) |
|---|---|---|---|---|
| Kubernetes control plane | cluster-month | [70] | 1 | [70] |
| Worker nodes (4 vCPU / 16 GB, ARM) | node-hour | [0.15] | 12 nodes x 730 h | [1 314] |
| Managed PostgreSQL, multi-AZ | instance-month | [1 100] | 1 | [1 100] |
| Database storage | GB-month | [0.25] | 500 | [125] |
| Managed Redis | node-month | [180] | 2 | [360] |
| NAT gateways | gateway-hour plus data | [0.05 + 0.05/GB] | 3 x 730 h, 2 TB | [210] |
| Load balancer | hour plus capacity units | [0.03] | 730 h | [40] |
| Object storage | GB-month | [0.023] | 3 000 | [69] |
| Logs and metrics | GB ingested | [0.60] | 800 | [480] |
| DR region pilot light | various | | | [600] |
| Non-production environments | various | | | [1 400] |
| **Total before discounts** | | | | **[5 768]** |
| Commitment discount on baseline compute and database | | [30%] | | [-725] |
| **Estimated total** | | | | **[5 043]** |

Monthly cost for a node pool is $C = N \times h \times p$, where $N$ is
nodes, $h$ hours per month (730) and $p$ the hourly price.

## 19. Appendices

<!-- Guidance: Glossary, exceptions and references. -->

### 19.1 Glossary

| Term | Meaning |
|---|---|
| IaC | Infrastructure as Code: infrastructure defined in versioned files and applied by tools |
| Root module | Directory where plan and apply run, combining modules for one environment and layer |
| State | Record the IaC tool keeps of the resources it manages |
| Drift | Difference between the real infrastructure and the code |
| Pilot light | DR approach with minimal standby resources scaled up during failover |
| [Term] | [Meaning] |

### 19.2 Exceptions and manual resources

| ID | Resource or rule | Reason | Compensating control | Owner | Expires |
|---|---|---|---|---|---|
| EXC-01 | *Example:* DNS zone created manually | Created before IaC adoption | Import planned in sprint [n] | [@name] | YYYY-MM-DD |
| [EXC-nn] | [Item] | [Reason] | [Control] | [@name] | YYYY-MM-DD |

### 19.3 References

| Reference | Use |
|---|---|
| [Cloud provider documentation] | Service limits and configuration |
| [IaC tool documentation] | State, modules and providers |
| [Organisation cloud landing zone standard] | Account structure and guardrails |
| [Security architecture document] | Controls referenced in section 16 |

## Standard and licence note

Based on common industry practice for cloud infrastructure design and
Infrastructure as Code. Original text written for this collection;
prices, sizes and ranges are illustrative and must be checked against
your provider's current offering. Template licence: MIT.
