> For the complete documentation index, see [llms.txt](https://docs.caf.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.caf.io/caf-api/all-id/production-guidance/ecs.md).

# AWS ECS (CDK)

Complete guide for deploying All ID on AWS ECS Fargate using CDK.

## Overview

This guide explains how to deploy All ID using the AWS CDK (Cloud Development Kit) Infrastructure as Code.

The deployment requires:

* **All ID Application CDK** (`allid/`) - Application services (ECS, RDS, ALB)
* **Existing VPC** - You must provide a VPC ID in the CDK context

{% hint style="info" %}
**Network Requirements**: The All ID CDK requires an existing VPC with public, private, and isolated subnets. If you don't have a VPC yet, Certta provides a reference CDK project (`shared/`) that creates a production-ready network infrastructure.
{% endhint %}

{% hint style="success" %}
Certta will provide you with a package containing:

* All ID CDK project (required)
* Reference network infrastructure CDK project (optional, use if you need to create a new VPC)
* Configuration examples and documentation
  {% endhint %}

## Prerequisites

Before deploying, ensure you have:

**Tools**:

* Node.js >= 18.x
* AWS CLI configured with credentials
* AWS CDK >= 2.x (`npm install -g aws-cdk`)

**AWS Account**:

* AWS account with appropriate permissions
* Permissions to create: ECS, RDS, ALB, CloudFormation, IAM roles, Security Groups
* AWS credentials configured (`aws configure`)

**Network Infrastructure**:

* Existing VPC ID with:
  * Public subnets (minimum 2 AZs) for Application Load Balancer
  * Private subnets (minimum 2 AZs) for ECS services
  * Isolated subnets (minimum 2 AZs) for RDS database
  * Internet Gateway and NAT Gateway configured

{% hint style="info" %}
**Don't have a VPC?** Certta provides a reference CDK project that creates all required network infrastructure. See [Step 2: Network Infrastructure](#step-2-network-infrastructure-optional) below.
{% endhint %}

**Container Images**:

* Access to Certta's private container registry
* ECR repository URIs for Peer and Facematch images
* Image versions/tags to deploy

{% hint style="warning" %}
**Important**: Contact your Certta technical account manager to obtain:

* CDK project files (All ID + optional network infrastructure)
* Container registry credentials and image URIs
* Router Service certificates for mTLS communication
  {% endhint %}

## Project structure

Certta will provide you with CDK project files. The package structure:

```
allid-ecs-quickstart/
│
└── src/
    └── cdk/
        │
        ├── allid/                      # All ID application (required)
        │   ├── src/
        │   │   ├── main.ts                  # Main entry point (orchestrates all stacks)
        │   │   ├── stacks/
        │   │   │   ├── cluster-stack.ts         # ECS cluster + Service Discovery
        │   │   │   ├── database-stack.ts        # Aurora MySQL Serverless v2
        │   │   │   ├── load-balancer-stack.ts   # Application Load Balancer
        │   │   │   ├── peer-stack.ts            # Multi-tenant Peer Services (creates 3 peers)
        │   │   │   └── facematch-stack.ts       # Facematch Service (CPU: 1024, RAM: 2GB)
        │   │   └── constructs/
        │   │       ├── peer-construct.ts        # Single peer instance (used by peer-stack)
        │   │       ├── task-definition-construct.ts
        │   │       └── fargate-service-construct.ts
        │   ├── utils/                       # Helper utilities
        │   ├── constants.ts                 # Configuration constants
        │   ├── cdk.json                     # CDK configuration and context
        │   └── package.json
        │
        └── shared/                          # Network infrastructure (optional)
            ├── src/
            │   ├── stacks/
            │   │   └── network-stack.ts     # VPC, subnets, gateways
            │   └── utils/                   # Helper utilities
            ├── cdk.json                     # CDK configuration
            └── package.json
```

{% hint style="info" %}
**Shared infrastructure is optional**: Only deploy if you need to create a new VPC. If you already have a VPC, skip the `shared/` project and configure your VPC ID in `src/cdk/allid/cdk.json`.
{% endhint %}

## Step 1: Extract CDK project

Extract the CDK project files provided by Certta:

```bash
# Extract the package
tar -xzf allid-ecs-quickstart.tar.gz
cd allid-ecs-quickstart
```

## Step 2: Network infrastructure (optional)

{% hint style="warning" %}
**Skip this step if you already have a VPC**. Only deploy the shared infrastructure if you need to create a new VPC.
{% endhint %}

If you need to create a new VPC, deploy the reference network infrastructure:

```bash
# Navigate to shared project
cd src/cdk/shared

# Install dependencies
npm install

# Bootstrap CDK (first time only)
cdk bootstrap

# Set environment variables
export CDK_DEFAULT_ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
export CDK_DEFAULT_REGION=us-east-1

# Deploy network infrastructure
cdk deploy --all --require-approval never
```

**What gets deployed**:

* VPC with environment-specific CIDR blocks
* 2 Public subnets (across 2 availability zones)
* 2 Private subnets (across 2 availability zones)
* 2 Isolated subnets (across 2 availability zones) for databases
* Internet Gateway for public internet access
* NAT Gateway for private subnet outbound access
* VPC Endpoints for AWS services (S3, ECR, CloudWatch, Secrets Manager)

**Deployment time**: \~5-10 minutes

After deployment, get the VPC ID:

**Option 1: AWS Console**

1. Navigate to VPC Console → Your VPCs
2. Find the VPC created by the deployment (look for tags with prefix `shared`)
3. Copy the VPC ID (format: `vpc-xxxxxxxxxxxxx`)

**Option 2: AWS CLI**

```bash
# List all VPCs to find the one you just created
aws ec2 describe-vpcs --query 'Vpcs[*].[VpcId, Tags[?Key==`Name`].Value | [0], CidrBlock]' --output table
```

Save this VPC ID - you'll need it in the next step.

## Step 3: Configure CDK context

Edit `src/cdk/allid/cdk.json` to configure your deployment:

```bash
cd src/cdk/allid
```

**Update the context section** with your environment-specific values:

```json
{
  "context": {
    "prefix": "allid",
    "tags": {
      "business-unity-id": "allid",
      "workload-id": "allid",
      "cost-center": "engineering"
    },
    "us-east-1": {
      "dev": {
        "vpc-id": "vpc-xxxxxxxxxxxxx",
        "peer-ecr-repository-uri": "123456789012.dkr.ecr.us-east-1.amazonaws.com/peer-v2",
        "peer-version": "latest",
        "facematch-ecr-repository-uri": "123456789012.dkr.ecr.us-east-1.amazonaws.com/facematch",
        "facematch-version": "latest",
        "router-rest-url": "https://mtls.us.prd.caf.io/v1/allid"
      }
    }
  }
}
```

**Configuration values to update**:

| Field                          | Description                                | How to obtain                                         |
| ------------------------------ | ------------------------------------------ | ----------------------------------------------------- |
| `vpc-id`                       | Your VPC ID                                | From Step 2 (if deployed shared) or your existing VPC |
| `peer-ecr-repository-uri`      | Peer Service container registry            | Provided by Certta                                    |
| `peer-version`                 | Peer image tag to deploy                   | Provided by Certta (e.g., `latest`, commit hash)      |
| `facematch-ecr-repository-uri` | Facematch container registry               | Provided by Certta                                    |
| `facematch-version`            | Facematch image tag to deploy              | Provided by Certta (e.g., `latest`, commit hash)      |
| `router-rest-url`              | Certta Router endpoint for your region/env | Provided by Certta                                    |

{% hint style="info" %}
**Multiple environments**: You can configure multiple environments (dev, stg, prd) in the same `cdk.json` file. The CDK will use the context based on the region and environment variables.
{% endhint %}

## Step 4: Deploy All ID application

Deploy the All ID application services:

```bash
# Make sure you're in the allid directory
cd src/cdk/allid

# Install dependencies
npm install

# Bootstrap CDK (first time only)
cdk bootstrap

# Set environment variables
export CDK_DEFAULT_ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
export CDK_DEFAULT_REGION=us-east-1

# Review what will be created (optional)
cdk diff

# Deploy all application stacks
cdk deploy --all
```

**What gets deployed**:

1. **ECS Cluster + Service Discovery** (`ClusterStack`)
   * ECS Fargate cluster
   * Cloud Map private namespace (`allid.local`)
2. **Database** (`DatabaseStack`)
   * Aurora MySQL Serverless v2 cluster
   * Database security group
   * Secrets Manager for credentials (auto-generated)
3. **Load Balancer** (`LoadBalancerStack`)
   * Application Load Balancer (internet-facing)
   * HTTP listener (port 80)
   * Security group with IP whitelisting
4. **Facematch Service** (`FacematchStack`)
   * ECS Task Definition (CPU: 1024, Memory: 2048 MB)
   * Fargate Service with 2 instances
   * Cloud Map service registration (`facematch.allid.local`)
   * Security group for internal communication
5. **Peer Services** (`PeerStack`)
   * Creates 3 peer instances (default, client-a, client-b)
   * Each peer has:
     * ECS Task Definition (CPU: 256, Memory: 512 MB)
     * Fargate Service with 1 instance
     * ALB target group with path-based routing
     * Cloud Map service registration
     * Database connection to RDS
     * Router mTLS certificate secret (placeholder)

**Deployment time**: \~15-20 minutes

{% hint style="info" %}
The CDK automatically handles stack dependencies and deploys them in the correct order: cluster → database → load-balancer → facematch → peer.
{% endhint %}

{% hint style="warning" %}
**Important**: After deployment, the Peer Services will initially fail to start. You must complete Steps 5 and 6 (database initialization and Router certificates update) to make them operational. The services will automatically restart and become healthy once these steps are completed.
{% endhint %}

## Step 5: Initialize database

The Peer Service requires a database schema to be initialized. Certta will provide a SQL dump file that must be restored to the Aurora MySQL database.

{% hint style="danger" %}
**Critical**: The Peer Services are currently failing to start because the database schema is not initialized. Complete this step to allow the services to become operational.
{% endhint %}

{% hint style="info" %}
**Multi-tenant setup**: Each peer instance uses its own database. The database names follow the pattern `peer-{name}`:

* `peer-default` (for default peer)
* `peer-client-a` (for client-a peer)
* `peer-client-b` (for client-b peer)
  {% endhint %}

### Database access methods

The Aurora MySQL database is deployed in isolated subnets with no direct internet access. You need to establish a secure connection to access it.

**Option 1: Bastion Host (Recommended for production)**

Deploy a bastion host (EC2 instance) in a public subnet to act as a jump server:

1. Launch an EC2 instance in a public subnet of your VPC
2. Configure security groups to allow:
   * SSH access from your IP to the bastion host
   * MySQL access from bastion host to the RDS security group
3. Connect to the database through SSH tunnel:

```bash
# SSH tunnel to bastion host
ssh -i your-key.pem -L 3306:DATABASE_ENDPOINT:3306 ec2-user@BASTION_IP

# In another terminal, connect to database via localhost
mysql -h 127.0.0.1 -u DB_USERNAME -p
```

**Option 2: VPN Connection**

If you have a VPN connection configured to your VPC:

1. Connect to your VPN
2. Access the database directly using its private endpoint

**Option 3: AWS Systems Manager Session Manager**

Use Session Manager for secure access without exposing SSH ports:

1. Ensure your bastion host has SSM agent installed
2. Grant necessary IAM permissions
3. Create port forwarding session:

```bash
aws ssm start-session \
  --target INSTANCE_ID \
  --document-name AWS-StartPortForwardingSessionToRemoteHost \
  --parameters '{"portNumber":["3306"],"localPortNumber":["3306"],"host":["DATABASE_ENDPOINT"]}'
```

**Option 4: Temporary Public Access (Development Only)**

{% hint style="danger" %}
**Not recommended for production**: Only use this method for development/testing environments.
{% endhint %}

1. Temporarily modify the RDS security group to allow your IP
2. Make the RDS instance publicly accessible (requires modification)
3. Revert changes after database initialization

### Get database endpoint and credentials

**Option 1: AWS Console**

1. Navigate to RDS Console → Databases
2. Find the Aurora cluster (look for name with `allid-database`)
3. Copy the **Writer endpoint** (e.g., `allid-database-cluster.cluster-xxx.us-east-1.rds.amazonaws.com`)
4. Navigate to Secrets Manager Console → Secrets
5. Find the database secret (look for name with `allid-database`)
6. Click "Retrieve secret value" to see username and password

**Option 2: AWS CLI**

```bash
# List RDS clusters to find your database
aws rds describe-db-clusters --query 'DBClusters[*].[DBClusterIdentifier, Endpoint, Port]' --output table

# List secrets to find database credentials
aws secretsmanager list-secrets --query 'SecretList[*].[Name, ARN]' --output table

# Get specific secret value (replace SECRET_ARN with actual ARN from above)
aws secretsmanager get-secret-value --secret-id SECRET_ARN --query 'SecretString' --output text | jq
```

### Create and initialize databases

You can use any of the following tools to create databases and restore the dump:

#### Option A: MySQL Command Line Client

Best for automation and CI/CD pipelines:

```bash
# Connect to Aurora MySQL (replace with your endpoint, username, and password from above)
mysql -h {DB_ENDPOINT} -u {DB_USERNAME} -p

# Create databases
CREATE DATABASE IF NOT EXISTS `peer-default`;
CREATE DATABASE IF NOT EXISTS `peer-client-a`;
CREATE DATABASE IF NOT EXISTS `peer-client-b`;
EXIT;

# Restore the dump to each database (Certta will provide the SQL dump file)
mysql -h {DB_ENDPOINT} -u {DB_USERNAME} -p peer-default < allid-dump.sql
mysql -h {DB_ENDPOINT} -u {DB_USERNAME} -p peer-client-a < allid-dump.sql
mysql -h {DB_ENDPOINT} -u {DB_USERNAME} -p peer-client-b < allid-dump.sql
```

#### Option B: DBeaver (GUI Tool)

Recommended for visual database management:

1. **Create a new connection**:
   * Database: MySQL
   * Host: `{DB_ENDPOINT}` (from above)
   * Port: 3306
   * Username: `{DB_USERNAME}` (from Secrets Manager)
   * Password: `{DB_PASSWORD}` (from Secrets Manager)
2. **Create databases**:

   * Right-click on connection → SQL Editor → New SQL Script
   * Execute:

   ```sql
   CREATE DATABASE IF NOT EXISTS `peer-default`;
   CREATE DATABASE IF NOT EXISTS `peer-client-a`;
   CREATE DATABASE IF NOT EXISTS `peer-client-b`;
   ```
3. **Restore dump**:
   * Right-click on each database → Tools → Execute Script
   * Select the `allid-dump.sql` file provided by Certta
   * Click "Start" to execute

#### Option C: MySQL Workbench (GUI Tool)

Alternative GUI tool for MySQL management:

1. **Create a new connection**:
   * Connection Name: `All ID Database`
   * Hostname: `{DB_ENDPOINT}`
   * Port: 3306
   * Username: `{DB_USERNAME}`
   * Password: Store in Keychain/Vault
2. **Create databases**:

   * Open connection → Query tab
   * Execute:

   ```sql
   CREATE DATABASE IF NOT EXISTS `peer-default`;
   CREATE DATABASE IF NOT EXISTS `peer-client-a`;
   CREATE DATABASE IF NOT EXISTS `peer-client-b`;
   ```
3. **Import dump**:
   * Server → Data Import
   * Select "Import from Self-Contained File"
   * Choose `allid-dump.sql` file
   * Select target database (repeat for each: peer-default, peer-client-a, peer-client-b)
   * Click "Start Import"

#### Option D: phpMyAdmin (Web Interface)

If you have phpMyAdmin deployed in your environment:

1. Login to phpMyAdmin
2. Create databases using the "New" button
3. Select each database and use the "Import" tab to upload and execute the SQL dump file

{% hint style="info" %}
**Connection troubleshooting**: If you cannot connect to the database, ensure:

* You have established proper access (bastion host, VPN, or Session Manager)
* The RDS security group allows connections from your source
* The database endpoint and credentials are correct
  {% endhint %}

{% hint style="danger" %}
**Critical**: Each Peer Service will fail to start if its corresponding database is not initialized. Contact your Certta technical account manager to obtain the SQL dump file.
{% endhint %}

## Step 6: Update Router mTLS certificates

The Peer Services require mTLS certificates to communicate with Certta's Router Service. The CDK automatically created placeholder secrets during deployment. You must update these secrets with the actual certificates before the Peer Services can start successfully.

{% hint style="warning" %}
**Placeholder values**: The CDK created secrets with placeholder text `REPLACE_WITH_ACTUAL_PRIVATE_KEY` and `REPLACE_WITH_ACTUAL_CERTIFICATE`. The Peer Services are failing to start until you replace these with real certificates.
{% endhint %}

**Update certificate secrets for each peer instance**:

**Option 1: AWS Console** (recommended)

1. Navigate to Secrets Manager Console → Secrets
2. Find and click on each secret:
   * `allid-peer-default-router-certificate`
   * `allid-peer-client-a-router-certificate`
   * `allid-peer-client-b-router-certificate`
3. Click "Retrieve secret value" → "Edit"
4. Update the JSON with your actual certificates:

```json
{
  "private-key": "-----BEGIN PRIVATE KEY-----\nMIIE...content...\n-----END PRIVATE KEY-----",
  "certificate": "-----BEGIN CERTIFICATE-----\nMIID...content...\n-----END CERTIFICATE-----"
}
```

5. Save changes

**Option 2: AWS CLI**

```bash
# Update default peer certificate (replace with actual content)
aws secretsmanager update-secret \
  --secret-id allid-peer-default-router-certificate \
  --secret-string '{
    "private-key": "-----BEGIN PRIVATE KEY-----\nYOUR_KEY_HERE\n-----END PRIVATE KEY-----",
    "certificate": "-----BEGIN CERTIFICATE-----\nYOUR_CERT_HERE\n-----END CERTIFICATE-----"
  }'

# Update client-a peer certificate
aws secretsmanager update-secret \
  --secret-id allid-peer-client-a-router-certificate \
  --secret-string '{
    "private-key": "-----BEGIN PRIVATE KEY-----\nYOUR_KEY_HERE\n-----END PRIVATE KEY-----",
    "certificate": "-----BEGIN CERTIFICATE-----\nYOUR_CERT_HERE\n-----END CERTIFICATE-----"
  }'

# Update client-b peer certificate
aws secretsmanager update-secret \
  --secret-id allid-peer-client-b-router-certificate \
  --secret-string '{
    "private-key": "-----BEGIN PRIVATE KEY-----\nYOUR_KEY_HERE\n-----END PRIVATE KEY-----",
    "certificate": "-----BEGIN CERTIFICATE-----\nYOUR_CERT_HERE\n-----END CERTIFICATE-----"
  }'
```

{% hint style="danger" %}
**Critical**: Contact your Certta technical account manager to obtain:

* mTLS certificate files (private key and certificate) for your environment
* Proper formatting instructions for the certificates
  {% endhint %}

{% hint style="info" %}
**Certificate formatting**: If you have certificate files (`.pem` or `.key` files), you need to format them for JSON by replacing newlines with `\n`. The AWS Console handles this automatically when you paste multi-line certificates in plain text mode.

For CLI users, you can use text processing tools like `awk` or `sed` to format the certificates, or use the console for easier management.
{% endhint %}

{% hint style="success" %}
**Services will auto-recover**: Once you update the certificates, the Peer Services will automatically restart and become healthy within a few minutes. ECS will detect the configuration change and redeploy the tasks.
{% endhint %}

## Step 7: Retrieve endpoints

After deployment completes, get the application endpoint:

**Option 1: AWS Console**

1. Navigate to EC2 Console → Load Balancers
2. Find the load balancer (look for name with `allid-load-balancer`)
3. Copy the **DNS name** (e.g., `allid-load-balancer-123456789.us-east-1.elb.amazonaws.com`)

**Option 2: AWS CLI**

```bash
# List all load balancers to find yours
aws elbv2 describe-load-balancers --query 'LoadBalancers[*].[LoadBalancerName, DNSName]' --output table
```

{% hint style="info" %}
The deployment creates 3 peer instances with path-based routing for multi-tenant support. Each peer has its own isolated endpoint with path prefix.
{% endhint %}

**API endpoints** (3 peer instances by default):

```
# Default peer
http://{alb-dns}/default/v1/biometric-validation-responder

# Client A peer
http://{alb-dns}/client-a/v1/biometric-validation-responder

# Client B peer
http://{alb-dns}/client-b/v1/biometric-validation-responder
```

**Health check endpoints**:

```
http://{alb-dns}/default/status
http://{alb-dns}/client-a/status
http://{alb-dns}/client-b/status
```

{% hint style="warning" %}
To add/remove peers, edit `src/cdk/allid/src/stacks/peer-stack.ts` and modify the `peerConfigs` array. Remember to create the corresponding database and update the Router certificate secret for new peers BEFORE deploying.
{% endhint %}

## Step 8: Validate deployment

Test that all services are healthy:

**Test peer endpoints** (using ALB DNS from Step 7):

```bash
# Replace {URL} with your actual ALB DNS name
curl -f http://{URL}/default/status
curl -f http://{URL}/client-a/status
curl -f http://{URL}/client-b/status
```

All endpoints should return HTTP 200 with status information.

**Check ECS services status**:

**AWS Console**:

1. Navigate to ECS Console → Clusters → `allid-cluster`
2. Click on "Services" tab
3. Verify all services show "Running" status and desired count matches running count

**AWS CLI**:

```bash
# List all services in the cluster
aws ecs list-services --cluster allid-cluster --output table

# Check detailed status (replace service names if different)
aws ecs describe-services \
  --cluster allid-cluster \
  --services peer-default peer-client-a peer-client-b facematch \
  --query 'services[*].[serviceName, runningCount, desiredCount]' \
  --output table
```

All services should show `runningCount` matching `desiredCount`.

**Check CloudWatch Logs** (if services fail to start):

**AWS Console**:

1. Navigate to CloudWatch Console → Log groups
2. Find log groups: `/ecs/allid/peer-default`, `/ecs/allid/facematch`, etc.
3. Check recent log streams for errors

**AWS CLI**:

```bash
# List log groups
aws logs describe-log-groups --log-group-name-prefix /ecs/allid --output table

# Tail specific log (replace log group name)
aws logs tail /ecs/allid/peer-default --follow
```

## Architecture overview

### Network layers

The deployment creates a three-tier network architecture:

```
┌─────────────────────────────────────────┐
│ Public Subnets (2 AZs)                  │
│ • Application Load Balancer             │
│ • Internet Gateway                      │
└─────────────────┬───────────────────────┘
                  │
┌─────────────────▼───────────────────────┐
│ Private Subnets (2 AZs)                 │
│ • Peer Service (ECS Fargate)            │
│ • Facematch Service (ECS Fargate)       │
│ • NAT Gateway (outbound internet)       │
└─────────────────┬───────────────────────┘
                  │
┌─────────────────▼───────────────────────┐
│ Isolated Subnets (2 AZs)                │
│ • Aurora MySQL Database                 │
│ • No internet access                    │
└─────────────────────────────────────────┘
```

### Service communication

```
Internet → ALB (HTTP:80) → Peer (8080) → Facematch (8080)
                              ↓
                         Database (3306)
```

### Path-based routing (multi-tenant)

The ALB uses path-based routing to support multiple peer instances:

```
/default/*   → peer-default   (Target Group 1)
/client-a/*  → peer-client-a  (Target Group 2)
/client-b/*  → peer-client-b  (Target Group 3)
```

Each peer instance:

* Has its own ECS service
* Has its own isolated database (peer-default, peer-client-a, peer-client-b)
* Operates independently from other peers
* Shares the same Facematch service pool

## Configuration management

The CDK project automatically configures environment variables and secrets for all services.

**Environment variables** are defined in `src/cdk/allid/src/constructs/peer-construct.ts` and include:

* Database connection (host, port, database name)
* Facematch service endpoint
* Router Service URL
* Feature flags (RabbitMQ, Redis, Router communication)

**Secrets** are injected via ECS task secrets and include:

* Database credentials (auto-generated by CDK)
* Router mTLS certificates (created with placeholders, must be updated manually)

{% hint style="info" %}
See [Configuration](/caf-api/all-id/configuration.md) for complete list of environment variables, required values, and configuration details.
{% endhint %}

### Secrets management

The CDK automatically creates secrets in AWS Secrets Manager:

**Secret naming convention**:

* Database credentials: `allid-database-secret-<random-suffix>`
* Router certificates: `allid-peer-{name}-router-certificate` (e.g., `allid-peer-default-router-certificate`)

{% hint style="warning" %}
Router certificate secrets are created with placeholder values during deployment. You must update them with real certificates before the Peer Services can start successfully (see Step 6).
{% endhint %}

## Security

The CDK automatically configures security groups and IP whitelisting following least-privilege principles.

**Security groups**:

* ALB accepts traffic only from whitelisted IPs
* Peer Services accept traffic only from ALB and internal VPC
* Facematch accepts traffic only from Peer Services
* Database accepts traffic only from Peer Services

**IP whitelisting**:

* ALB is configured to accept traffic only from Certta Router IP addresses
* Configure additional IPs in `src/cdk/allid/src/stacks/load-balancer-stack.ts`

{% hint style="info" %}
See [Security Best Practices](/caf-api/all-id/security-best-practices.md) for:

* Complete list of Certta Router IP addresses
* Network segmentation recommendations
* Secrets management best practices
* Additional security hardening options
  {% endhint %}

## Updating the deployment

### Update container images

1. Update image versions in `src/cdk/allid/cdk.json`:

```json
{
  "context": {
    "us-east-1": {
      "dev": {
        "peer-version": "new-commit-hash",
        "facematch-version": "new-commit-hash"
      }
    }
  }
}
```

2. Redeploy affected stacks:

```bash
cd src/cdk/allid

# Redeploy Peer Services (deploys all 3 peers)
cdk deploy allid-peer

# Redeploy Facematch Service
cdk deploy allid-facematch
```

### Update environment variables

1. Edit variables in `src/cdk/allid/src/constructs/peer-construct.ts`
2. Redeploy the Peer stack:

```bash
cd src/cdk/allid
cdk deploy allid-peer
```

This will update all peer instances (default, client-a, client-b) with the new configuration.

### Scale services

**Scale Peer Services**: Edit desired count in `src/cdk/allid/src/constructs/peer-construct.ts`:

```typescript
desiredCount: 2, // Change from 1 to 2
minHealthyPercent: 50,
maxHealthyPercent: 200,
```

Then redeploy:

```bash
cdk deploy allid-peer
```

**Scale Facematch Service**: Edit desired count in `src/cdk/allid/src/stacks/facematch-stack.ts`:

```typescript
desiredCount: 4, // Change from 2 to 4
```

Then redeploy:

```bash
cdk deploy allid-facematch
```

### Add/remove peer instances

To add a new peer instance:

1. **Edit the CDK configuration** in `src/cdk/allid/src/stacks/peer-stack.ts`:

```typescript
const peerConfigs: PeerConfig[] = [
  { name: "default" },
  { name: "client-a" },
  { name: "client-b" },
  { name: "client-c" },  // Add new peer
];
```

2. **Deploy the updated Peer stack**:

```bash
cdk deploy allid-peer
```

The CDK will automatically create the Router certificate secret with placeholder values.

3. **Create and initialize the database** for the new peer (see Step 5):

```bash
mysql -h {DB_ENDPOINT} -u {DB_USERNAME} -p

CREATE DATABASE IF NOT EXISTS `peer-client-c`;
EXIT;

# Restore the dump
mysql -h {DB_ENDPOINT} -u {DB_USERNAME} -p peer-client-c < allid-dump.sql
```

4. **Update the Router certificate secret** with real certificates (see Step 6):

```bash
aws secretsmanager update-secret \
  --secret-id allid-peer-client-c-router-certificate \
  --secret-string '{
    "private-key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----",
    "certificate": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
  }'
```

{% hint style="info" %}
The new Peer Service will fail to start initially (like in the initial deployment), but will automatically become healthy after you complete steps 3 and 4 (database initialization and certificate update).
{% endhint %}

## Troubleshooting

### Deployment fails with "VPC not found"

**Cause**: VPC ID not configured correctly in `cdk.json` or VPC doesn't exist.

**Solution**:

1. **Verify VPC ID**:
   * AWS Console: Navigate to VPC Console → Your VPCs
   * Look for the VPC you want to use and copy its VPC ID
2. **If you don't have a VPC**, deploy the shared infrastructure (see Step 2):

```bash
cd src/cdk/shared
npm install
cdk bootstrap
cdk deploy --all --require-approval never
```

3. **Get the VPC ID**:
   * AWS Console: VPC Console → Your VPCs → find your VPC
   * AWS CLI: List all VPCs:

```bash
aws ec2 describe-vpcs --query 'Vpcs[*].[VpcId, CidrBlock, Tags[?Key==`Name`].Value | [0]]' --output table
```

4. **Update `src/cdk/allid/cdk.json`** with the correct VPC ID:

```json
{
  "context": {
    "us-east-1": {
      "dev": {
        "vpc-id": "vpc-xxxxxxxxxxxxx"
      }
    }
  }
}
```

5. **Redeploy**:

```bash
cd src/cdk/allid
cdk deploy --all --require-approval never
```

### Peer Service won't start

**Check CloudWatch Logs**:

**AWS Console**:

1. CloudWatch Console → Log groups
2. Find `/ecs/allid/peer-default` (or peer-client-a, peer-client-b)
3. Click on latest log stream
4. Look for error messages in the logs

**AWS CLI**:

```bash
# Tail logs for specific peer
aws logs tail /ecs/allid/peer-default --follow
```

**Common causes**:

1. **Database not initialized**: See Step 5 (database initialization and dump restoration)
2. **Router certificates not updated**: See Step 6 (update secrets with real certificates) - the CDK creates secrets with placeholder values that must be replaced
3. **Database connection failed**: Check security groups and RDS endpoint
4. **Facematch service unavailable**: Check Facematch service status

**Check task stopped reason**:

**AWS Console**:

1. ECS Console → Clusters → allid-cluster
2. Click on service (e.g., peer-default)
3. Go to "Tasks" tab → Click on stopped tasks
4. Check "Stopped reason" field

### Health check failures

**Check ECS service status**:

**AWS Console**:

1. ECS Console → Clusters → allid-cluster → Services
2. Check each service status
3. Look at "Events" tab for recent messages

**Check ALB target health**:

**AWS Console**:

1. EC2 Console → Target Groups
2. Find target groups with `allid` prefix
3. Click on each target group
4. Go to "Targets" tab
5. Check health status of registered targets (should be "healthy")

**AWS CLI**:

```bash
# List target groups
aws elbv2 describe-target-groups --query 'TargetGroups[?contains(TargetGroupName, `allid`)][TargetGroupName, TargetGroupArn]' --output table

# Check health (replace TARGET_GROUP_ARN with actual ARN from above)
aws elbv2 describe-target-health --target-group-arn TARGET_GROUP_ARN
```

**Common issues**:

* Security groups blocking traffic between ALB and Peer Services
* Service not registered in Cloud Map (DNS resolution fails)
* Database connection errors (check credentials secret)
* Facematch service not responding

### Can't access ALB

**Check if your IP is whitelisted**:

```bash
# Replace {URL} with your actual ALB DNS
curl -v http://{URL}/default/status
```

If you get connection timeout or 403 Forbidden, your IP is not whitelisted.

**Check ALB security group**:

**AWS Console**:

1. EC2 Console → Load Balancers → Find your ALB
2. Click on "Security" tab
3. Click on the security group
4. Check "Inbound rules" - verify your IP is allowed

**Add your IP to the allowlist**:

1. Edit `src/cdk/allid/src/stacks/load-balancer-stack.ts`:

```typescript
const allowedCidrBlocks = [
  // Add your IP to the list
  "YOUR_IP_HERE/32",
];
```

2. Redeploy:

```bash
cd src/cdk/allid
cdk deploy allid-load-balancer
```

{% hint style="info" %}
See [Security Best Practices](/caf-api/all-id/security-best-practices.md) for the complete list of Certta Router IP addresses that should be whitelisted.
{% endhint %}

## Cleanup

To remove all resources and stop incurring costs:

```bash
# Destroy All ID application (in dependency order)
cd src/cdk/allid

cdk destroy allid-peer
cdk destroy allid-facematch
cdk destroy allid-load-balancer
cdk destroy allid-database
cdk destroy allid-cluster

# Destroy shared infrastructure (only if you deployed it)
cd ../shared
cdk destroy --all
```

{% hint style="danger" %}
**Warning**: This permanently deletes all resources including:

* Aurora MySQL database and all data
* CloudWatch logs
* Secrets Manager secrets (database credentials, Router certificates)
* ECS services and tasks

Ensure you have backups before destroying. Database snapshots are NOT automatically created during destroy.
{% endhint %}

**Create database snapshot before cleanup** (optional):

```bash
aws rds create-db-cluster-snapshot \
  --db-cluster-identifier allid-database-cluster \
  --db-cluster-snapshot-identifier allid-final-snapshot-$(date +%Y%m%d)
```

## Next steps

* Review [Technical Requirements](/caf-api/all-id/technical-requirements.md) for resource specifications
* Review [Configuration](/caf-api/all-id/configuration.md) for environment variable details
* Review [Security Best Practices](/caf-api/all-id/security-best-practices.md) for hardening


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.caf.io/caf-api/all-id/production-guidance/ecs.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
