Skip to content

Terraform

Overview

Infrastructure is managed with Terraform (>= 1.6.0) using the AWS provider (~> 5.0). State is stored remotely in S3 with file-based locking and encryption.

Each environment lives in its own AWS account and has its own state file. Changes are currently applied locally — CI-based apply is planned but not yet implemented.

Repository Structure

infra/
├── environments/
│   ├── prod/
│   │   ├── eu-central-1/       # Production core, vault, edge, atrax-api
│   │   └── eu-west-1/          # Production atrax-node (region-isolated)
│   └── stage/
│       ├── eu-central-1/       # Staging core, vault, atrax-api
│       └── eu-west-1/          # Staging atrax-node
└── modules/
    ├── core/                   # Core-group services
    │   ├── core-api/
    │   ├── atrax-api/          # atrax controller, lives on the core cluster
    │   ├── ecr/                # core-group ECR repos (atrax-api image)
    │   └── ecs/                # core ECS cluster (also hosts atrax-api)
    ├── atrax/                  # atrax-node only (region-isolated workers)
    │   ├── ecr/                # atrax-node ECR repo (eu-west-1)
    │   ├── ecs/                # atrax-node cluster: fixed EC2s + per-host EIPs
    │   └── node/               # atrax-node ECS service / task def
    ├── edge/                   # Edge CDN, Lambda@Edge, consent queue
    └── vault/                  # Vault analytics pipeline
        ├── clickhouse/
        ├── ecs/
        ├── etl/
        ├── grafana/
        └── vault-api/

Each environments/<env>/<region>/ directory is an independent root module with its own backend configuration. You run terraform init and terraform apply from within that directory.

State Backend

State is stored in per-account S3 buckets with encryption and locking:

Environment Region Bucket Key
prod eu-central-1 cookiehub-terraform-state-prod-759286286879 prod/eu-central-1/terraform.tfstate
prod eu-west-1 cookiehub-terraform-state-prod-759286286879 prod/eu-west-1/terraform.tfstate
stage eu-central-1 cookiehub-terraform-state-stage-258618559895 stage/eu-central-1/terraform.tfstate
stage eu-west-1 cookiehub-terraform-state-stage-258618559895 stage/eu-west-1/terraform.tfstate

The state buckets themselves both live in eu-central-1 regardless of which region's resources they describe — one bucket per environment, multiple state keys for multi-region environments.

Both use use_lockfile = true and encrypt = true (AES256).

Naming Convention

All resource names follow the pattern {env}-{region_short}-{group}-{component}:

stage-euc1-core-ecs-cluster
prod-euc1-vault-clickhouse
stage-euc1-atrax-atrax-api

This is generated from locals.tf in each root module:

locals {
  region_short = {
    eu-central-1 = "euc1"
    eu-west-1    = "euw1"
  }

  name_prefix = "${var.environment}-${local.region_short}-${var.group}"

  base_tags = {
    Environment = var.environment
    Region      = var.region
    Group       = var.group
    ManagedBy   = "terraform"
  }
}

The name_prefix and base_tags are passed down to every module.

Module Conventions

Each module follows a standard file layout:

File Purpose
main.tf Resource definitions
variables.tf Input variables
outputs.tf Output values
versions.tf Provider constraints
data.tf Data sources (when needed)

Modules are small and purpose-specific. A typical service module manages:

  • ECS task definition (with SSM secrets)
  • ALB target group and listener rules
  • Route53 DNS record
  • IAM roles and security group
  • CloudWatch log group

All modules receive common variables: name_prefix, environment, region, group, vpc_id, subnet_ids, and base_tags.

How to Apply Changes

Prerequisites

  • AWS CLI configured with named profiles for each account
  • Terraform >= 1.6.0 installed

Workflow

# 1. Set the right AWS profile
export AWS_PROFILE=stage  # or prod

# 2. Navigate to the environment
cd environments/stage/eu-central-1

# 3. Initialize (first time or after adding modules)
terraform init

# 4. Review changes
terraform plan

# 5. Apply
terraform apply

Manual applies only

There is no CI pipeline for Terraform yet. All changes are applied locally. Always run terraform plan first and review the output carefully before applying.

Environment Differences

Production Stage
AWS Account 759286286879 258618559895
Regions eu-central-1 + eu-west-1 (atrax-node) eu-central-1 + eu-west-1 (atrax-node)
Vault services Yes Yes
Atrax services Yes (api in eu-central-1, node in eu-west-1) Yes (same shape as prod)
Core API Not yet (TF stubbed, commented out) Yes
Public ALB Yes (atrax-api.cookiehub.net) + internal Yes (multiple hosts)
Core ECS host t3.small t3.small
Atrax ECS host r6a.xlarge (1 instance, 2 tasks) t3.medium (1 instance, 1 task)

What's Not in Terraform

Some resources were created manually and don't have corresponding .tf files:

  • GitHub Actions OIDC provider and deploy roles — created via console.
  • Pre-existing S3 buckets in prod (e.g. cookiehub-cdn, cookiehub-consent, cookiehub-s3-access-logs) and the atrax-stage-* buckets in stage — referenced by data sources or by ARN in policies, but not adopted under TF management.