Kubernetes IRSA Deployment Guide
This guide covers deploying the Agentic Identity Broker to Amazon EKS using IAM Roles for Service Accounts (IRSA) for secure AWS resource access.
Overview
IRSA (IAM Roles for Service Accounts) enables Kubernetes pods to assume AWS IAM roles without requiring static credentials. This integration provides:
- Least privilege access: Each service account gets its own IAM role
- No credential management: No AWS keys stored in Kubernetes Secrets
- Audit trail: CloudTrail logs show which pods accessed AWS services
- Automatic credential rotation: AWS SDK handles credential refresh
- Fine-grained permissions: KMS and DynamoDB access scoped to specific resources
Architecture
┌─────────────────────────────────────────────────────────────┐
│ EKS Cluster │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Pod: agentic-identity-broker │ │
│ │ │ │
│ │ ServiceAccount: broker-sa │ │
│ │ Annotation: iam.amazonaws.com/role=RoleARN │ │
│ │ │ │
│ │ AWS SDK → STS AssumeRoleWithWebIdentity │ │
│ └──────────────────┬───────────────────────────────┘ │
│ │ │
│ │ Federated Trust (OIDC) │
└─────────────────────┼───────────────────────────────────────┘
│
▼
┌────────────────────────┐
│ AWS IAM Role │
│ (Created by CDK) │
│ │
│ Trust Policy: │
│ - OIDC Provider │
│ - Namespace │
│ - Service Account │
│ │
│ Permissions: │
│ - KMS Decrypt │
│ - DynamoDB R/W │
└───────┬───────────────┘
│
▼
┌────────────────────────────┐
│ Encryption Resources │
│ - KMS CMK │
│ - DynamoDB Table │
└────────────────────────────┘
Prerequisites
Before you deploy with IRSA, make sure that these prerequisites are available:
1. EKS Cluster with OIDC Provider
Your EKS cluster must have an OIDC identity provider configured:
# Check if OIDC provider exists
aws eks describe-cluster \
--name my-cluster \
--query 'cluster.identity.oidc.issuer' \
--output text
# Example output:
# https://oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE
If not configured, create the OIDC provider:
eksctl utils associate-iam-oidc-provider \
--cluster my-cluster \
--approve
2. Get OIDC Provider ARN and Subject Key
Extract the OIDC provider ARN and subject condition key for CDK deployment:
# Get cluster OIDC issuer
OIDC_ISSUER=$(aws eks describe-cluster \
--name my-cluster \
--query 'cluster.identity.oidc.issuer' \
--output text)
# Derive provider host from the issuer URL
OIDC_PROVIDER_HOST=${OIDC_ISSUER#https://}
# Get AWS account ID
ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
# Construct deploy-time context values
OIDC_PROVIDER_ARN="arn:aws:iam::${ACCOUNT_ID}:oidc-provider/${OIDC_PROVIDER_HOST}"
OIDC_SUBJECT_KEY="${OIDC_PROVIDER_HOST}:sub"
echo "OIDC Provider ARN: $OIDC_PROVIDER_ARN"
echo "OIDC Subject Key: $OIDC_SUBJECT_KEY"
These values are required alongside serviceAccountSubject for every cdk synth, cdk diff, and cdk deploy invocation.
3. Define Service Account Details
Choose your Kubernetes namespace and service account name:
export K8S_NAMESPACE="identity-broker"
export K8S_SERVICE_ACCOUNT="broker-sa"
4. Tools Required
- AWS CLI 2.x
- Go 1.27.1+ (CDK infrastructure is written in Go)
- Node.js 24+ with AWS CDK CLI (
npm install -g aws-cdk) - kubectl (configured for your EKS cluster)
- Helm 3.x
- jq (for JSON parsing)
Note: The CDK infrastructure is written in Go but uses the Node.js AWS CDK CLI to synthesize and deploy. The CLI invokes the Go code as configured in cdk.json.
Deployment Steps
Step 1: Deploy CDK Encryption Infrastructure
The CDK stack creates:
- KMS CMK for envelope encryption
- DynamoDB table for branch key caching
- IAM role with IRSA trust policy
- CloudWatch dashboard and alarms
1.1 Navigate to CDK Directory
cd infra/cdk
1.2 Install CDK Dependencies
The CDK infrastructure uses a Go/Node.js hybrid approach: the infrastructure code is written in Go, but deployed via the AWS CDK CLI (Node.js). Install both:
# Install Go dependencies
go mod tidy && go mod download
# Install AWS CDK CLI globally (or use npx if preferred)
npm install -g aws-cdk
# Alternative (if you don't want to install globally):
# npx aws-cdk@latest (then use npx cdk instead of cdk in subsequent commands)
1.3 Synthesize CloudFormation Template
The AWS CDK CLI will invoke the Go code to generate a CloudFormation template:
npx cdk synth \
-c env=prod \
-c oidcProviderArn="$OIDC_PROVIDER_ARN" \
-c oidcSubjectKey="$OIDC_SUBJECT_KEY" \
-c serviceAccountSubject="system:serviceaccount:${K8S_NAMESPACE}:${K8S_SERVICE_ACCOUNT}"
This command:
- Calls the Go CDK app (via
go run .as configured incdk.json) - Generates a CloudFormation template in
cdk.out/
Review the synthesized template: cdk.out/AgenticIdentityBrokerEncryptionVault-prod.template.json
1.4 Preview Infrastructure Changes
npx cdk diff \
-c env=prod \
-c oidcProviderArn="$OIDC_PROVIDER_ARN" \
-c oidcSubjectKey="$OIDC_SUBJECT_KEY" \
-c serviceAccountSubject="system:serviceaccount:${K8S_NAMESPACE}:${K8S_SERVICE_ACCOUNT}"
1.5 Deploy Stack
npx cdk deploy \
-c env=prod \
-c oidcProviderArn="$OIDC_PROVIDER_ARN" \
-c oidcSubjectKey="$OIDC_SUBJECT_KEY" \
-c serviceAccountSubject="system:serviceaccount:${K8S_NAMESPACE}:${K8S_SERVICE_ACCOUNT}"
Deployment takes approximately 3-5 minutes.
Step 2: Extract Stack Outputs
After successful deployment, extract the stack outputs needed for Helm configuration:
STACK_NAME="AgenticIdentityBrokerEncryptionVault-prod"
# Extract all outputs
aws cloudformation describe-stacks \
--stack-name $STACK_NAME \
--query 'Stacks[0].Outputs' \
--output table
# Extract specific outputs for Helm
export KMS_KEY_ARN=$(aws cloudformation describe-stacks \
--stack-name $STACK_NAME \
--query 'Stacks[0].Outputs[?OutputKey==`EncryptionKeyARN`].OutputValue' \
--output text)
export DYNAMODB_TABLE_NAME=$(aws cloudformation describe-stacks \
--stack-name $STACK_NAME \
--query 'Stacks[0].Outputs[?OutputKey==`BranchKeyTableName`].OutputValue' \
--output text)
export IAM_ROLE_ARN=$(aws cloudformation describe-stacks \
--stack-name $STACK_NAME \
--query 'Stacks[0].Outputs[?OutputKey==`EncryptionRoleARN`].OutputValue' \
--output text)
export IAM_ROLE_NAME=$(aws cloudformation describe-stacks \
--stack-name $STACK_NAME \
--query 'Stacks[0].Outputs[?OutputKey==`IamRoleName`].OutputValue' \
--output text)
# Verify outputs
echo "KMS Key ARN: $KMS_KEY_ARN"
echo "DynamoDB Table: $DYNAMODB_TABLE_NAME"
echo "IAM Role ARN: $IAM_ROLE_ARN"
echo "IAM Role Name: $IAM_ROLE_NAME"
Expected outputs:
EncryptionKeyARN: KMS key ARN (e.g.,arn:aws:kms:us-east-1:ACCOUNT:key/UUID)BranchKeyTableName: DynamoDB table name (e.g.,AgenticIdentityBrokerBranchKeys-prod)EncryptionRoleARN: IAM role ARN (e.g.,arn:aws:iam::ACCOUNT:role/AgenticIdentityBrokerEncryptionRole-prod)IamRoleName: IAM role name (e.g.,AgenticIdentityBrokerEncryptionRole-prod)
Step 3: Examine IAM role trust policy
Make sure that the IAM role has the required IRSA trust policy:
aws iam get-role \
--role-name $IAM_ROLE_NAME \
--query 'Role.AssumeRolePolicyDocument' \
--output json | jq .
Expected trust policy structure:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::ACCOUNT:oidc-provider/oidc.eks.REGION.amazonaws.com/id/EXAMPLEID"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.REGION.amazonaws.com/id/EXAMPLEID:sub": "system:serviceaccount:identity-broker:broker-sa",
"oidc.eks.REGION.amazonaws.com/id/EXAMPLEID:aud": "sts.amazonaws.com"
}
}
}
]
}
Step 4: Configure Helm Values for IRSA
Create a Helm values file (values-irsa.yaml) with IRSA configuration:
# Storage configuration
storage:
type: postgres # Use PostgreSQL for production
# PostgreSQL configuration (use external or operator)
postgresql:
external:
enabled: true
host: postgres.database.svc.cluster.local
port: 5432
database: broker
migrationSecretName: broker-db-migration
brokerSecretName: broker-db
# ServiceAccount configuration with IRSA
serviceAccount:
create: true
name: broker-sa
annotations: {}
# IRSA configuration
irsa:
enabled: true
role: "arn:aws:iam::ACCOUNT:role/AgenticIdentityBrokerEncryptionRole-prod" # Replace with actual ARN or role name
# Broker configuration (AWS encryption settings)
broker:
# Additional configuration passed to config.yaml
extraConfig:
encryption:
aws_kms:
key_arn: "arn:aws:kms:us-east-1:ACCOUNT:key/UUID" # Replace with actual ARN
dynamodb_table_name: "AgenticIdentityBrokerBranchKeys-prod" # Replace with actual table name
region: "us-east-1" # Replace with your region
branch_key_ttl: "1h"
# Production resource configuration
replicaCount: 3
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
# Autoscaling
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70
# Pod disruption budget
podDisruptionBudget:
enabled: true
minAvailable: 2
# Ingress configuration (adjust for your environment)
ingress:
enduser:
enabled: true
className: nginx
hosts:
- host: broker.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: broker-tls
hosts:
- broker.example.com
admin:
enabled: true
className: nginx
annotations:
nginx.ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/8"
hosts:
- host: broker-admin.internal.example.com
paths:
- path: /
pathType: Prefix
Automated Configuration:
You can generate this file automatically using the extracted outputs:
cat > values-irsa.yaml <<EOF
storage:
type: postgres
postgresql:
external:
enabled: true
host: postgres.database.svc.cluster.local
port: 5432
database: broker
migrationSecretName: broker-db-migration
brokerSecretName: broker-db
serviceAccount:
create: true
name: ${K8S_SERVICE_ACCOUNT}
irsa:
enabled: true
role: "${IAM_ROLE_ARN}"
broker:
extraConfig:
encryption:
aws_kms:
key_arn: "${KMS_KEY_ARN}"
dynamodb_table_name: "${DYNAMODB_TABLE_NAME}"
region: "us-east-1"
replicaCount: 3
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
EOF
Step 5: Create Kubernetes Namespace
Create the namespace specified in the CDK deployment:
kubectl create namespace ${K8S_NAMESPACE}
Step 6: Create PostgreSQL Secrets
Create database credentials (if using external PostgreSQL):
kubectl create secret generic broker-db-migration \
-n ${K8S_NAMESPACE} \
--from-literal=username=broker-migration \
--from-literal=password=SECURE_MIGRATION_PASSWORD
kubectl create secret generic broker-db \
-n ${K8S_NAMESPACE} \
--from-literal=username=broker \
--from-literal=password=SECURE_BROKER_PASSWORD
Step 7: Deploy with Helm
Deploy the broker with IRSA configuration:
cd charts/agentic-identity-broker
helm install broker . \
-n ${K8S_NAMESPACE} \
-f values-irsa.yaml
Step 8: Examine deployment
8.1 ServiceAccount annotation
Make sure that the ServiceAccount has the IRSA role annotation:
kubectl get serviceaccount ${K8S_SERVICE_ACCOUNT} \
-n ${K8S_NAMESPACE} \
-o yaml
Expected output:
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
iam.amazonaws.com/role: arn:aws:iam::ACCOUNT:role/AgenticIdentityBrokerEncryptionRole-prod
name: broker-sa
namespace: identity-broker
8.2 Pod status
Make sure that broker pods are running:
kubectl get pods -n ${K8S_NAMESPACE} -l app.kubernetes.io/name=agentic-identity-broker
Expected output:
NAME READY STATUS RESTARTS AGE
broker-agentic-identity-broker-xxxxx-yyyyy 1/1 Running 0 2m
broker-agentic-identity-broker-xxxxx-zzzzz 1/1 Running 0 2m
broker-agentic-identity-broker-xxxxx-wwwww 1/1 Running 0 2m
8.3 Pod environment
Make sure that the pod has AWS SDK environment variables for IRSA:
POD_NAME=$(kubectl get pods -n ${K8S_NAMESPACE} \
-l app.kubernetes.io/name=agentic-identity-broker \
--output jsonpath='{.items[0].metadata.name}')
kubectl exec -n ${K8S_NAMESPACE} $POD_NAME -- env | grep AWS
Expected output:
AWS_ROLE_ARN=arn:aws:iam::ACCOUNT:role/AgenticIdentityBrokerEncryptionRole-prod
AWS_WEB_IDENTITY_TOKEN_FILE=/var/run/secrets/eks.amazonaws.com/serviceaccount/token
AWS_REGION=us-east-1
The EKS Pod Identity Webhook adds these environment variables.
8.4 Application logs
Make sure that the application can access AWS resources:
kubectl logs -n ${K8S_NAMESPACE} $POD_NAME | grep -i kms
kubectl logs -n ${K8S_NAMESPACE} $POD_NAME | grep -i dynamodb
The logs must not contain AWS credential or permission errors.
8.5 Run Helm tests
Run Helm tests to examine deployment health:
helm test broker -n ${K8S_NAMESPACE}
Step 9: Basic health
Make sure that the broker runs and responds:
# Port-forward to broker service
kubectl port-forward -n ${K8S_NAMESPACE} svc/broker-agentic-identity-broker 8000:8000 &
# Test health endpoint
curl http://localhost:8000/health
For end-to-end OAuth2 and encryption tests, read the integration tests and API documentation.
Step 10: Monitor CloudWatch metrics
Examine encryption infrastructure metrics:
# View CloudWatch dashboard
aws cloudwatch get-dashboard \
--dashboard-name AgenticIdentityBroker-Encryption-prod
# Check KMS API call metrics
aws cloudwatch get-metric-statistics \
--namespace AWS/KMS \
--metric-name ApiCallCount \
--dimensions Name=KeyId,Value=$(echo $KMS_KEY_ARN | awk -F'/' '{print $2}') \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
--period 300 \
--statistics Sum
# Check DynamoDB read/write metrics
aws cloudwatch get-metric-statistics \
--namespace AWS/DynamoDB \
--metric-name ConsumedReadCapacityUnits \
--dimensions Name=TableName,Value=$DYNAMODB_TABLE_NAME \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
--period 300 \
--statistics Sum
Deployment checklist
After deployment, make sure that these conditions are true:
- CDK stack deployed successfully
- IAM role created with federated trust policy
- Trust policy includes correct OIDC provider ARN
- Trust policy condition keys match namespace and service account
- ServiceAccount has
iam.amazonaws.com/roleannotation - Pods have AWS_ROLE_ARN and AWS_WEB_IDENTITY_TOKEN_FILE env vars
- Application logs show successful AWS SDK initialization
- No credential or permission errors in logs
- Health endpoint responds successfully
- CloudWatch dashboard shows KMS and DynamoDB metrics
- KMS API calls visible in CloudWatch
- DynamoDB read/write operations recorded
Troubleshooting
Issue: Pod Cannot Assume IAM Role
Symptoms:
AccessDenied: User: sts:assumed-role/eks-node-role/i-xxxxx is not authorized to perform: sts:AssumeRoleWithWebIdentity
Diagnosis:
-
Examine the ServiceAccount annotation:
kubectl get sa ${K8S_SERVICE_ACCOUNT} -n ${K8S_NAMESPACE} -o yaml -
Examine the OIDC provider trust relationship:
aws iam get-role --role-name $IAM_ROLE_NAME \--query 'Role.AssumeRolePolicyDocument'
Solutions:
- Make sure that the ServiceAccount name matches the trust policy condition.
- Make sure that the namespace matches the trust policy condition.
- Make sure that the OIDC provider ARN is correct.
- Examine the pod ServiceAccount:
kubectl get pod $POD_NAME -n ${K8S_NAMESPACE} -o jsonpath='{.spec.serviceAccountName}'
Issue: KMS Access Denied
Symptoms:
KMS.AccessDeniedException: User: arn:aws:sts::ACCOUNT:assumed-role/AgenticIdentityBrokerEncryptionRole-prod/xxxxx is not authorized to perform: kms:Decrypt
Diagnosis:
Examine IAM role permissions:
aws iam list-attached-role-policies --role-name $IAM_ROLE_NAME
aws iam list-role-policies --role-name $IAM_ROLE_NAME
Solutions:
- Deploy the CDK stack again. It manages permissions.
- Make sure that the KMS key ARN in Helm values matches the CDK output.
- Make sure that the KMS key policy allows the role.
Issue: DynamoDB Access Denied
Symptoms:
DynamoDB.AccessDeniedException: User: arn:aws:sts::ACCOUNT:assumed-role/AgenticIdentityBrokerEncryptionRole-prod/xxxxx is not authorized to perform: dynamodb:GetItem
Diagnosis:
Examine DynamoDB table configuration:
aws dynamodb describe-table --table-name $DYNAMODB_TABLE_NAME
Solutions:
- Make sure that the DynamoDB table name in Helm values matches the CDK output.
- Make sure that the IAM role has DynamoDB permissions. The CDK stack manages these.
- Make sure that the table exists and is
ACTIVE.
Issue: OIDC Provider Not Found
Symptoms:
InvalidIdentityToken: No OpenIDConnect provider found
Diagnosis:
Make sure that the OIDC provider exists:
aws iam list-open-id-connect-providers
Solutions:
- Create the OIDC provider for the EKS cluster:
eksctl utils associate-iam-oidc-provider --cluster my-cluster --approve
- Make sure that the OIDC provider ARN matches the CDK input.
- Make sure that the EKS cluster has an OIDC identity provider.
Issue: Wrong Namespace or Service Account
Symptoms:
AssumeRoleWithWebIdentity: Condition not met (namespace mismatch)
Diagnosis:
Compare actual and expected values:
# Actual pod configuration
kubectl get pod $POD_NAME -n ${K8S_NAMESPACE} -o jsonpath='{.spec.serviceAccountName}'
kubectl get pod $POD_NAME -n ${K8S_NAMESPACE} -o jsonpath='{.metadata.namespace}'
# Expected subject used at deploy time (serviceAccountSubject)
# Format: system:serviceaccount:<namespace>:<service-account-name>
# Expected condition key: <oidc-provider-host>:sub (oidcSubjectKey)
Solutions:
- Make sure that Helm values match the CDK deployment parameters.
- Deploy the CDK stack again with the correct namespace and service account.
- Make sure that the pod is in the correct namespace.
- Make sure that the ServiceAccount name matches Helm values.
Security Best Practices
1. Principle of Least Privilege
The CDK stack creates IAM roles with minimal permissions:
- KMS: Only
Decrypton specific key (noEncryptneeded - hierarchical keyring handles that) - DynamoDB: Only
GetItemandPutItemon branch key table - No wildcard permissions
- No cross-account access
2. Namespace Isolation
Use separate namespaces for different environments:
# Development
kubectl create namespace identity-broker-dev
# Staging
kubectl create namespace identity-broker-staging
# Production
kubectl create namespace identity-broker-prod
Deploy separate CDK stacks with environment-specific IRSA roles.
3. Service Account Per Application
Do not share service accounts between applications. Each service must have:
- A dedicated ServiceAccount
- A dedicated IAM role
- A dedicated KMS key
- A dedicated DynamoDB table
4. Pod Security Standards
The Helm chart enforces restricted Pod Security Standards:
- Non-root user (UID 1000)
- Read-only root filesystem
- No privilege escalation
- All capabilities dropped
- Seccomp profile applied
5. Network Policies
Restrict network access to AWS services:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: broker-netpol
namespace: identity-broker
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: agentic-identity-broker
policyTypes:
- Egress
egress:
# Allow AWS API endpoints
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 443 # HTTPS for KMS, DynamoDB, STS
# Allow DNS
- to:
- namespaceSelector: {}
ports:
- protocol: UDP
port: 53
6. Audit Logging
Enable AWS CloudTrail to audit IRSA operations:
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=Username,AttributeValue=$IAM_ROLE_NAME \
--max-results 50
Monitor for:
AssumeRoleWithWebIdentityevents- KMS
Decryptoperations - DynamoDB
GetItem/PutItemoperations
CI/CD Integration
GitHub Actions Example
name: Deploy to EKS with IRSA
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: arn:aws:iam::ACCOUNT:role/GitHubActionsDeployRole
aws-region: us-east-1
- name: Deploy CDK Stack
run: |
cd infra/cdk
npm install -g aws-cdk
npx cdk deploy \
-c env=prod \
-c oidcProviderArn="${{ secrets.EKS_OIDC_PROVIDER_ARN }}" \
-c oidcSubjectKey="${{ secrets.EKS_OIDC_SUBJECT_KEY }}" \
-c serviceAccountSubject=system:serviceaccount:identity-broker:broker-sa \
--require-approval never
- name: Extract Stack Outputs
id: outputs
run: |
ROLE_ARN=$(aws cloudformation describe-stacks \
--stack-name AgenticIdentityBrokerEncryptionVault-prod \
--query 'Stacks[0].Outputs[?OutputKey==`EncryptionRoleARN`].OutputValue' \
--output text)
echo "role_arn=$ROLE_ARN" >> $GITHUB_OUTPUT
- name: Update kubeconfig
run: |
aws eks update-kubeconfig --name my-cluster --region us-east-1
- name: Deploy Helm Chart
run: |
helm upgrade --install broker ./charts/agentic-identity-broker \
-n identity-broker \
--set serviceAccount.irsa.enabled=true \
--set serviceAccount.irsa.role=${{ steps.outputs.outputs.role_arn }}
Cost Optimization
KMS Key Rotation
Enable automatic key rotation (already configured in CDK):
- KMS automatically rotates backing keys annually
- Application transparently uses old and new keys
- No downtime or redeployment required
DynamoDB On-Demand Pricing
The CDK stack uses on-demand billing for DynamoDB:
- No capacity planning required
- Pay only for requests
- Automatic scaling
- Suitable for variable workloads
CloudWatch Dashboard
Monitor costs in CloudWatch dashboard:
- KMS request volume
- DynamoDB read/write capacity
- Set alarms for unexpected usage spikes
Disaster Recovery
KMS Key Recovery
KMS keys have a 30-day pending deletion window:
# Cancel pending deletion (within 30 days)
aws kms cancel-key-deletion --key-id $KMS_KEY_ARN
DynamoDB Point-in-Time Recovery
The CDK stack enables PITR for DynamoDB:
# Restore to specific timestamp
aws dynamodb restore-table-to-point-in-time \
--source-table-name $DYNAMODB_TABLE_NAME \
--target-table-name ${DYNAMODB_TABLE_NAME}-restored \
--restore-date-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S)
Next Steps
- Production deployment checklist
- Plan KMS key management
- Define DynamoDB recovery procedures
- Set up monitoring and alerting
- Review security hardening requirements