AWS CDK infrastructure development with TypeScript...
aws-cdk-lib and constructsThe entire application is provisioned by one CDK stack (PlatformStack). Application code is shipped out-of-band via AWS APIs (ECR push ā ECS service update / Lambda code update / AgentCore Runtime update).
infrastructure/
āāā bin/infrastructure.ts # App entrypoint (instantiates PlatformStack)
āāā lib/
ā āāā platform-stack.ts # The one stack ā all infrastructure
ā āāā config.ts # Configuration loader & validator
ā āāā constructs/ # 39 reusable CDK constructs
ā āāā network/ # VPC, ALB, ECS cluster
ā āāā identity/ # Cognito, secrets, KMS, OAuth
ā āāā data/ # DynamoDB tables, file uploads
ā āāā rag/ # RAG documents, vectors
ā āāā rag-ingestion/ # RAG ingestion Lambda
ā āāā artifacts/ # Artifact rendering pipeline
ā āāā mcp-sandbox/ # MCP Apps sandbox proxy
ā āāā agentcore/ # Memory, Code Interpreter, Browser, Gateway
ā āāā inference-api/ # AgentCore Runtime
ā āāā app-api/ # Fargate service
ā āāā fine-tuning/ # SageMaker IAM
ā āāā spa/ # SPA CloudFront distribution
ā āāā zones/ # Route53, ALB DNS
āāā cdk.context.json # Configuration defaults
Key principle: CDK deploys are rare (infrastructure changes only). Day-to-day code changes deploy via backend.yml (AWS API calls, no CDK).
Use the centralized config system:
import { loadConfig, getResourceName, getStackEnv, applyStandardTags } from './config';
PlatformStack receives config via props:
const config = loadConfig(app);
new PlatformStack(app, `${config.projectPrefix}-PlatformStack`, { config, env });
For configuration patterns, see references/configuration.md.
Resource Names: Use getResourceName():
getResourceName(config, 'user-quotas') // "bsu-agentcore-user-quotas"
SSM Parameters: Hierarchical naming for runtime consumption:
/{projectPrefix}/{category}/{resource-type}
Categories: /network/, /quota/, /cost-tracking/, /auth/, /frontend/, /gateway/, /rag/, /artifacts/
Since everything is in one stack, use typed props ā not SSM:
// In PlatformStack:
const network = new NetworkConstruct(this, 'Network', { config });
new AlbConstruct(this, 'Alb', { config, vpc: network.vpc });
SSM parameters are published only for runtime consumption by ECS tasks and Lambdas ā never for CDK-to-CDK references within the same stack.
When a construct exposes a table/bucket that backend code reads via
os.environ.get("X_NAME", "default"), you must set X_NAME in the container
environment of every compute that runs that code ā thread the typed ref
through that compute's env builder (e.g. buildAppApiEnvironment for app-api,
the inference-agentcore construct's environment for inference-api). Wiring one
does not wire the other.
Why this bites (silent 502): the backend's default fallback hides the
omission. If the env var is missing, the code queries the default name
(e.g. "memory-spaces" instead of {prefix}-memory-spaces), the resource
isn't found, boto3 raises ResourceNotFoundException, and the centralized
handler (apis/shared/security/error_handler.py) maps it to a generic
502 {"detail":"Upstream service error."}. Nothing in cdk synth or CI
catches it ā the stack is valid, the IAM grant may even exist; only a runtime
read fails. (Real instance: PR #588 ā memory-spaces names were wired to
inference-api but not app-api, which owns the CRUD routes.)
Guard it: add an env-map unit test asserting the key is emitted (see
test/app-api-environment.test.ts). Mind the boundary: app-api owns
user-facing CRUD; granting IAM or wiring inference-api does not cover it.
PAY_PER_REQUEST billingFor table patterns, see references/dynamodb.md.
update-serviceFor service patterns, see references/ecs-fargate.md.
update-function-codeFor Lambda patterns, see references/lambda.md.
For bucket patterns, see references/s3.md.
For IAM patterns, see references/iam.md.
AgentCore Names: Use underscores, not hyphens:
name: getResourceName(config, 'memory').replace(/-/g, '_')
Secrets Manager ARN: Include wildcard for random suffix:
resources: [`${secret.secretArn}*`]
Removal Policy:
removalPolicy: getRemovalPolicy(config) // RETAIN in prod, DESTROY in dev
cd infrastructure
npm ci # Install dependencies
npx cdk synth # Synthesize CloudFormation
npx cdk deploy {prefix}-PlatformStack # Deploy
npx cdk diff # Preview changes