AWSTemplateFormatVersion: "2010-09-09" Description: "CloudFormation template for Customer Support System with DynamoDB tables, SSM parameters, and synthetic data" Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Lambda Code Configuration Parameters: - LambdaS3Bucket - LambdaS3Key Parameters: LambdaS3Bucket: Description: The name of S3 bucket which contains lambda code Type: String MinLength: 1 LambdaS3Key: Description: The S3 object key which contains customer support assistant code in zip format Type: String MinLength: 1 Resources: RuntimeAgentCoreRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - bedrock-agentcore.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: BedrockAgentPolicy PolicyDocument: Version: "2012-10-17" Statement: - Sid: ECRImageAccess Effect: Allow Action: - ecr:BatchGetImage - ecr:GetDownloadUrlForLayer Resource: - !Sub arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/bedrock-agentcore-customersupport* - Effect: Allow Action: - logs:DescribeLogStreams - logs:CreateLogGroup Resource: - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/bedrock-agentcore/runtimes/* - Effect: Allow Action: - logs:DescribeLogGroups Resource: - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:* - Effect: Allow Action: - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:* - Sid: ECRTokenAccess Effect: Allow Action: - ecr:GetAuthorizationToken Resource: "*" - Effect: Allow Action: - xray:PutTraceSegments - xray:PutTelemetryRecords - xray:GetSamplingRules - xray:GetSamplingTargets Resource: "*" - Effect: Allow Action: - cloudwatch:PutMetricData Resource: "*" Condition: StringEquals: cloudwatch:namespace: bedrock-agentcore - Sid: GetAgentAccessToken Effect: Allow Action: - bedrock-agentcore:GetWorkloadAccessToken - bedrock-agentcore:GetWorkloadAccessTokenForJWT - bedrock-agentcore:GetWorkloadAccessTokenForUserId Resource: - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default/workload-identity/customersupport* - Sid: ProvisionedThroughputModelInvocation Effect: Allow Action: - bedrock:InvokeModel - bedrock:InvokeModelWithResponseStream Resource: - "arn:aws:bedrock:*::foundation-model/*" - !Sub arn:aws:bedrock:${AWS::Region}:${AWS::AccountId}:* - Sid: SSMGetparam Effect: Allow Action: - ssm:GetParameter Resource: - !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/app/customersupport/* - Sid: Identity Effect: Allow Action: - bedrock-agentcore:GetResourceOauth2Token Resource: - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:token-vault/default/oauth2credentialprovider/customersupport* - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default/workload-identity/customersupport* - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:token-vault/default - Sid: SecretManager Effect: Allow Action: - secretsmanager:GetSecretValue Resource: - !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:bedrock-agentcore-identity!default/oauth2/customersupport* - Sid: AgentCoreMemory Effect: Allow Action: - bedrock-agentcore:ListMemories - bedrock-agentcore:ListMemoryRecords - bedrock-agentcore:RetrieveMemoryRecords - bedrock-agentcore:GetMemory - bedrock-agentcore:GetMemoryRecord - bedrock-agentcore:CreateEvent - bedrock-agentcore:GetEvent - bedrock-agentcore:ListEvents Resource: - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:memory/customersupport* GatewayAgentCoreRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - bedrock-agentcore.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: BedrockAgentPolicy PolicyDocument: Version: "2012-10-17" Statement: - Sid: InvokeFunction Effect: Allow Action: - lambda:InvokeFunction Resource: - !GetAtt CustomerSupportLambda.Arn # DynamoDB Table for Warranty Information WarrantyTable: Type: AWS::DynamoDB::Table Properties: BillingMode: PAY_PER_REQUEST AttributeDefinitions: - AttributeName: serial_number AttributeType: S - AttributeName: customer_id AttributeType: S KeySchema: - AttributeName: serial_number KeyType: HASH GlobalSecondaryIndexes: - IndexName: customer-index KeySchema: - AttributeName: customer_id KeyType: HASH Projection: ProjectionType: ALL PointInTimeRecoverySpecification: PointInTimeRecoveryEnabled: true Tags: - Key: Application Value: CustomerSupport - Key: CostCenter Value: CustomerSupport # DynamoDB Table for Customer Profiles CustomerProfileTable: Type: AWS::DynamoDB::Table Properties: BillingMode: PAY_PER_REQUEST AttributeDefinitions: - AttributeName: customer_id AttributeType: S - AttributeName: email AttributeType: S - AttributeName: phone AttributeType: S KeySchema: - AttributeName: customer_id KeyType: HASH GlobalSecondaryIndexes: - IndexName: email-index KeySchema: - AttributeName: email KeyType: HASH Projection: ProjectionType: ALL - IndexName: phone-index KeySchema: - AttributeName: phone KeyType: HASH Projection: ProjectionType: ALL PointInTimeRecoverySpecification: PointInTimeRecoveryEnabled: true Tags: - Key: Application Value: CustomerSupport - Key: CostCenter Value: CustomerSupport # Lambda function to populate synthetic data PopulateDataFunction: Type: AWS::Lambda::Function Properties: Runtime: python3.12 Handler: index.lambda_handler Role: !GetAtt PopulateDataRole.Arn Timeout: 120 Code: ZipFile: | import boto3 import json import cfnresponse from datetime import datetime, timedelta import random import uuid from decimal import Decimal def lambda_handler(event, context): try: if event['RequestType'] == 'Create': dynamodb = boto3.resource('dynamodb') warranty_table_name = event['ResourceProperties']['WarrantyTableName'] customer_table_name = event['ResourceProperties']['CustomerTableName'] warranty_table = dynamodb.Table(warranty_table_name) customer_table = dynamodb.Table(customer_table_name) # Customer profile data customer_data = [ { 'customer_id': 'CUST001', 'first_name': 'John', 'last_name': 'Smith', 'email': 'john.smith@email.com', 'phone': '+1-555-0101', 'address': { 'street': '123 Main Street', 'city': 'New York', 'state': 'NY', 'zip_code': '10001', 'country': 'USA' }, 'date_of_birth': '1985-03-15', 'registration_date': '2022-11-20', 'tier': 'Premium', 'communication_preferences': { 'email': True, 'sms': True, 'phone': False }, 'support_cases_count': 2, 'total_purchases': 3, 'lifetime_value': 2850.00, 'notes': 'VIP customer, prefers email communication' }, { 'customer_id': 'CUST002', 'first_name': 'Sarah', 'last_name': 'Johnson', 'email': 'sarah.johnson@email.com', 'phone': '+1-555-0102', 'address': { 'street': '456 Oak Avenue', 'city': 'Los Angeles', 'state': 'CA', 'zip_code': '90210', 'country': 'USA' }, 'date_of_birth': '1990-07-22', 'registration_date': '2023-03-15', 'tier': 'Standard', 'communication_preferences': { 'email': True, 'sms': False, 'phone': True }, 'support_cases_count': 1, 'total_purchases': 1, 'lifetime_value': 1299.99, 'notes': 'Tech-savvy customer, quick to resolve issues' }, { 'customer_id': 'CUST003', 'first_name': 'Mike', 'last_name': 'Davis', 'email': 'mike.davis@email.com', 'phone': '+1-555-0103', 'address': { 'street': '789 Pine Street', 'city': 'Chicago', 'state': 'IL', 'zip_code': '60601', 'country': 'USA' }, 'date_of_birth': '1988-12-03', 'registration_date': '2023-08-10', 'tier': 'Gold', 'communication_preferences': { 'email': True, 'sms': True, 'phone': True }, 'support_cases_count': 0, 'total_purchases': 2, 'lifetime_value': 549.98, 'notes': 'Audio enthusiast, interested in premium products' }, { 'customer_id': 'CUST004', 'first_name': 'Emily', 'last_name': 'Brown', 'email': 'emily.brown@email.com', 'phone': '+1-555-0104', 'address': { 'street': '321 Elm Drive', 'city': 'Houston', 'state': 'TX', 'zip_code': '77001', 'country': 'USA' }, 'date_of_birth': '1992-04-18', 'registration_date': '2022-09-05', 'tier': 'Standard', 'communication_preferences': { 'email': True, 'sms': False, 'phone': False }, 'support_cases_count': 3, 'total_purchases': 1, 'lifetime_value': 399.99, 'notes': 'Fitness enthusiast, uses wearables frequently' }, { 'customer_id': 'CUST005', 'first_name': 'Robert', 'last_name': 'Wilson', 'email': 'robert.wilson@email.com', 'phone': '+1-555-0105', 'address': { 'street': '654 Maple Lane', 'city': 'Phoenix', 'state': 'AZ', 'zip_code': '85001', 'country': 'USA' }, 'date_of_birth': '1983-09-11', 'registration_date': '2023-10-12', 'tier': 'Premium', 'communication_preferences': { 'email': False, 'sms': True, 'phone': True }, 'support_cases_count': 1, 'total_purchases': 1, 'lifetime_value': 699.99, 'notes': 'Gaming enthusiast, prefers phone support' } ] # Warranty data with customer_id references warranty_data = [ { 'serial_number': 'ABC12345678', 'customer_id': 'CUST001', 'product_name': 'SmartPhone Pro Max 128GB', 'purchase_date': '2023-01-15', 'warranty_end_date': '2025-01-15', 'warranty_type': 'Extended Warranty', 'coverage_details': 'Full coverage including accidental damage, water damage, and manufacturer defects', 'purchase_price': 1299.99, 'store_location': 'New York - 5th Avenue' }, { 'serial_number': 'DEF98765432', 'customer_id': 'CUST002', 'product_name': 'Laptop Ultra 15.6"', 'purchase_date': '2023-06-20', 'warranty_end_date': '2024-06-20', 'warranty_type': 'Standard Warranty', 'coverage_details': 'Hardware defects and manufacturing issues covered. Software support included', 'purchase_price': 1299.99, 'store_location': 'Los Angeles - Beverly Hills' }, { 'serial_number': 'GHI11111111', 'customer_id': 'CUST003', 'product_name': 'Wireless Headphones Elite', 'purchase_date': '2024-02-10', 'warranty_end_date': '2026-02-10', 'warranty_type': 'Premium Warranty', 'coverage_details': 'Comprehensive coverage including battery replacement, driver issues, and cosmetic damage', 'purchase_price': 299.99, 'store_location': 'Chicago - Michigan Avenue' }, { 'serial_number': 'JKL22222222', 'customer_id': 'CUST004', 'product_name': 'Smart Watch Series X', 'purchase_date': '2022-12-05', 'warranty_end_date': '2023-12-05', 'warranty_type': 'Standard Warranty', 'coverage_details': 'Hardware and software defects covered. Water resistance guaranteed', 'purchase_price': 399.99, 'store_location': 'Houston - Galleria' }, { 'serial_number': 'MNO33333333', 'customer_id': 'CUST005', 'product_name': 'Gaming Console Pro', 'purchase_date': '2023-11-25', 'warranty_end_date': '2024-11-25', 'warranty_type': 'Gaming Warranty', 'coverage_details': 'Controller issues, overheating protection, and hard drive replacement covered', 'purchase_price': 699.99, 'store_location': 'Phoenix - Scottsdale' }, { 'serial_number': 'PQR44444444', 'customer_id': 'CUST001', 'product_name': 'Tablet Air 10.9"', 'purchase_date': '2024-03-12', 'warranty_end_date': '2025-03-12', 'warranty_type': 'Standard Warranty', 'coverage_details': 'Screen defects, battery issues, and charging port problems covered', 'purchase_price': 599.99, 'store_location': 'New York - 5th Avenue' }, { 'serial_number': 'STU55555555', 'customer_id': 'CUST001', 'product_name': 'Smart TV 65" OLED', 'purchase_date': '2023-08-30', 'warranty_end_date': '2025-08-30', 'warranty_type': 'Extended Warranty', 'coverage_details': 'Panel replacement, smart features, sound system, and remote control covered', 'purchase_price': 1999.99, 'store_location': 'New York - 5th Avenue' }, { 'serial_number': 'VWX66666666', 'customer_id': 'CUST003', 'product_name': 'Bluetooth Speaker Pro', 'purchase_date': '2024-01-08', 'warranty_end_date': '2026-01-08', 'warranty_type': 'Audio Warranty', 'coverage_details': 'Driver replacement, battery issues, and waterproofing covered', 'purchase_price': 249.99, 'store_location': 'Chicago - Michigan Avenue' } ] # Insert customer data with customer_table.batch_writer() as batch: for item in customer_data: item = json.loads(json.dumps(item), parse_float=Decimal) batch.put_item(Item=item) # Insert warranty data with warranty_table.batch_writer() as batch: for item in warranty_data: item = json.loads(json.dumps(item), parse_float=Decimal) batch.put_item(Item=item) print(f"Successfully populated {len(customer_data)} customer profiles") print(f"Successfully populated {len(warranty_data)} warranty records") cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) else: cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) except Exception as e: print(f"Error: {str(e)}") cfnresponse.send(event, context, cfnresponse.FAILED, {}) # IAM Role for Lambda function PopulateDataRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: DynamoDBWritePolicy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - dynamodb:PutItem - dynamodb:BatchWriteItem Resource: - !GetAtt WarrantyTable.Arn - !GetAtt CustomerProfileTable.Arn - PolicyName: AllowBasicLogs PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: arn:aws:logs:*:*:* # Custom resource to trigger Lambda function PopulateData: Type: AWS::CloudFormation::CustomResource Properties: ServiceToken: !GetAtt PopulateDataFunction.Arn WarrantyTableName: !Ref WarrantyTable CustomerTableName: !Ref CustomerProfileTable DependsOn: - WarrantyTable - CustomerProfileTable # Lambda target CustomerSupportLambdaRole: Type: AWS::IAM::Role Properties: Path: /service-role/ ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: lambda.amazonaws.com Policies: - PolicyName: CustomerProfileAccessPolicy PolicyDocument: Version: "2012-10-17" Statement: - Sid: AllowReadCustomerTableNameFromSSM Effect: Allow Action: ssm:GetParameter Resource: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter${CustomerProfileTableNameParameter} - Sid: AllowReadCustomerProfileTable Effect: Allow Action: - dynamodb:GetItem - dynamodb:Query - dynamodb:DescribeTable Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${CustomerProfileTable} - Sid: AllowReadCustomerTableIndexes Effect: Allow Action: - dynamodb:Query Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${CustomerProfileTable}/index/* - PolicyName: WarrantyCheckAccessPolicy PolicyDocument: Version: "2012-10-17" Statement: - Sid: AllowReadWarrantyTableNameFromSSM Effect: Allow Action: ssm:GetParameter Resource: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter${WarrantyTableNameParameter} - Sid: AllowReadWarrantyTable Effect: Allow Action: - dynamodb:GetItem - dynamodb:DescribeTable Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${WarrantyTable} CustomerSupportLambda: Type: AWS::Lambda::Function Properties: Description: "Lambda function for Customer Support Assistant" Handler: lambda_function.lambda_handler Code: S3Bucket: !Ref LambdaS3Bucket S3Key: !Ref LambdaS3Key Role: !GetAtt CustomerSupportLambdaRole.Arn Runtime: python3.12 PackageType: Zip Architectures: - x86_64 # SSM Parameter to store warranty table name WarrantyTableNameParameter: Type: AWS::SSM::Parameter Properties: Name: /app/customersupport/dynamodb/warranty_table_name Type: String Value: !Ref WarrantyTable Description: DynamoDB table name for warranty information Tags: Application: CustomerSupport # SSM Parameter to store customer profile table name CustomerProfileTableNameParameter: Type: AWS::SSM::Parameter Properties: Name: /app/customersupport/dynamodb/customer_profile_table_name Type: String Value: !Ref CustomerProfileTable Description: DynamoDB table name for customer profiles Tags: Application: CustomerSupport GatewayAgentcoreIAMRoleParameter: Type: AWS::SSM::Parameter Properties: Name: /app/customersupport/agentcore/gateway_iam_role Type: String Value: !GetAtt GatewayAgentCoreRole.Arn Description: agentcore IAM role to assume Tags: Application: CustomerSupport RuntimeAgentcoreIAMRoleParameter: Type: AWS::SSM::Parameter Properties: Name: /app/customersupport/agentcore/runtime_iam_role Type: String Value: !GetAtt RuntimeAgentCoreRole.Arn Description: agentcore IAM role to assume Tags: Application: CustomerSupport LambdaArnParameter: Type: AWS::SSM::Parameter Properties: Name: /app/customersupport/agentcore/lambda_arn Type: String Value: !GetAtt CustomerSupportLambda.Arn Description: ARN of the lambda that integrates with agentcore Tags: Application: CustomerSupport