diff --git a/waf-agentcore-gateway-bedrock-cdk/README.md b/waf-agentcore-gateway-bedrock-cdk/README.md new file mode 100644 index 000000000..8f5c72745 --- /dev/null +++ b/waf-agentcore-gateway-bedrock-cdk/README.md @@ -0,0 +1,125 @@ +# AWS WAF Protection for Amazon Bedrock AgentCore Gateway with Lambda Tools and Amazon Bedrock (CDK) + +This pattern deploys an AWS WAF WebACL protecting an Amazon Bedrock AgentCore Gateway that routes MCP tool calls to an AWS Lambda function invoking Amazon Bedrock for AI inference. The WAF configuration includes rate limiting, AWS Managed Rules, Bot Control, IP allowlisting, and CloudWatch logging. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/waf-agentcore-gateway-bedrock-cdk + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. + +## Requirements + +- [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +- [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +- [Node.js 22+](https://nodejs.org/en/download/) installed +- [AWS CDK v2](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) installed (`npm install -g aws-cdk`) +- [AWS CDK bootstrapped](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html) in your account/region + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ```bash + git clone https://github.com/aws-samples/serverless-patterns + cd serverless-patterns/waf-agentcore-gateway-bedrock-cdk/cdk + ``` + +2. Install dependencies: + ```bash + npm install + ``` + +3. Deploy the stack: + ```bash + cdk deploy + ``` + +## Architecture + +This pattern creates the following resources: + +1. **AWS WAF WebACL** — A regional WebACL with five rules: + - **AllowTrustedAgentIPs** (Priority 0): Allows traffic from a configurable IP set (internal agents/services) + - **RateLimitPerIP** (Priority 1): Blocks IPs exceeding 100 requests per 5 minutes with a JSON 429 response + - **AWSManagedRulesCommonRuleSet** (Priority 2): Protects against common web exploits (XSS, SQLi, etc.) + - **AWSManagedRulesKnownBadInputsRuleSet** (Priority 3): Blocks requests with known malicious patterns + - **AWSManagedRulesBotControlRuleSet** (Priority 4): Detects and labels bot traffic (count mode) + +2. **Amazon Bedrock AgentCore Gateway** — An MCP-protocol gateway that routes tool invocations to registered targets + +3. **AWS Lambda Function** — A Node.js 22 tool handler that invokes Amazon Bedrock Claude Sonnet 4 for AI inference + +4. **AWS WAF Logging** — Full request logs delivered to Amazon CloudWatch Logs for security monitoring and incident response + +5. **AWS WAF IP Set** — A configurable set of trusted CIDR blocks that bypass rate limiting + +## How it works + +When an agent or client sends a tool invocation request to the AgentCore Gateway: + +1. AWS WAF evaluates the request against the WebACL rules in priority order +2. Trusted IPs (from the IP set) are immediately allowed through +3. Other requests are checked for rate limiting (100 req/5 min per IP) +4. Requests passing rate limits are evaluated against AWS Managed Rules for malicious content +5. Bot traffic is detected and labeled (count mode allows but flags) +6. Clean requests reach the Amazon Bedrock AgentCore Gateway, which routes to the AWS Lambda tool target +7. The Lambda function invokes Amazon Bedrock and returns the AI-generated response +8. All WAF decisions are logged to CloudWatch for monitoring + +## Testing + +After deployment, retrieve the Gateway ID from stack outputs: + +```bash +aws cloudformation describe-stacks \ + --stack-name WafAgentcoreGatewayBedrockStack \ + --query 'Stacks[0].Outputs' +``` + +Invoke the AWS Lambda tool directly to verify Amazon Bedrock connectivity: + +```bash +aws lambda invoke \ + --function-name $(aws cloudformation describe-stacks \ + --stack-name WafAgentcoreGatewayBedrockStack \ + --query 'Stacks[0].Outputs[?OutputKey==`ToolFunctionArn`].OutputValue' \ + --output text) \ + --payload '{"prompt": "What is AWS WAF and how does it protect APIs?"}' \ + --cli-binary-format raw-in-base64-out \ + /tmp/response.json && cat /tmp/response.json | python3 -m json.tool +``` + +Check WAF metrics in CloudWatch: + +```bash +aws cloudwatch get-metric-statistics \ + --namespace AWS/WAFV2 \ + --metric-name AllowedRequests \ + --dimensions Name=WebACL,Value=agentcore-gateway-protection Name=Region,Value=us-east-1 Name=Rule,Value=ALL \ + --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 +``` + +## Cleanup + +Delete the stack to remove all resources: + +```bash +cdk destroy +``` + +**Warning:** This will delete all resources including the AWS WAF WebACL, Amazon Bedrock AgentCore Gateway, AWS Lambda function, and Amazon CloudWatch Log Group. AWS WAF logs stored in Amazon CloudWatch will also be deleted (retention is set to 7 days with DESTROY removal policy). + +## Customization + +- **Rate limit**: Modify the `limit` value in the `RateLimitPerIP` rule (minimum 100) +- **Trusted IPs**: Update the `addresses` array in the `TrustedAgentIPs` IP set with your CIDR blocks +- **Bot Control**: Change from `count` to `block` action on the Bot Control rule for stricter enforcement +- **Model**: Update `MODEL_ID` environment variable to use a different Amazon Bedrock foundation model + +--- + +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/waf-agentcore-gateway-bedrock-cdk/cdk/.gitignore b/waf-agentcore-gateway-bedrock-cdk/cdk/.gitignore new file mode 100644 index 000000000..3106b4689 --- /dev/null +++ b/waf-agentcore-gateway-bedrock-cdk/cdk/.gitignore @@ -0,0 +1,8 @@ +*.js +!jest.config.js +!lambda/**/*.js +*.d.ts +node_modules +cdk.out +cdk.context.json +build diff --git a/waf-agentcore-gateway-bedrock-cdk/cdk/bin/app.ts b/waf-agentcore-gateway-bedrock-cdk/cdk/bin/app.ts new file mode 100644 index 000000000..3f7548474 --- /dev/null +++ b/waf-agentcore-gateway-bedrock-cdk/cdk/bin/app.ts @@ -0,0 +1,15 @@ +#!/usr/bin/env node +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT-0 (2026) + +import 'source-map-support/register'; +import * as cdk from 'aws-cdk-lib'; +import { WafAgentcoreGatewayBedrockStack } from '../lib/waf-agentcore-gateway-bedrock-stack'; + +const app = new cdk.App(); +new WafAgentcoreGatewayBedrockStack(app, 'WafAgentcoreGatewayBedrockStack', { + env: { + account: process.env.CDK_DEFAULT_ACCOUNT, + region: process.env.CDK_DEFAULT_REGION, + }, +}); diff --git a/waf-agentcore-gateway-bedrock-cdk/cdk/cdk.json b/waf-agentcore-gateway-bedrock-cdk/cdk/cdk.json new file mode 100644 index 000000000..a6700a2ff --- /dev/null +++ b/waf-agentcore-gateway-bedrock-cdk/cdk/cdk.json @@ -0,0 +1,3 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/app.ts" +} diff --git a/waf-agentcore-gateway-bedrock-cdk/cdk/lambda/index.js b/waf-agentcore-gateway-bedrock-cdk/cdk/lambda/index.js new file mode 100644 index 000000000..acc4c8e87 --- /dev/null +++ b/waf-agentcore-gateway-bedrock-cdk/cdk/lambda/index.js @@ -0,0 +1,54 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT-0 (2026) + +const { BedrockRuntimeClient, InvokeModelCommand } = require('@aws-sdk/client-bedrock-runtime'); + +const client = new BedrockRuntimeClient({ region: process.env.REGION }); + +exports.handler = async (event) => { + console.log('Tool invocation received:', JSON.stringify(event)); + + try { + const body = typeof event.body === 'string' ? JSON.parse(event.body) : event; + const prompt = body.prompt || body.arguments?.prompt || 'Hello, how can I help you today?'; + + const response = await client.send( + new InvokeModelCommand({ + modelId: process.env.MODEL_ID, + contentType: 'application/json', + accept: 'application/json', + body: JSON.stringify({ + anthropic_version: 'bedrock-2023-05-31', + max_tokens: 1024, + messages: [ + { + role: 'user', + content: prompt, + }, + ], + }), + }) + ); + + const result = JSON.parse(new TextDecoder().decode(response.body)); + const text = result.content?.[0]?.text || ''; + + return { + statusCode: 200, + body: JSON.stringify({ + result: text, + model: process.env.MODEL_ID, + usage: result.usage, + }), + }; + } catch (error) { + console.error('Bedrock invocation failed:', error); + return { + statusCode: 500, + body: JSON.stringify({ + error: 'InferenceError', + message: error.message || 'Failed to invoke Amazon Bedrock model', + }), + }; + } +}; diff --git a/waf-agentcore-gateway-bedrock-cdk/cdk/lib/waf-agentcore-gateway-bedrock-stack.ts b/waf-agentcore-gateway-bedrock-cdk/cdk/lib/waf-agentcore-gateway-bedrock-stack.ts new file mode 100644 index 000000000..1f465cc44 --- /dev/null +++ b/waf-agentcore-gateway-bedrock-cdk/cdk/lib/waf-agentcore-gateway-bedrock-stack.ts @@ -0,0 +1,304 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT-0 (2026) + +import * as cdk from 'aws-cdk-lib'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as wafv2 from 'aws-cdk-lib/aws-wafv2'; +import * as logs from 'aws-cdk-lib/aws-logs'; +import { Construct } from 'constructs'; +import * as path from 'path'; + +export class WafAgentcoreGatewayBedrockStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const region = cdk.Stack.of(this).region; + const account = cdk.Stack.of(this).account; + + // --- Lambda Tool: Bedrock Inference --- + const toolRole = new iam.Role(this, 'ToolLambdaRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), + managedPolicies: [ + iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'), + ], + inlinePolicies: { + BedrockInvoke: new iam.PolicyDocument({ + statements: [ + new iam.PolicyStatement({ + actions: ['bedrock:InvokeModel'], + resources: [ + `arn:aws:bedrock:*::foundation-model/anthropic.claude-sonnet-4-6*`, + `arn:aws:bedrock:${region}:${account}:inference-profile/us.anthropic.claude-sonnet-4-6`, + ], + }), + ], + }), + }, + }); + + const toolFunction = new lambda.Function(this, 'BedrockToolFunction', { + runtime: lambda.Runtime.NODEJS_22_X, + handler: 'index.handler', + code: lambda.Code.fromAsset(path.join(__dirname, '..', 'lambda')), + role: toolRole, + timeout: cdk.Duration.seconds(30), + memorySize: 256, + environment: { + MODEL_ID: 'us.anthropic.claude-sonnet-4-6', + REGION: region, + }, + }); + + // --- AgentCore Gateway --- + const gatewayRole = new iam.Role(this, 'GatewayRole', { + assumedBy: new iam.ServicePrincipal('bedrock-agentcore.amazonaws.com'), + inlinePolicies: { + InvokeLambda: new iam.PolicyDocument({ + statements: [ + new iam.PolicyStatement({ + actions: ['lambda:InvokeFunction'], + resources: [toolFunction.functionArn], + }), + ], + }), + }, + }); + + const gateway = new cdk.CfnResource(this, 'AgentCoreGateway', { + type: 'AWS::BedrockAgentCore::Gateway', + properties: { + Name: 'waf-protected-gateway', + Description: 'AgentCore Gateway protected by AWS WAF', + AuthorizerType: 'NONE', + RoleArn: gatewayRole.roleArn, + ProtocolConfiguration: { + Mcp: { + SupportedVersions: ['2025-11-25'], + Instructions: 'WAF-protected gateway routing tool invocations to Lambda functions with Amazon Bedrock inference', + }, + }, + }, + }); + + const gatewayTarget = new cdk.CfnResource(this, 'BedrockToolTarget', { + type: 'AWS::BedrockAgentCore::GatewayTarget', + properties: { + GatewayIdentifier: gateway.ref, + Name: 'bedrock-inference', + Description: 'Lambda tool that invokes Amazon Bedrock for AI inference', + CredentialProviderConfigurations: [ + { + CredentialProviderType: 'GATEWAY_IAM_ROLE', + }, + ], + TargetConfiguration: { + Mcp: { + Lambda: { + LambdaArn: toolFunction.functionArn, + ToolSchema: { + InlinePayload: [ + { + Name: 'invoke_bedrock', + Description: 'Invoke Amazon Bedrock Claude model with a user prompt and return the AI-generated response', + InputSchema: { + Type: 'object', + Properties: { + prompt: { + Type: 'string', + Description: 'The user prompt to send to Amazon Bedrock', + }, + }, + Required: ['prompt'], + }, + }, + ], + }, + }, + }, + }, + }, + }); + + // --- WAF WebACL --- + const wafLogGroup = new logs.LogGroup(this, 'WafLogGroup', { + logGroupName: `aws-waf-logs-agentcore-gateway`, + retention: logs.RetentionDays.ONE_WEEK, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + const ipAllowSet = new wafv2.CfnIPSet(this, 'TrustedAgentIPs', { + name: 'TrustedAgentIPSet', + scope: 'REGIONAL', + ipAddressVersion: 'IPV4', + addresses: [ + // Add trusted agent/client CIDR blocks here + '10.0.0.0/8', + ], + description: 'IP addresses of trusted AI agents and internal services', + }); + + const webAcl = new wafv2.CfnWebACL(this, 'GatewayWebACL', { + name: 'agentcore-gateway-protection', + scope: 'REGIONAL', + defaultAction: { allow: {} }, + visibilityConfig: { + cloudWatchMetricsEnabled: true, + metricName: 'AgentCoreGatewayWAF', + sampledRequestsEnabled: true, + }, + rules: [ + // Rule 1: Rate-based rule — throttle abusive traffic + { + name: 'RateLimitPerIP', + priority: 1, + action: { + block: { + customResponse: { + responseCode: 429, + customResponseBodyKey: 'rate-limited', + }, + }, + }, + statement: { + rateBasedStatement: { + limit: 100, + aggregateKeyType: 'IP', + }, + }, + visibilityConfig: { + cloudWatchMetricsEnabled: true, + metricName: 'RateLimitPerIP', + sampledRequestsEnabled: true, + }, + }, + // Rule 2: AWS Managed Rules — Common Rule Set + { + name: 'AWSManagedRulesCommonRuleSet', + priority: 2, + overrideAction: { none: {} }, + statement: { + managedRuleGroupStatement: { + vendorName: 'AWS', + name: 'AWSManagedRulesCommonRuleSet', + }, + }, + visibilityConfig: { + cloudWatchMetricsEnabled: true, + metricName: 'CommonRuleSet', + sampledRequestsEnabled: true, + }, + }, + // Rule 3: AWS Managed Rules — Known Bad Inputs + { + name: 'AWSManagedRulesKnownBadInputsRuleSet', + priority: 3, + overrideAction: { none: {} }, + statement: { + managedRuleGroupStatement: { + vendorName: 'AWS', + name: 'AWSManagedRulesKnownBadInputsRuleSet', + }, + }, + visibilityConfig: { + cloudWatchMetricsEnabled: true, + metricName: 'KnownBadInputs', + sampledRequestsEnabled: true, + }, + }, + // Rule 4: AWS Bot Control — detect and manage bot traffic + { + name: 'AWSManagedRulesBotControlRuleSet', + priority: 4, + overrideAction: { count: {} }, + statement: { + managedRuleGroupStatement: { + vendorName: 'AWS', + name: 'AWSManagedRulesBotControlRuleSet', + managedRuleGroupConfigs: [ + { + awsManagedRulesBotControlRuleSet: { + inspectionLevel: 'COMMON', + }, + }, + ], + }, + }, + visibilityConfig: { + cloudWatchMetricsEnabled: true, + metricName: 'BotControl', + sampledRequestsEnabled: true, + }, + }, + // Rule 5: Allow trusted agent IPs (higher priority override) + { + name: 'AllowTrustedAgentIPs', + priority: 0, + action: { allow: {} }, + statement: { + ipSetReferenceStatement: { + arn: ipAllowSet.attrArn, + }, + }, + visibilityConfig: { + cloudWatchMetricsEnabled: true, + metricName: 'TrustedAgentIPs', + sampledRequestsEnabled: true, + }, + }, + ], + customResponseBodies: { + 'rate-limited': { + contentType: 'APPLICATION_JSON', + content: JSON.stringify({ + error: 'TooManyRequests', + message: 'Rate limit exceeded. Please retry after a brief delay.', + retryAfterSeconds: 60, + }), + }, + }, + }); + + // --- WAF Association with AgentCore Gateway --- + const webAclAssociation = new wafv2.CfnWebACLAssociation(this, 'WafGatewayAssociation', { + resourceArn: gateway.getAtt('GatewayArn').toString(), + webAclArn: webAcl.attrArn, + }); + + // --- WAF Logging Configuration --- + const loggingConfig = new wafv2.CfnLoggingConfiguration(this, 'WafLogging', { + resourceArn: webAcl.attrArn, + logDestinationConfigs: [ + `arn:aws:logs:${region}:${account}:log-group:aws-waf-logs-agentcore-gateway`, + ], + }); + + loggingConfig.addDependency(wafLogGroup.node.defaultChild as cdk.CfnResource); + + // --- Outputs --- + new cdk.CfnOutput(this, 'GatewayId', { + value: gateway.ref, + description: 'AgentCore Gateway ID', + }); + + new cdk.CfnOutput(this, 'GatewayArn', { + value: gateway.getAtt('GatewayArn').toString(), + description: 'AgentCore Gateway ARN', + }); + + new cdk.CfnOutput(this, 'WebACLArn', { + value: webAcl.attrArn, + description: 'AWS WAF WebACL ARN protecting the gateway', + }); + + new cdk.CfnOutput(this, 'ToolFunctionArn', { + value: toolFunction.functionArn, + description: 'Lambda tool function ARN', + }); + + new cdk.CfnOutput(this, 'WafLogGroupName', { + value: wafLogGroup.logGroupName, + description: 'CloudWatch Log Group for AWS WAF logs', + }); + } +} diff --git a/waf-agentcore-gateway-bedrock-cdk/cdk/package.json b/waf-agentcore-gateway-bedrock-cdk/cdk/package.json new file mode 100644 index 000000000..73f104d5a --- /dev/null +++ b/waf-agentcore-gateway-bedrock-cdk/cdk/package.json @@ -0,0 +1,20 @@ +{ + "name": "waf-agentcore-gateway-bedrock-cdk", + "version": "1.0.0", + "bin": { + "app": "bin/app.js" + }, + "scripts": { + "build": "tsc", + "cdk": "cdk" + }, + "dependencies": { + "aws-cdk-lib": "2.185.0", + "constructs": "^10.0.0" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "typescript": "~5.4.0", + "aws-cdk": "^2.185.0" + } +} diff --git a/waf-agentcore-gateway-bedrock-cdk/cdk/tsconfig.json b/waf-agentcore-gateway-bedrock-cdk/cdk/tsconfig.json new file mode 100644 index 000000000..08c91a63c --- /dev/null +++ b/waf-agentcore-gateway-bedrock-cdk/cdk/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["es2020"], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "outDir": "./build", + "rootDir": "." + }, + "exclude": ["node_modules", "cdk.out"] +} diff --git a/waf-agentcore-gateway-bedrock-cdk/example-pattern.json b/waf-agentcore-gateway-bedrock-cdk/example-pattern.json new file mode 100644 index 000000000..4eadfd0d0 --- /dev/null +++ b/waf-agentcore-gateway-bedrock-cdk/example-pattern.json @@ -0,0 +1,92 @@ +{ + "title": "AWS WAF Protection for Amazon Bedrock AgentCore Gateway", + "description": "Deploy AWS WAF with rate limiting, managed rules, and bot control to protect an Amazon Bedrock AgentCore Gateway routing tool calls to AWS Lambda and Amazon Bedrock", + "language": "TypeScript", + "level": "300", + "framework": "AWS CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern deploys an AWS WAF WebACL that protects an Amazon Bedrock AgentCore Gateway from common web exploits and abuse.", + "The WebACL includes rate-based rules (100 requests per 5 minutes per IP), AWS Managed Rules for common threats and known bad inputs, Bot Control for detecting automated traffic, and an IP allowlist for trusted agents.", + "The AgentCore Gateway uses the MCP protocol to route tool invocations to an AWS Lambda function that invokes Amazon Bedrock Claude Sonnet 4 for AI inference.", + "All AWS WAF decisions are logged to Amazon CloudWatch Logs for security monitoring and incident response." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/waf-agentcore-gateway-bedrock-cdk", + "templateURL": "serverless-patterns/waf-agentcore-gateway-bedrock-cdk", + "projectFolder": "waf-agentcore-gateway-bedrock-cdk", + "templateFile": "cdk/lib/waf-agentcore-gateway-bedrock-stack.ts" + } + }, + "resources": { + "bullets": [ + { + "text": "AWS WAF Developer Guide - Protecting resources", + "link": "https://docs.aws.amazon.com/waf/latest/developerguide/how-aws-waf-works-resources.html" + }, + { + "text": "Amazon Bedrock AgentCore Gateway documentation", + "link": "https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway.html" + }, + { + "text": "AWS WAF rate-based rules", + "link": "https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-rate-based.html" + } + ] + }, + "deploy": { + "text": [ + "cd waf-agentcore-gateway-bedrock-cdk/cdk", + "npm install", + "cdk deploy" + ] + }, + "testing": { + "text": [ + "Invoke the AWS Lambda tool to verify Amazon Bedrock connectivity and check AWS WAF metrics in Amazon CloudWatch." + ] + }, + "cleanup": { + "text": [ + "cdk destroy" + ] + }, + "authors": [ + { + "name": "Nithin Chandran R", + "bio": "Technical Account Manager at AWS", + "linkedin": "nithin-chandran-r" + } + ], + "patternArch": { + "icon1": { + "x": 20, + "y": 50, + "service": "waf", + "label": "AWS WAF" + }, + "icon2": { + "x": 50, + "y": 50, + "service": "bedrock", + "label": "AgentCore Gateway" + }, + "icon3": { + "x": 80, + "y": 50, + "service": "lambda", + "label": "AWS Lambda" + }, + "line1": { + "from": "icon1", + "to": "icon2" + }, + "line2": { + "from": "icon2", + "to": "icon3" + } + } +}