Skip to content

Deployment with AWS CDK

Note: This documentation was generated with AI assistance.

Introduction

AWS CDK provides an infrastructure-as-code path for deploying CodeMetrics to AWS. The CDK packages under deployment/aws/cdk/ provision the backend Lambda, frontend (S3 + CloudFront), optional Cognito auth, DynamoDB cache tables, and (for demos) an Imposter Go mocks stack.

This is distinct from the SAM/Lambda packaging guide, which covers publishing a pre-built Lambda zip. Use CDK when you want the surrounding AWS resources (auth, datastore, frontend CDN, mocks) created and wired together from config.

When to choose CDK

Best suited for:

  • Full AWS demo or staging environments where you want Cognito, DynamoDB, CloudFront, and the API deployed together
  • Infrastructure as code managed alongside the application in this repository
  • Mock-backed demos that pair the native app stacks with the Imposter Go mocks stack

Consider alternatives if:

Architecture overview

Two CDK applications live under deployment/aws/cdk/:

Package Path What it deploys
Native deployment/aws/cdk/native Backend Lambda, async query SQS queue + processing Lambda, frontend on S3/CloudFront, optional Cognito, optional DynamoDB
Mocks deployment/aws/cdk/mocks Imposter Go Lambda behind API Gateway (demo data providers)

The native app synthesises several CloudFormation stacks (application registry, datastore, auth, backend, frontend) from a single config.yaml.

┌──────────────────────┐     ┌─────────────────────────────┐
│ Mocks stack (opt.)   │     │ Native stacks               │
│ Imposter Go Lambda   │◄────│ Backend Lambda              │
│ API Gateway          │     │  └► SQS queue ─► Query Lambda│
└──────────────────────┘     │ Frontend S3 + CloudFront    │
                              │ Cognito + DynamoDB (opt.)   │
                              └─────────────────────────────┘

Async query requests are enqueued by the backend Lambda onto the SQS queue and executed by the dedicated query-processing Lambda (same code asset, INVOCATION_MODE=execute-query); results are stored in the asyncQueryResults DynamoDB table.

For the end-to-end mock-backed demo (CI, GitHub Actions, Imposter Go version pinning), see the Demo instance deployment runbook.

Prerequisites

  • AWS CLI configured for the target account
  • Node.js 20+
  • AWS CDK CLI (npm install -g aws-cdk)
  • Target account/region CDK-bootstrapped: bash npx cdk bootstrap aws://ACCOUNT_ID/REGION
  • Staged application artifacts under deployment/dist/ (see deployment/README.md)

Configuration (config.yaml)

CDK infrastructure settings live in YAML next to each CDK app — not in cdk.json.

  • Native: deployment/aws/cdk/native/config.yaml
  • Mocks: deployment/aws/cdk/mocks/config.yaml

cdk.json is reserved for CDK feature-flag context. bin/cdk.ts / bin/mocks.ts load config.yaml at synth/deploy time.

CodeMetrics application config (remote-config.yaml, workloads, license, RBAC, etc.) is not stored inside the CDK package. Stage it into deployment/dist/codemetrics-api/config before deploy (see deployment/README.md). The backend Lambda asset path points at that staged directory.

Native config.yaml highlights

aws:
  region: # optional; omit to use ambient AWS credentials/region
  account: # optional

global:
  name: CodeMetrics
  environment: dev # required — scopes stack ids and resource names (see "Environments")

frontend:
  # Optional: frontend.auth.provided.{user,pass} publishes a login hint into config.json
  s3:
    # bucketName: optional override; default is '<resourcePrefix>-frontend'
    # ...

backend:
  features:
    dora: true
    languageSelector: true
    mlForecasts: true
    predictions: true
    temporalCoupling: true
  memorySize: 1024
  timeout: 180
  environment:
    AccessTokenSecret: "" # required — deploy fails if left empty
    CORSAllowedOrigin: "*"
    # ...

datastore:
  dynamodb:
    create: true
    # tableName: optional table-prefix override; default is the resource prefix
    # ...

auth:
  cognito:
    create: true
    # Optional: path to a demo users file (see demo-users.yaml.example in the
    # CDK app directory); empty = no seeding
    createDemoUsers: ""
    autoWireRedirectUrls:
      true # default on — a deploy-time custom resource points the OAuth
      # callback/logout URLs at this environment's CloudFront domain
    # Managed manually only when autoWireRedirectUrls is false:
    # callbackUrls:
    #   - "https://myapp.com/home"
    # logoutUrls:
    #   - "https://myapp.com/logout"
Key Purpose
aws.account / aws.region Optional explicit deploy target
global.environment Required environment slug scoping all stack ids and resource names
frontend.s3.bucketName Optional override; default is <resourcePrefix>-frontend
datastore.dynamodb.tableName Optional table-prefix override; default is the resource prefix
backend.environment.AccessTokenSecret Required JWT signing secret — set by the deployer; cdk synth/deploy fails if empty (checked-in default is empty)
backend.memorySize / backend.timeout Lambda memory (MB) and timeout (seconds) — applied to both the backend and query-processing Lambdas, and used as the async query queue visibility timeout
backend.environment.asyncQueryResultTtl TTL (seconds) for async query results in the asyncQueryResults DynamoDB table; default 3600
backend.features.* Feature flags passed into the backend Lambda environment
frontend.auth.provided Optional login hint in generated frontend config.json
auth.cognito.create Whether to create a Cognito user pool
auth.cognito.createDemoUsers Optional path to a demo users file (see demo-users.yaml.example); empty = no seeding
auth.cognito.autoWireRedirectUrls When true (default), each deploy sets the OAuth callback/logout URLs to this environment's CloudFront domain; false manages them via callbackUrls/logoutUrls
auth.cognito.callbackUrls / logoutUrls Cognito OAuth redirect URLs (used only when auto-wiring is disabled)
datastore.dynamodb.create Whether to create DynamoDB cache tables

Enabling Cognito demo users

Seeding is disabled by default. To turn it on for a throwaway environment, copy demo-users.yaml.example (in the CDK app directory) to a git-ignored demo-users.yaml and set auth.cognito.createDemoUsers to its path. On every deploy the Auth stack seeds the listed users (verified email, permanent password), and the user list is a CloudFormation property, so editing the file re-runs the seeding on the next deploy — existing users keep their account and their password is reset from the file. cdk synth fails fast if the file is missing, invalid, or contains no valid users. Keep staged backend rbac.yaml aligned with those usernames.

Do not commit real passwords or access-token secrets into the checked-in CDK config.yaml (or the demo users file, which is git-ignored for the same reason).

Mocks config.yaml highlights

global:
  name: CodeMetricsMock
  environment: dev # required — scopes the CloudFormation stack id (e.g. CodeMetricsMock-dev)

mocks:
  lambdaAssetPath: "../../../dist/imposter-go-lambda"
  useAPIGW: true
  memorySize: 512
  timeout: 30

Environments

Multiple named environments (e.g. prod, dev) can be deployed from the same configuration, even into the same AWS account, because every environment-scoped resource name is derived from global.environment.

  • global.environment is required in both CDK apps and must match ^[a-z0-9]+(-[a-z0-9]+)*$ (values are lower-cased). Synth fails fast with a descriptive error when it is missing or invalid.
  • Two prefixes are derived from global.name + global.environment:
  • stackPrefix = <name>-<env> (e.g. CodeMetrics-dev) — CloudFormation stack ids, Cognito pool/client names, AppRegistry name, Secrets Manager path.
  • resourcePrefix = stackPrefix.toLowerCase() (e.g. codemetrics-dev) — S3 bucket names and the DynamoDB table prefix (which the backend composes as <tablePrefix>_<table>).
  • frontend.s3.bucketName and datastore.dynamodb.tableName remain optional overrides; when set they are used verbatim and must be unique per environment (S3 bucket names are globally unique across AWS).
Resource prod dev
CFN stacks (native) CodeMetrics-prod-<Stack> CodeMetrics-dev-<Stack>
CFN stack (mocks) CodeMetricsMock-prod CodeMetricsMock-dev
S3 frontend bucket codemetrics-prod-frontend codemetrics-dev-frontend
S3 CloudFront log bucket codemetrics-prod-logging codemetrics-dev-logging
DynamoDB tables codemetrics-prod_<table> codemetrics-dev_<table>
Backend DATABASE_NAME codemetrics-prod codemetrics-dev
Cognito user pool / client CodeMetrics-prod-CognitoPool / -Client CodeMetrics-dev-CognitoPool / -Client
AppRegistry application CodeMetrics-prod CodeMetrics-dev
Secrets Manager path (IAM) secret:CodeMetrics-prod/* secret:CodeMetrics-dev/*
Tags on every resource Application: CodeMetrics, Environment: prod Application: CodeMetrics, Environment: dev

Deploying a specific environment

  • The checked-in default is environment: dev, so a deploy without CODEMETRICS_ENV targets dev, never prod.
  • Deploy a different environment without editing YAML via the CODEMETRICS_ENV environment variable (precedence: env var > config file). make deploy-aws-cdk-* targets pass it through automatically:

bash cd deployment CODEMETRICS_ENV=prod make deploy-aws-cdk-mocks CODEMETRICS_ENV=prod make deploy-aws-cdk-native

  • Teardown works the same way — cdk destroy --all only finds the stacks the current environment synthesises:

bash cd deployment CODEMETRICS_ENV=dev make destroy-aws-cdk-native-all CODEMETRICS_ENV=dev make destroy-aws-cdk-mocks-all

  • GitHub Actions: the Demo instance deployment workflow deploys prod on pushes to main. Manual runs take a non-production environment slug and default to dev. The companion Destroy workflow tears down one named non-production slug from workflow_dispatch; it shares the per-environment concurrency group and cannot target dev or prod.

GitHub OIDC for team workflows

The CDK packages and release artifacts do not require consumers to copy the CodeMetrics demo workflow. Teams can implement their own workflow and use deployment/aws/oidc-trust-role/setup-oidc-trust-role.sh to configure GitHub OIDC access to AWS.

The helper accepts the team's production slug and branch. For example:

deployment/aws/oidc-trust-role/setup-oidc-trust-role.sh \
  --repo owner/deployment-repo \
  --prod-environment live \
  --prod-branch release

The resulting trust policy permits the live STS role session only from release; other session names can be assumed from other branches in that repository. A consuming workflow must use the same environment expression for role-session-name and CODEMETRICS_ENV. No GitHub Environment object is required or created.

The role session name is caller-supplied, so this convention does not by itself provide AWS resource isolation. Use separate roles and resource-scoped policies when production and non-production require a hard IAM boundary.

Deploy (native application)

Stage backend/frontend artifacts, then deploy:

cd deployment

# Build or download application artifacts, then stage config
make get-dev          # or: make get-release && make extract && make copy-config

# Customise deployment options
# edit deployment/aws/cdk/native/config.yaml

make deploy-aws-cdk-native

The CDK deployment publishes the React frontend and generates frontend config.json from the deployed API URL — no manual S3 upload is required.

Outputs are written to deployment/native_output.json (including the CloudFront URL).

Deploy (mocks + demo app)

For a mock-backed demo (Imposter Go + rewritten remote-config URLs):

build-imposter-go-lambda downloads the pinned imposter-go release binary and the matching plugin-oidc-server plugin, then assembles them with the mocks/ config into deployment/dist/imposter-go-lambda. No Go toolchain is required.

cd deployment
make build-imposter-go-lambda
make deploy-aws-cdk-mocks

MOCKS_BASE_URL="https://<mock-api-id>.execute-api.<region>.amazonaws.com/" make prepare-demo-config
make deploy-aws-cdk-native

Full CI/GitHub Actions and IAM details: Demo instance deployment.

Teardown

cdk destroy --all only removes the stacks synthesised by the current global.environment, so set CODEMETRICS_ENV to the environment you want to tear down:

cd deployment
make destroy-aws-cdk-native-all           # default environment (dev)
CODEMETRICS_ENV=prod make destroy-aws-cdk-mocks-all   # tear down a prod deployment

Demo configs typically use RemovalPolicy.DESTROY for DynamoDB, S3, and Cognito so teardown is clean.

The GitHub Actions Destroy workflow is the guarded equivalent for custom non-production slugs. It inventories the selected stacks first, destroys native stacks before mocks, treats an already-absent environment as success, and leaves Secrets Manager values in place unless delete_secrets is selected. Owned secrets are names prefixed with CodeMetrics-<slug>/ and no contradictory tags; deletion uses a 7-day recovery window. Shared CodeMetrics/... secrets are retained. See Demo instance deployment — Destroy.