Skip to content

NHSDigital/hometest-mgmt-terraform

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

70 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

NHS HomeTest Management Terraform

Infrastructure as Code (IaC) for the NHS HomeTest Service using Terraform and Terragrunt for multi-environment, multi-account AWS deployments.

Table of Contents

Overview

This repository manages the AWS infrastructure for the NHS HomeTest Service, including:

  • Bootstrap β€” Terraform state backend (S3 + KMS) and GitHub OIDC for CI/CD
  • Networking β€” VPC, subnets, NAT gateways, Network Firewall, VPC endpoints, Route53 with DNSSEC
  • Shared Services β€” WAF, ACM certificates, KMS, Cognito, IAM roles, SNS alerts, Secrets Manager
  • Aurora PostgreSQL β€” Serverless v2 database with Goose migrations
  • ECS Cluster β€” Fargate cluster with shared ALB for WireMock and container workloads
  • HomeTest Application β€” Lambda functions, API Gateway, CloudFront + S3 SPA, SQS queues, WireMock

Architecture

Cloud Resources

graph TB
    classDef aws fill:#FF9900,stroke:#232F3E,color:#232F3E,font-weight:bold
    classDef network fill:#8C4FFF,stroke:#232F3E,color:#fff
    classDef security fill:#DD344C,stroke:#232F3E,color:#fff
    classDef compute fill:#ED7100,stroke:#232F3E,color:#fff
    classDef storage fill:#3B48CC,stroke:#232F3E,color:#fff
    classDef database fill:#3B48CC,stroke:#232F3E,color:#fff
    classDef cdn fill:#8C4FFF,stroke:#232F3E,color:#fff
    classDef messaging fill:#E7157B,stroke:#232F3E,color:#fff
    classDef identity fill:#DD344C,stroke:#232F3E,color:#fff
    classDef mgmt fill:#E7157B,stroke:#232F3E,color:#fff
    classDef container fill:#ED7100,stroke:#232F3E,color:#fff

    USER["πŸ‘€ User<br/>{env}.poc.hometest.service.nhs.uk"]

    subgraph AWS["☁️ AWS Account (poc: 781863586270 / dev: 781195019563) β€” eu-west-2"]
        subgraph BOOTSTRAP["πŸ”§ Bootstrap (deployed once)"]
            S3STATE["πŸ“¦ S3<br/>Terraform State"]:::storage
            KMSSTATE["πŸ” KMS<br/>State Encryption"]:::security
            OIDC["πŸ”‘ GitHub OIDC<br/>IAM Role"]:::identity
        end

        subgraph EDGE["🌐 Edge Services"]
            R53["🌍 Route53<br/>hometest.service.nhs.uk<br/>DNSSEC + DNS Query Logging"]:::network
            WAFCF["πŸ›‘οΈ WAF<br/>CloudFront"]:::security
            WAFAPIGW["πŸ›‘οΈ WAF<br/>API Gateway / ALB"]:::security
            ACM["πŸ“œ ACM<br/>*.poc.hometest.service.nhs.uk"]:::security
        end

        subgraph VPC["πŸ”’ VPC 10.0.0.0/16"]
            subgraph PUBSUB["Public Subnets"]
                NAT["πŸ”€ NAT Gateway"]:::network
                NFW["🧱 Network Firewall<br/>Domain + IP Filtering"]:::security
                ALB["πŸ”€ ALB<br/>Shared (ECS)"]:::network
            end

            subgraph PRIVSUB["Private Subnets"]
                subgraph ENV_DEV["πŸ“¦ Per-Environment (dev, uat, demo, prod)"]
                    CF["☁️ CloudFront<br/>+ S3 SPA (Next.js)"]:::cdn
                    APIGW["πŸ”Œ API Gateway<br/>REST API v1"]:::compute
                    L1["Ξ» eligibility-lookup"]:::compute
                    L2["Ξ» login"]:::compute
                    L3["Ξ» session"]:::compute
                    L4["Ξ» order-service"]:::compute
                    L5["Ξ» get-order"]:::compute
                    L6["Ξ» order-router<br/>(SQS-triggered)"]:::compute
                    L7["Ξ» order-result"]:::compute
                    L8["Ξ» get-results"]:::compute
                    L9["Ξ» order-status"]:::compute
                    L10["Ξ» postcode-lookup"]:::compute
                    SQS1["πŸ“¨ SQS<br/>order-placement"]:::messaging
                    SQS2["πŸ“¨ SQS<br/>notify-messages"]:::messaging
                    SQS3["πŸ“¨ SQS<br/>order-results"]:::messaging
                    SQS4["πŸ“¨ SQS FIFO<br/>notifications"]:::messaging
                    WM["🐳 WireMock<br/>ECS Fargate<br/>(optional)"]:::container
                end

                VPCE["πŸ”— VPC Endpoints<br/>S3, Lambda, SecretsManager,<br/>SQS, KMS, CloudWatch, ECR"]:::network
            end

            subgraph DATASUB["Data Subnets (isolated)"]
                RDS["🐘 Aurora PostgreSQL<br/>Serverless v2"]:::database
            end
        end

        subgraph SHARED["πŸ” Shared Services"]
            KMS["πŸ”‘ KMS<br/>main + pii_data keys"]:::security
            COGNITO["πŸ‘₯ Cognito<br/>User Pool + Identity Pool"]:::identity
            IAM["πŸ‘€ Developer IAM<br/>Deploy Role"]:::identity
            SM["πŸ—οΈ Secrets Manager<br/>Supplier Credentials"]:::security
            SNS["πŸ“’ SNS<br/>Alerts Topic"]:::messaging
        end

        subgraph EXTERNAL["🌐 External Services"]
            NHSLOGIN["NHS Login<br/>(sandpit / prod)"]
            OSPLACES["OS Places API<br/>(postcode lookup)"]
            SUPPLIERS["Supplier APIs<br/>(Preventex, etc.)"]
        end
    end

    USER -->|HTTPS| R53
    R53 -->|DNS| CF
    CF -->|"/* β†’ S3 SPA"| APIGW
    CF -.->|WAF| WAFCF
    CF -.->|TLS| ACM

    APIGW -->|"/eligibility-lookup/*"| L1
    APIGW -->|"/login/*"| L2
    APIGW -->|"/session/*"| L3
    APIGW -->|"/order/* (POST)"| L4
    APIGW -->|"/get-order/* (GET)"| L5
    APIGW -->|"/result/*"| L7
    APIGW -->|"/results/* (GET)"| L8
    APIGW -->|"/test-order-status/*"| L9
    APIGW -->|"/postcode-lookup/*"| L10
    APIGW -.->|WAF| WAFAPIGW

    SQS1 -->|trigger| L6

    L1 & L5 & L7 & L8 & L9 -->|query| RDS
    L4 -->|enqueue| SQS1
    L7 & L9 -->|enqueue| SQS2
    L2 -.->|auth| NHSLOGIN
    L10 -.->|lookup| OSPLACES
    L6 -->|HTTP| SUPPLIERS
    L6 -.->|secrets| SM

    L1 & L2 & L3 & L4 & L5 & L6 & L7 & L8 & L9 & L10 -->|egress| NAT
    NAT -->|filtered| NFW
    L1 & L2 & L3 & L4 & L5 & L6 & L7 & L8 & L9 & L10 -.->|encrypt| KMS
    L1 & L2 & L3 & L4 & L5 & L6 & L7 & L8 & L9 & L10 -.->|private access| VPCE

    ALB -->|host routing| WM

    SNS -.->|alarms| SQS1 & SQS2 & SQS3 & SQS4

    OIDC -.->|"CI/CD"| S3STATE
Loading

Prerequisites

The following tools are managed via mise (see .mise.toml):

Tool Version Purpose
Terraform 1.14.8 Infrastructure provisioning
Terragrunt 1.0.0 DRY Terraform configuration
AWS CLI 2.34.26 AWS interaction
Python 3.14.2 Git hooks, scripting
TFLint 0.61.0 Terraform linting
terraform-docs 0.22.0 Auto-generated documentation
Trivy 0.69.3 Security scanning
Checkov 3.2.517 Policy-as-code scanning
Gitleaks 8.30.1 Secret scanning
pre-commit 4.5.1 Git hooks
Go 1.26.2 Lambda goose migrator builds
goose 3.27.0 Database migrations
Vale 3.14.1 Prose linting

Additional requirements:

Install all tool versions:

mise install

AWS SSO Setup

aws configure sso

# Resulting ~/.aws/config profile:
# [profile Admin-PoC]
# sso_session = nhs
# sso_account_id = 781863586270
# sso_role_name = Admin
# region = eu-west-2
#
# [sso-session nhs]
# sso_start_url = https://d-9c67018f89.awsapps.com/start/#
# sso_region = eu-west-2
# sso_registration_scopes = sso:account:access

aws sso login --profile Admin-PoC
export AWS_PROFILE=Admin-PoC

Getting Started

# Clone the repository
git clone https://github.com/NHSDigital/hometest-mgmt-terraform.git
cd hometest-mgmt-terraform

# Install tool versions
mise install

# Configure pre-commit hooks and development dependencies
make config

Infrastructure Components

See infrastructure/README.md for the full infrastructure guide including:

  • Directory structure and module documentation
  • Deployment order and dependencies
  • Security features (WAF, KMS, VPC, Network Firewall)
  • Troubleshooting guide

Key Directories

Directory Purpose
infrastructure/src/ Terraform root modules (bootstrap, network, shared_services, aurora-postgres, ecs-cluster, hometest-app, lambda-goose-migrator, mock-service, rds-postgres)
infrastructure/modules/ Reusable Terraform modules (api-gateway, cloudfront-spa, lambda, lambda-iam, aurora-postgres, sqs, sns, waf, developer-iam, deployment-artifacts)
infrastructure/environments/ Terragrunt environment configurations (poc, dev accounts with core + per-env app stacks)
scripts/ Build, test, and deployment helper scripts
docs/ ADRs, developer guides, diagrams, user guides
.github/workflows/ CI/CD pipelines

Deployment

Quick Deploy

# 1. Bootstrap (first time only β€” creates state backend)
cd infrastructure/src/bootstrap
terraform init && terraform apply

# 2. Deploy core (network β†’ shared_services β†’ aurora-postgres β†’ ecs)
cd infrastructure/environments/poc/core/network
terragrunt apply

cd ../shared_services
terragrunt apply

cd ../aurora-postgres
terragrunt apply

cd ../ecs
terragrunt apply

# 3. Deploy application environment (app + lambda-goose-migrator)
cd ../../hometest-app/dev/lambda-goose-migrator
terragrunt apply

cd ../app
terragrunt apply

Deploy All

cd infrastructure/environments/poc
terragrunt run-all apply

Environments

The infrastructure supports multiple AWS accounts and environments:

Account Account ID Environments
poc 781863586270 dev, uat, demo, prod, dev-example
dev 781195019563 staging

Each environment under poc/hometest-app/{env}/ or dev/hometest-app/{env}/ contains:

  • env.hcl β€” environment name, domain overrides, feature flags (e.g., WireMock)
  • app/terragrunt.hcl β€” hometest application stack
  • lambda-goose-migrator/terragrunt.hcl β€” database migrations

Domain pattern: {env}.{account}.hometest.service.nhs.uk (e.g., dev.poc.hometest.service.nhs.uk)

Development Tools

Pre-commit Hooks

Configured in .pre-commit-config.yaml:

  • terraform_fmt / terragrunt_fmt β€” formatting
  • terraform_tflint β€” linting
  • terragrunt_validate_inputs β€” input validation per stack
  • terraform_checkov β€” policy-as-code
  • terraform_docs β€” auto-generate module docs
  • gitleaks β€” secret detection
  • markdownlint β€” Markdown linting
  • sqlfluff-lint / sqlfluff-fix β€” SQL linting
  • actionlint β€” GitHub Actions linting
  • shellcheck β€” shell script analysis
  • yamllint β€” YAML linting
# Run all checks
pre-commit run --all-files

# Or via mise task
mise run pre-commit

Mise Tasks

mise run pre-commit            # Run all pre-commit hooks
mise run test-migrations       # Test Goose DB migrations against local PostgreSQL
mise run test-migrations-keep  # Same, but keep the PostgreSQL container running
mise run tf-clean-cache        # Remove .external_modules, .terragrunt-cache, .terraform.lock.hcl

Testing

make test

Documentation

External Resources

Licence

Unless stated otherwise, the codebase is released under the MIT License. This covers both the codebase and any sample code in the documentation.

Any HTML or Markdown documentation is Β© Crown Copyright and available under the terms of the Open Government Licence v3.0.

About

Terraform to hold management account infrastructure code for Home Test

Resources

License

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors