Deployment Guide

Instructions for deploying the three-tier chat application to AWS.


Deployment Overview

The application can be deployed to AWS using the provided infrastructure code in base-infra/.

Deployed Architecture

flowchart TB
    subgraph CDN["CloudFront CDN"]
        CF[S3 Static Files<br/>HTTPS/SSL<br/>Global Edge]
    end

    subgraph Compute["Backend Services"]
        AppRunner[App Runner<br/>Flask Backend<br/>JWT Forwarding]
    end

    subgraph APIs["API Layer"]
        APIGW[API Gateway<br/>+ Lambda<br/>Secured API]
        Agent[Bedrock AgentCore<br/>AI Agent<br/>USER_FEDERATION]
    end

    CDN -->|HTTPS| Compute
    Compute --> APIGW
    Compute --> Agent
    Agent -.->|Federated JWT| APIGW

CDK Stacks

Stack Component Resources
three-tier-chat-api-dev Secured API HTTP API Gateway, Lambda
three-tier-chat-agentcore-dev AI Agent AgentCore Runtime, Gateway, Interceptor
three-tier-chat-backend-dev Backend App Runner (Flask)
three-tier-chat-app-dev Frontend S3, CloudFront

Prerequisites

AWS Account Setup

  1. AWS Account with appropriate permissions
  2. AWS CLI installed and configured
  3. AWS CDK installed:
    npm install -g aws-cdk
    

Environment Variables

Configure production environment variables:

  • Okta: Production Okta application
  • AWS: Production AWS account
  • Secrets: Store in AWS Secrets Manager

Quick Start: Single-Click Deploy

For fast deployment, use the automated deploy script:

Option 1: Environment Variables

export OKTA_DOMAIN=your-domain.okta.com
export OKTA_CLIENT_ID=your_okta_client_id
./deploy.sh

Option 2: Command Line Parameters

./deploy.sh --okta-domain your-domain.okta.com --okta-client-id your_client_id

Option 3: Environment File

# Create config file
cp .env.deploy.example .env.deploy
# Edit .env.deploy with your values
./deploy.sh --env-file .env.deploy

Deploy Script Options

./deploy.sh --help

Options:
  --okta-domain DOMAIN      Okta domain
  --okta-client-id ID       Okta client ID
  --region REGION           AWS region (default: us-east-1)
  --environment ENV         Environment name (default: dev)
  --env-file FILE           Load configuration from file
  --skip-lambda-build       Skip Lambda package build
  --skip-cdk                Skip CDK deployment
  --skip-target             Skip Gateway Target creation

The script automatically:

  1. Builds Lambda package with Docker (correct architecture)
  2. Deploys all CDK stacks
  3. Creates/updates Gateway Target via CLI

Manual Deployment Steps

1. Frontend Deployment (S3 + CloudFront)

Build Frontend:

cd frontend

# Install dependencies
npm install

# Build production bundle
npm run build
# Creates build/ directory with optimized files

Deploy to S3:

# Create S3 bucket (if not exists)
aws s3 mb s3://your-frontend-bucket

# Upload build files
aws s3 sync build/ s3://your-frontend-bucket/

# Set public read permissions
aws s3api put-bucket-policy --bucket your-frontend-bucket --policy '{
  "Statement": [{
    "Effect": "Allow",
    "Principal": "*",
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::your-frontend-bucket/*"
  }]
}'

Configure CloudFront:

# Use CDK or create manually in AWS Console
cd ../base-infra/cdk

# Deploy CDK stack
cdk deploy FrontendStack

Update Okta Configuration:

  • Add CloudFront URL to Okta redirect URIs
  • Update CORS settings in backend

2. Backend Deployment (App Runner via CDK)

The backend is deployed as a Docker container on AWS App Runner. CDK handles this automatically.

Manual Testing (Local Docker):

cd backend

# Build Docker image
docker build -t backend-flask .

# Run locally
docker run -p 8080:8080 \
  -e OKTA_ISSUER=https://your-domain.okta.com/oauth2/default \
  -e OKTA_CLIENT_ID=your_client_id \
  -e SECURED_API_URL=http://localhost:5001 \
  backend-flask

CDK Deployment:

cd base-infra/cdk
npx cdk deploy three-tier-chat-backend-dev \
  -c oktaDomain=your-domain.okta.com \
  -c oktaClientId=your_client_id

The CDK stack creates:

  • ECR repository for Docker images
  • App Runner service with auto-scaling
  • IAM roles for AgentCore invocation

3. Secured API Deployment (API Gateway + Lambda)

Build Lambda Package:

The Lambda package must be built with Docker to ensure dependencies (like native extensions) are compiled for Amazon Linux:

cd api

# Build using Docker (recommended)
./build-lambda.sh

# This creates lambda-package/ with:
#   - All Python dependencies (compiled for Amazon Linux x86_64)
#   - Source code (api.py, lambda_handler.py, user_database.py)

Deploy via CDK:

cd ../base-infra/cdk
npx cdk deploy three-tier-chat-api-dev \
  -c oktaDomain=your-domain.okta.com \
  -c oktaClientId=your_client_id

Note: JWT signature verification is handled by API Gateway. The Lambda only decodes the JWT payload to extract claims, avoiding the need for cryptography library (which has native dependencies).

4. AgentCore Deployment

AgentCore deployment involves two steps:

  1. CDK Deployment - Gateway, Runtime, IAM roles (automated)
  2. Gateway Target Setup - Must be done via CLI (manual, one-time)

Step 1: Deploy CDK Stack

cd base-infra/cdk

# Install dependencies
npm install

# Deploy AgentCore stack (requires Okta configuration)
npx cdk deploy three-tier-chat-agentcore-dev \
  -c oktaDomain=your-domain.okta.com \
  -c oktaClientId=your_okta_client_id

CDK creates:

  • AgentCore Gateway (with CUSTOM_JWT authorizer)
  • AgentCore Runtime (with container image)
  • Interceptor Lambda
  • IAM roles and policies

CDK does NOT create:

  • Gateway Target (requires token-vault which is CLI-only)

Step 2: Create Gateway Target (Manual - One Time)

Why manual? The CfnGatewayTarget CDK construct requires credentialProviderConfigurations with a providerArn pointing to a bedrock-agentcore token-vault resource. Token-vault resources can only be created via the agentcore CLI, not CloudFormation/CDK.

After CDK deployment, create the gateway target:

# Get values from CDK outputs
GATEWAY_ARN="arn:aws:bedrock-agentcore:us-east-1:ACCOUNT_ID:gateway/GATEWAY_ID"
GATEWAY_URL="https://GATEWAY_ID.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp"
ROLE_ARN="arn:aws:iam::ACCOUNT_ID:role/three-tier-chat-gateway-role"
API_URL="https://API_ID.execute-api.us-east-1.amazonaws.com"

# Prepare OpenAPI schema with server URL
SCHEMA_STRING=$(cat base-infra/cdk/lib/schemas/user-profile-openapi.json | \
  jq -c ". + {servers: [{url: \"$API_URL\"}]}")

# Create gateway target
agentcore gateway create-mcp-gateway-target \
  --gateway-arn "$GATEWAY_ARN" \
  --gateway-url "$GATEWAY_URL" \
  --role-arn "$ROLE_ARN" \
  --name "UserProfileAPI" \
  --target-type "openApiSchema" \
  --target-payload "{\"inlinePayload\": $(echo "$SCHEMA_STRING" | jq -R -s .)}" \
  --credentials '{"api_key": "dummy-value", "credential_location": "HEADER", "credential_parameter_name": "X-Dummy-Key"}' \
  --region us-east-1

Updating Gateway Target (When OpenAPI Schema Changes)

If you modify the OpenAPI schema, delete and recreate the target:

# List targets to get TARGET_ID
agentcore gateway list-mcp-gateway-targets \
  --id GATEWAY_ID \
  --region us-east-1

# Delete existing target
agentcore gateway delete-mcp-gateway-target \
  --id GATEWAY_ID \
  --target-id TARGET_ID \
  --region us-east-1

# Recreate with updated schema (same command as above)

Verify Gateway Target

# List targets
agentcore gateway list-mcp-gateway-targets \
  --id GATEWAY_ID \
  --region us-east-1

# Should show: status: READY

Update Backend Environment

# Update backend .env with AgentCore ARN from CDK output
AGENTCORE_AGENT_ARN=arn:aws:bedrock-agentcore:us-east-1:ACCOUNT_ID:runtime/AGENT_ID
AGENTCORE_REGION=us-east-1

Using CDK for Deployment

Install CDK Dependencies

cd base-infra/cdk
npm install

Deploy All Stacks

# Bootstrap CDK (first time only)
cdk bootstrap aws://ACCOUNT_ID/REGION

# Deploy all stacks
cdk deploy --all

# Or deploy individually
cdk deploy FrontendStack
cdk deploy BackendStack
cdk deploy ApiStack
cdk deploy AgentCoreStack

CDK Stacks

Stack Resources Created Notes
three-tier-chat-api-dev HTTP API Gateway, Secured API Lambda Fully CDK-managed
three-tier-chat-agentcore-dev Gateway, Runtime, Interceptor Lambda, IAM Gateway Target requires CLI
three-tier-chat-app-dev S3 bucket, CloudFront distribution Fully CDK-managed

CDK Limitations

Gateway Target cannot be managed via CDK due to:

  • Requires credentialProviderConfigurations with token-vault ARN
  • Token-vault resources are bedrock-agentcore specific (not in CloudFormation)
  • Must use agentcore CLI to create/update targets

See AgentCore Deployment for manual target setup instructions.


Configuration for Production

1. Secrets Management

Move secrets to AWS Secrets Manager:

# Store Okta credentials
aws secretsmanager create-secret \
  --name chat-app/okta-credentials \
  --secret-string '{
    "client_id":"your_client_id",
    "client_secret":"your_client_secret",
    "issuer":"https://your-domain.okta.com/oauth2/default"
  }'

# Store backend secrets
aws secretsmanager create-secret \
  --name chat-app/backend-secrets \
  --secret-string '{
    "secret_key":"your_backend_secret_key",
    "api_secret":"your_api_secret"
  }'

Update Lambda environment variables:

# Reference secret ARN instead of plain text
SECRET_ARN=arn:aws:secretsmanager:region:account:secret:chat-app/okta-credentials

2. Enable Production Features

Backend & API:

# Set environment variable
FLASK_ENV=production

# Or in Lambda environment
Environment:
  Variables:
    FLASK_ENV: production

CORS:

# Update ALLOWED_ORIGINS
ALLOWED_ORIGINS=https://your-cloudfront-domain.cloudfront.net

3. Monitoring & Logging

Enable CloudWatch Logs:

# Logs automatically enabled for Lambda
# Check in AWS Console → CloudWatch → Log Groups

Set up Alarms:

# Create CloudWatch alarms for:
# - Lambda errors
# - API Gateway 4xx/5xx errors
# - CloudFront error rate

Post-Deployment Verification

Health Checks

Frontend:

curl https://your-cloudfront-domain.cloudfront.net
# Should return React app HTML

Backend (via CloudFront or API Gateway):

curl https://your-backend-url/api/health
# Expected: {"status":"healthy"}

Secured API:

curl https://your-api-gateway-url/api/health
# Expected: {"status":"healthy","service":"secured-api"}

Test Authentication Flow

  1. Open CloudFront URL in browser
  2. Click “Login with Okta”
  3. Authenticate
  4. Verify profile loads
  5. Test chat functionality

Rollback Procedures

CDK Rollback

# Destroy specific stack
cdk destroy BackendStack

# Redeploy previous version
git checkout <previous-commit>
cdk deploy BackendStack

Lambda Rollback

# List versions
aws lambda list-versions-by-function --function-name chat-backend

# Update alias to previous version
aws lambda update-alias \
  --function-name chat-backend \
  --name production \
  --function-version <previous-version>

Monitoring & Maintenance

CloudWatch Dashboards

Create dashboard for:

  • Lambda invocations
  • API Gateway requests
  • Error rates
  • Response times
  • CloudFront cache hit rate

Logs

Lambda Logs:

# Stream logs
aws logs tail /aws/lambda/chat-backend --follow

API Gateway Logs:

# Enable logging in API Gateway settings
# View in CloudWatch Log Groups

Costs

Monitor AWS costs:

  • Lambda invocations
  • API Gateway requests
  • CloudFront data transfer
  • Bedrock AgentCore usage
  • S3 storage

Troubleshooting

Common Issues

CORS Errors:

  • Check ALLOWED_ORIGINS includes CloudFront domain
  • Verify API Gateway CORS configuration
  • Check CloudFront origin settings

Authentication Fails:

  • Verify Okta redirect URIs include CloudFront URL
  • Check JWT validation in Lambda logs
  • Ensure OKTA_ISSUER is correct

AgentCore Errors:

  • Verify agent ARN in backend .env matches CDK output
  • Check CloudWatch logs for the Runtime

MCP Client Initialization Error:

{"error": "the client initialization failed", "error_type": "MCPClientInitializationError"}
  • Cause: Gateway Target not configured or has outdated schema
  • Fix: Create/update gateway target via CLI (see AgentCore Deployment section)
  • Verify target status is READY:
    agentcore gateway list-mcp-gateway-targets --id GATEWAY_ID --region us-east-1
    

Gateway Target Issues:

  • Target shows only 1 tool: OpenAPI schema may be outdated, recreate target
  • Target status CREATING: Wait for it to become READY
  • Target status FAILED: Check OpenAPI schema validity

Lambda Timeout:

  • Increase Lambda timeout (default 3s → 30s recommended)
  • Check CloudWatch logs for slow operations

Security Checklist for Production

  • Secrets moved to AWS Secrets Manager
  • FLASK_ENV=production
  • CORS restricted to production domain
  • HTTPS enforced (CloudFront)
  • Lambda execution role follows least privilege
  • CloudWatch Logs enabled
  • CloudWatch Alarms configured
  • Rate limiting enabled
  • Input validation added
  • CSRF protection enabled
  • Security headers verified
  • Penetration testing completed

Useful Commands

# CDK
cdk diff               # Preview changes
cdk deploy --all       # Deploy all stacks
cdk destroy --all      # Remove all resources

# Lambda
aws lambda update-function-code --function-name chat-backend --zip-file fileb://backend-lambda.zip
aws lambda invoke --function-name chat-backend output.json

# S3
aws s3 sync build/ s3://your-bucket/ --delete
aws cloudfront create-invalidation --distribution-id ID --paths "/*"

# Secrets Manager
aws secretsmanager get-secret-value --secret-id chat-app/okta-credentials

Last Updated: 2026-01-21 For detailed deployment scripts, see: base-infra/scripts/