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
- AWS Account with appropriate permissions
- AWS CLI installed and configured
- 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:
- Builds Lambda package with Docker (correct architecture)
- Deploys all CDK stacks
- 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:
- CDK Deployment - Gateway, Runtime, IAM roles (automated)
- 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
CfnGatewayTargetCDK construct requirescredentialProviderConfigurationswith aproviderArnpointing 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
credentialProviderConfigurationswith token-vault ARN - Token-vault resources are bedrock-agentcore specific (not in CloudFormation)
- Must use
agentcoreCLI 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
- Open CloudFront URL in browser
- Click “Login with Okta”
- Authenticate
- Verify profile loads
- 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_ORIGINSincludes 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
.envmatches 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 becomeREADY - 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/