diff --git a/.github/workflows/build-pull-request.yml b/.github/workflows/build-pull-request.yml index 3c8d734bd3..e7c3f4c9cc 100644 --- a/.github/workflows/build-pull-request.yml +++ b/.github/workflows/build-pull-request.yml @@ -30,22 +30,28 @@ jobs: with: language: ${{ matrix.language }} - # Get the list of changed files, excluding Markdown files + # Get the list of changed files, excluding Markdown files and deleted files - name: Get changed files id: changed-files uses: tj-actions/changed-files@27ae6b33eaed7bf87272fdeb9f1c54f9facc9d99 with: files: ${{ matrix.language }}/** files_ignore: '**/*.md' + files_separator: ' ' # Build the changed files for the specified language - name: Build changed files if: steps.changed-files.outputs.any_changed == 'true' run: | + # Create a temporary directory for build logs + mkdir -p /tmp/build_logs + npm install -g eslint > /dev/null + # Function to build a single file build_file() { echo "Build File $1" local file="$1" + local log_file="/tmp/build_logs/$(basename "$file" | sed 's/\//_/g').log" IFS="/" read -ra path_parts <<< "$file" language=${path_parts[0]} @@ -57,14 +63,35 @@ jobs: echo "Build Path $file" # Run the build script for the current language, passing the project directory and extra path - echo "::group::$file" - if ../scripts/build-${language}.sh "$file"; then - echo "::endgroup::" + echo "::group::Building $file" + # Run the build command and capture output to both the log file and stdout + ../scripts/build-${language}.sh "$file" 2>&1 | tee "$log_file" + local exit_code=${PIPESTATUS[0]} + + if [ $exit_code -eq 0 ]; then + echo "✅ Build succeeded for $file" + + # Clean up node_modules and cdk.out for this project immediately after successful build + echo "Cleaning up build artifacts for $(dirname "$file")" + local project_dir=$(dirname "$file") + + # Remove node_modules directory if it exists + if [ -d "$project_dir/node_modules" ]; then + echo "Removing $project_dir/node_modules" + rm -rf "$project_dir/node_modules" + fi + + # Remove cdk.out directory if it exists + if [ -d "$project_dir/cdk.out" ]; then + echo "Removing $project_dir/cdk.out" + rm -rf "$project_dir/cdk.out" + fi else - echo "::endgroup::" - echo "::error::Build failed for $file" - return 1 + echo "❌ Build failed for $file with exit code $exit_code" + echo "::error::Build failed for $file with exit code $exit_code" fi + echo "::endgroup::" + return $exit_code } # Export the build_file function for use in parallel @@ -73,7 +100,8 @@ jobs: # Create an array to store directories to be built apps_to_build=() - files=(${{ steps.changed-files.outputs.all_modified_files }}) + # Use only added and modified files, ignoring deleted files + files=(${{ steps.changed-files.outputs.added_files }} ${{ steps.changed-files.outputs.modified_files }}) # Check the directories of each changed file for cdk.json for file in "${files[@]}"; do @@ -110,4 +138,18 @@ jobs: # Run the build_file function in parallel for each project to be built # Halt the execution if any of the build_file invocations fail + echo "::group::Build Output" + echo "Starting builds for all projects..." parallel --keep-order --halt-on-error 2 build_file ::: "${apps_to_build[@]}" + echo "::endgroup::" + + # Check the exit status of parallel + parallel_exit=$? + + # If parallel failed, make sure the workflow fails too + if [ $parallel_exit -ne 0 ]; then + echo "::error::One or more builds failed. See build output above for details." + exit $parallel_exit + else + echo "✅ All builds completed successfully!" + fi diff --git a/SNAPSHOT_TESTING.md b/SNAPSHOT_TESTING.md new file mode 100644 index 0000000000..26c80caca7 --- /dev/null +++ b/SNAPSHOT_TESTING.md @@ -0,0 +1,106 @@ +# CDK Snapshot Testing Guide + +This guide explains how to use the asset hash normalization utility for consistent CDK snapshot testing across different environments. + +## Problem + +CDK snapshot tests often fail in CI/CD environments because asset hashes change between different environments. This happens because: + +- Asset hashes are generated based on content and environment variables +- Different machines or CI/CD environments produce different hashes +- This causes snapshot tests to fail even when there are no actual changes to the infrastructure + +## Solution + +We've created a utility function that normalizes asset hashes and other environment-specific values in CloudFormation templates before comparing them with snapshots. + +## How to Use the Normalization Utility + +### 1. Import the Utility + +```typescript +import { normalizeTemplate } from '../../test-utils/normalize-template'; +``` + +### 2. Apply Normalization Before Snapshot Comparison + +```typescript +import { App } from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { YourStack } from '../lib/your-stack'; +import { normalizeTemplate } from '../../test-utils/normalize-template'; + +test('YourStack creates the expected resources', () => { + const app = new App(); + const stack = new YourStack(app, 'TestStack'); + + // Get the CloudFormation template + const template = Template.fromStack(stack); + + // Normalize the template before snapshot comparison + const normalizedTemplate = normalizeTemplate(template.toJSON()); + + // Compare with snapshot + expect(normalizedTemplate).toMatchSnapshot(); +}); +``` + +### 3. Update Existing Snapshots + +After adding the normalization to your tests, update your snapshots: + +```bash +npm test -- -u +``` + +## What Gets Normalized + +The utility currently normalizes: + +1. **S3 Asset Keys**: Replaces asset hashes in S3Key properties + - Pattern with extension: `[64 hex chars].zip` → `NORMALIZED_ASSET_HASH.zip` + - Pattern without extension: `[64 hex chars]` → `NORMALIZED_ASSET_HASH` + +2. **Docker Image Digests**: Replaces image digests + - Pattern: `sha256:[digest]` → `NORMALIZED_IMAGE_DIGEST` + +## Adding New Test Files + +When creating new test files that use snapshot testing: + +1. Import the normalization utility +2. Apply it to your template before comparing with snapshots +3. Update your snapshots with the `-u` flag + +## Extending the Utility + +If you encounter other environment-specific values that need normalization, you can extend the utility at `typescript/test-utils/normalize-template.ts`. + +Example of adding a new normalization rule: + +```typescript +// Inside the normalizeValues function +if (key === 'NewPropertyToNormalize' && typeof obj[key] === 'string' && /pattern-to-match/.test(obj[key])) { + obj[key] = 'NORMALIZED_VALUE'; +} +``` + +## Troubleshooting + +If you're still seeing snapshot test failures: + +1. **Check for new patterns**: There might be new types of asset hashes or environment-specific values that need normalization +2. **Verify imports**: Make sure you're importing and using the utility correctly +3. **Check snapshot updates**: Ensure you've updated your snapshots after adding normalization + +## Best Practices + +1. **Always normalize before snapshot comparison**: This ensures consistent results +2. **Update snapshots deliberately**: Only use the `-u` flag when you expect changes +3. **Review snapshot diffs**: When updating snapshots, review the changes to ensure they're expected +4. **Keep the utility updated**: As new patterns emerge, add them to the normalization utility + +## Additional Resources + +- [Jest Snapshot Testing Documentation](https://jestjs.io/docs/snapshot-testing) +- [AWS CDK Testing Documentation](https://docs.aws.amazon.com/cdk/v2/guide/testing.html) diff --git a/scripts/build-all-typescript.sh b/scripts/build-all-typescript.sh new file mode 100755 index 0000000000..eb6287b539 --- /dev/null +++ b/scripts/build-all-typescript.sh @@ -0,0 +1,166 @@ +#!/bin/bash +# +# Script to build all TypeScript CDK projects in parallel +# +set -euo pipefail + +# Get the directory of this script +SCRIPT_DIR=$(cd $(dirname $0) && pwd) +REPO_ROOT=$(dirname "$SCRIPT_DIR") +TYPESCRIPT_DIR="${REPO_ROOT}/typescript" +BUILD_SCRIPT="$SCRIPT_DIR/build-typescript.sh" + +# Check if parallel is installed +if ! command -v parallel &> /dev/null; then + echo "GNU parallel is not installed. Please install it first." + exit 1 +fi + +# Check if the build script exists +if [ ! -f "$BUILD_SCRIPT" ]; then + echo "Error: Build script not found at $BUILD_SCRIPT" + exit 1 +fi + +# Create a temporary directory for build logs +TEMP_DIR=$(mktemp -d) +trap 'rm -rf "$TEMP_DIR"' EXIT + +# Find all TypeScript CDK projects (directories with cdk.json) +# Exclude any matches from node_modules directories, cdk.out directories, and specific problematic projects +echo "Finding all TypeScript CDK projects..." +PROJECTS=$(find "$TYPESCRIPT_DIR" -name "cdk.json" -type f \ + -not -path "*/node_modules/*" \ + -not -path "*/cdk.out/*" \ + | sort) + +# Count total projects +TOTAL_PROJECTS=$(echo "$PROJECTS" | wc -l) +echo "Found $TOTAL_PROJECTS TypeScript CDK projects to build" +echo "==============================" + +# Function to build a single project +build_project() { + CDK_JSON_PATH="$1" + PROJECT_DIR=$(dirname "$CDK_JSON_PATH") + REL_PATH=$(realpath --relative-to="$REPO_ROOT" "$PROJECT_DIR") + LOG_FILE="$TEMP_DIR/$(echo "$REL_PATH" | tr '/' '_').log" + + # Show start message + echo "▶️ Starting build: $REL_PATH" + + # Check for DO_NOT_AUTOTEST file + if [ -f "$PROJECT_DIR/DO_NOT_AUTOTEST" ]; then + echo "⏭️ Skipping $REL_PATH (DO_NOT_AUTOTEST flag found)" + echo "SKIPPED:$REL_PATH" > "$LOG_FILE.status" + return 0 + fi + + # Find the package.json in the project directory + PACKAGE_JSON="$PROJECT_DIR/package.json" + if [ ! -f "$PACKAGE_JSON" ]; then + echo "⏭️ Skipping $REL_PATH (no package.json found)" + echo "SKIPPED:$REL_PATH" > "$LOG_FILE.status" + return 0 + fi + + # Get the relative path to package.json for the build script + PACKAGE_JSON_REL_PATH=$(realpath --relative-to="$REPO_ROOT" "$PACKAGE_JSON") + + # Create a modified version of the build script that suppresses cdk synth output + TEMP_BUILD_SCRIPT="$TEMP_DIR/build-$(basename "$REL_PATH").sh" + cat > "$TEMP_BUILD_SCRIPT" << 'EOF' +#!/bin/bash +set -euo pipefail + +# Get the original script and arguments +ORIGINAL_SCRIPT="$1" +shift +ARGS="$@" + +# Run the original script but capture and filter the output +"$ORIGINAL_SCRIPT" "$ARGS" 2>&1 | grep -v "cdk synth" | grep -v "Synthesizing" +EOF + chmod +x "$TEMP_BUILD_SCRIPT" + + # Run the build script with filtered output + if "$TEMP_BUILD_SCRIPT" "$BUILD_SCRIPT" "$PACKAGE_JSON_REL_PATH" > "$LOG_FILE" 2>&1; then + echo "✅ Build successful: $REL_PATH" + echo "SUCCESS:$REL_PATH" > "$LOG_FILE.status" + else + echo "❌ Build failed: $REL_PATH" + echo "FAILED:$REL_PATH" > "$LOG_FILE.status" + fi +} +export -f build_project +export SCRIPT_DIR +export REPO_ROOT +export BUILD_SCRIPT +export TEMP_DIR + +# Run builds in parallel +echo "$PROJECTS" | parallel --will-cite --jobs 50% build_project + +# Collect results +SUCCESSFUL=0 +FAILED=0 +SKIPPED=0 +ALL_PROJECTS=() + +for STATUS_FILE in "$TEMP_DIR"/*.status; do + [ -f "$STATUS_FILE" ] || continue + + STATUS_CONTENT=$(cat "$STATUS_FILE") + STATUS_TYPE=${STATUS_CONTENT%%:*} + PROJECT_PATH=${STATUS_CONTENT#*:} + + case "$STATUS_TYPE" in + "SUCCESS") + ((SUCCESSFUL++)) + ALL_PROJECTS+=("✅ $PROJECT_PATH") + ;; + "FAILED") + ((FAILED++)) + ALL_PROJECTS+=("❌ $PROJECT_PATH") + ;; + "SKIPPED") + ((SKIPPED++)) + ALL_PROJECTS+=("⏭️ $PROJECT_PATH") + ;; + esac +done + +# Sort the projects list +IFS=$'\n' SORTED_PROJECTS=($(sort <<<"${ALL_PROJECTS[*]}")) +unset IFS + +# Print summary +echo "" +echo "==============================" +echo "BUILD SUMMARY" +echo "==============================" +echo "Total: $((SUCCESSFUL + FAILED + SKIPPED)) (✅ $SUCCESSFUL succeeded, ❌ $FAILED failed, ⏭️ $SKIPPED skipped)" +echo "" +echo "Project Status:" +echo "-----------------------------" +for PROJ in "${SORTED_PROJECTS[@]}"; do + echo "$PROJ" +done + +# If any builds failed, print their logs and exit with error +if [ $FAILED -gt 0 ]; then + echo "" + echo "Build logs for failed projects:" + echo "==============================" + for PROJ in "${SORTED_PROJECTS[@]}"; do + if [[ $PROJ == ❌* ]]; then + PROJECT_PATH=${PROJ#❌ } + echo "" + echo "Log for $PROJECT_PATH:" + echo "-------------------------------------------" + cat "$TEMP_DIR/$(echo "$PROJECT_PATH" | tr '/' '_').log" + echo "-------------------------------------------" + fi + done + exit 1 +fi diff --git a/typescript/amazon-mq-rabbitmq-lambda/package.json b/typescript/amazon-mq-rabbitmq-lambda/package.json index 75379da8ec..d9b7741a6b 100644 --- a/typescript/amazon-mq-rabbitmq-lambda/package.json +++ b/typescript/amazon-mq-rabbitmq-lambda/package.json @@ -11,17 +11,17 @@ "cdk": "cdk" }, "devDependencies": { - "@types/jest": "^29.5.12", - "@types/node": "22.5.4", - "aws-cdk": "2.160.0", + "@types/jest": "^29.5.14", + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", "jest": "^29.7.0", "ts-jest": "^29.2.5", "ts-node": "^10.9.2", - "typescript": "~5.6.2" + "typescript": "~5.6.3" }, "dependencies": { "@cdklabs/cdk-amazonmq": "^0.0.1", - "aws-cdk-lib": "2.160.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0" } } diff --git a/typescript/amplify-console-app/package.json b/typescript/amplify-console-app/package.json index 079e25b40d..54d653c109 100644 --- a/typescript/amplify-console-app/package.json +++ b/typescript/amplify-console-app/package.json @@ -14,12 +14,12 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/node": "^8.10.38", - "aws-cdk": "*", - "typescript": "~5.1.6" + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "^2.0.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0" } } diff --git a/typescript/api-cors-lambda-crud-dynamodb/lambdas/package.json b/typescript/api-cors-lambda-crud-dynamodb/lambdas/package.json index be979d03ee..48ba4b28e6 100644 --- a/typescript/api-cors-lambda-crud-dynamodb/lambdas/package.json +++ b/typescript/api-cors-lambda-crud-dynamodb/lambdas/package.json @@ -5,7 +5,7 @@ "private": true, "license": "Apache-2.0", "devDependencies": { - "@types/node": "*", + "@types/node": "22.7.9", "@types/uuid": "*" }, "dependencies": { diff --git a/typescript/api-cors-lambda-crud-dynamodb/package.json b/typescript/api-cors-lambda-crud-dynamodb/package.json index 5eb01180c4..9a21e6edda 100644 --- a/typescript/api-cors-lambda-crud-dynamodb/package.json +++ b/typescript/api-cors-lambda-crud-dynamodb/package.json @@ -15,13 +15,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/node": "*", - "aws-cdk": "*", + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", "esbuild": "*", - "typescript": "~5.1.6" + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "^2.0.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0" } } diff --git a/typescript/api-gateway-async-lambda-invocation/package.json b/typescript/api-gateway-async-lambda-invocation/package.json index ea1fcf4f12..93fe31608d 100644 --- a/typescript/api-gateway-async-lambda-invocation/package.json +++ b/typescript/api-gateway-async-lambda-invocation/package.json @@ -10,13 +10,13 @@ "cdk": "cdk" }, "devDependencies": { - "@types/node": "22.5.4", - "aws-cdk": "2.163.0", + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", "ts-node": "^10.9.2", - "typescript": "~5.6.2" + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "2.163.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0", "source-map-support": "^0.5.21" } diff --git a/typescript/api-gateway-lambda-token-authorizer/package.json b/typescript/api-gateway-lambda-token-authorizer/package.json index 25a23241d2..1674e6e5ef 100644 --- a/typescript/api-gateway-lambda-token-authorizer/package.json +++ b/typescript/api-gateway-lambda-token-authorizer/package.json @@ -13,18 +13,18 @@ "cdk": "cdk" }, "devDependencies": { - "@types/jest": "^29.5.4", - "@types/node": "20.5.9", - "aws-cdk": "2.96.2", - "jest": "^29.6.4", - "ts-jest": "^29.1.1", - "ts-node": "^10.9.1", - "typescript": "~5.2.2", + "@types/jest": "^29.5.14", + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", + "jest": "^29.7.0", + "ts-jest": "^29.2.5", + "ts-node": "^10.9.2", + "typescript": "~5.6.3", "esbuild": "^0.19.4" }, "dependencies": { "@types/aws-lambda": "^8.10.121", - "aws-cdk-lib": "2.177.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0", "source-map-support": "^0.5.21" }, diff --git a/typescript/api-gateway-lambda-token-authorizer/test/stack/__snapshots__/gateway-lambda-auth-stack.test.ts.snap b/typescript/api-gateway-lambda-token-authorizer/test/stack/__snapshots__/gateway-lambda-auth-stack.test.ts.snap index e54989d21d..eca14a1665 100644 --- a/typescript/api-gateway-lambda-token-authorizer/test/stack/__snapshots__/gateway-lambda-auth-stack.test.ts.snap +++ b/typescript/api-gateway-lambda-token-authorizer/test/stack/__snapshots__/gateway-lambda-auth-stack.test.ts.snap @@ -50,10 +50,10 @@ exports[`Snapshot Stack 1`] = ` "value": "nodejs20.x", }, "cn-north-1": { - "value": "nodejs18.x", + "value": "nodejs20.x", }, "cn-northwest-1": { - "value": "nodejs18.x", + "value": "nodejs20.x", }, "eu-central-1": { "value": "nodejs20.x", @@ -104,10 +104,10 @@ exports[`Snapshot Stack 1`] = ` "value": "nodejs20.x", }, "us-gov-east-1": { - "value": "nodejs18.x", + "value": "nodejs20.x", }, "us-gov-west-1": { - "value": "nodejs18.x", + "value": "nodejs20.x", }, "us-iso-east-1": { "value": "nodejs18.x", @@ -198,7 +198,7 @@ exports[`Snapshot Stack 1`] = ` "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "2819175352ad1ce0dae768e83fc328fb70fb5f10b4a8ff0ccbcb791f02b0716d.zip", + "S3Key": "NORMALIZED_ASSET_HASH.zip", }, "Handler": "index.handler", "Role": { @@ -322,7 +322,7 @@ exports[`Snapshot Stack 1`] = ` "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "a1deff17a695906f93c544b1deae2bf444210622f05330da961687eb8f4a51a7.zip", + "S3Key": "NORMALIZED_ASSET_HASH.zip", }, "Description": "Lambda Authorizer", "Environment": { @@ -507,7 +507,7 @@ exports[`Snapshot Stack 1`] = ` "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "5ce4b859d5e57c3d238da3ac017e0ea37bf34ad1b71de268430b08e38bb9450d.zip", + "S3Key": "NORMALIZED_ASSET_HASH.zip", }, "Description": "Operational Lambda", "Environment": { diff --git a/typescript/api-gateway-lambda-token-authorizer/test/stack/gateway-lambda-auth-stack.test.ts b/typescript/api-gateway-lambda-token-authorizer/test/stack/gateway-lambda-auth-stack.test.ts index 9f8882602e..75e4f0bb21 100644 --- a/typescript/api-gateway-lambda-token-authorizer/test/stack/gateway-lambda-auth-stack.test.ts +++ b/typescript/api-gateway-lambda-token-authorizer/test/stack/gateway-lambda-auth-stack.test.ts @@ -1,6 +1,7 @@ import { App } from "aws-cdk-lib"; import { GatewayLambdaAuth } from "../../lib/stack/gateway-lambda-auth-stack"; import { Template } from "aws-cdk-lib/assertions"; +import { normalizeTemplate } from "../../../test-utils/normalize-template"; describe('Snapshot', () => { @@ -8,7 +9,10 @@ describe('Snapshot', () => { const app = new App(); const stack = new GatewayLambdaAuth(app, 'test-api-gateway-lambda-auth'); const template = Template.fromStack(stack); - expect(template.toJSON()).toMatchSnapshot(); + + // Normalize the template before snapshot comparison + const normalizedTemplate = normalizeTemplate(template.toJSON()); + expect(normalizedTemplate).toMatchSnapshot(); }); }) diff --git a/typescript/api-gateway-parallel-step-functions/package.json b/typescript/api-gateway-parallel-step-functions/package.json index 34d1403737..660fe08d47 100644 --- a/typescript/api-gateway-parallel-step-functions/package.json +++ b/typescript/api-gateway-parallel-step-functions/package.json @@ -18,14 +18,14 @@ "cdk": "cdk" }, "devDependencies": { - "@types/node": "20.5.7", - "aws-cdk": "2.93.0", - "ts-node": "^10.9.1", - "typescript": "^5.2.2" + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", + "ts-node": "^10.9.2", + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "2.177.0", - "constructs": "^10.2.70", + "aws-cdk-lib": "2.185.0", + "constructs": "^10.0.0", "source-map-support": "^0.5.21" } } diff --git a/typescript/api-websocket-lambda-dynamodb/package.json b/typescript/api-websocket-lambda-dynamodb/package.json index 73a1e8f180..54033a7c27 100644 --- a/typescript/api-websocket-lambda-dynamodb/package.json +++ b/typescript/api-websocket-lambda-dynamodb/package.json @@ -15,12 +15,12 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/node": "^10.17.0", - "aws-cdk": "*", - "typescript": "~5.1.6" + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "^2.0.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0" } } diff --git a/typescript/application-load-balancer/package.json b/typescript/application-load-balancer/package.json index f43fa0e87b..a2e717adbb 100644 --- a/typescript/application-load-balancer/package.json +++ b/typescript/application-load-balancer/package.json @@ -15,12 +15,12 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/node": "^10.17.0", - "aws-cdk": "*", - "typescript": "~5.1.6" + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "^2.0.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0" } } diff --git a/typescript/appsync-graphql-dynamodb/package.json b/typescript/appsync-graphql-dynamodb/package.json index 18ecbfeaf9..34494a7e88 100644 --- a/typescript/appsync-graphql-dynamodb/package.json +++ b/typescript/appsync-graphql-dynamodb/package.json @@ -11,17 +11,17 @@ "push-data": "node utils/index.js" }, "devDependencies": { - "@types/jest": "^29.5.5", - "@types/node": "20.6.3", - "aws-cdk": "2.99.1", + "@types/jest": "^29.5.14", + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", "jest": "^29.7.0", - "ts-jest": "^29.1.1", - "ts-node": "^10.9.1", - "typescript": "~5.2.2" + "ts-jest": "^29.2.5", + "ts-node": "^10.9.2", + "typescript": "~5.6.3" }, "dependencies": { "@aws-sdk/client-dynamodb": "^3.535.0", - "aws-cdk-lib": "2.99.1", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0", "source-map-support": "^0.5.21" } diff --git a/typescript/appsync-graphql-eventbridge/package.json b/typescript/appsync-graphql-eventbridge/package.json index fa9ee586f8..766dc439ab 100644 --- a/typescript/appsync-graphql-eventbridge/package.json +++ b/typescript/appsync-graphql-eventbridge/package.json @@ -13,13 +13,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/node": "10.17.0", - "aws-cdk": "*", - "typescript": "~5.1.6", - "ts-node": "^8.1.0" + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", + "typescript": "~5.6.3", + "ts-node": "^10.9.2" }, "dependencies": { - "aws-cdk-lib": "^2.0.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0", "source-map-support": "^0.5.9" } diff --git a/typescript/appsync-graphql-http/package.json b/typescript/appsync-graphql-http/package.json index 887f193cac..fd574aae4c 100644 --- a/typescript/appsync-graphql-http/package.json +++ b/typescript/appsync-graphql-http/package.json @@ -16,12 +16,12 @@ "license": "Apache-2.0", "devDependencies": { "@aws-appsync/utils": "^1.5.0", - "@types/node": "^10.17.0", - "aws-cdk": "*", - "typescript": "~5.2.2" + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "^2.103.1", - "constructs": "^10.3.0" + "aws-cdk-lib": "2.185.0", + "constructs": "^10.0.0" } } diff --git a/typescript/appsync-graphql-typescript-resolver/resolvers/package.json b/typescript/appsync-graphql-typescript-resolver/resolvers/package.json index 8c0801d552..e1a5e8fa08 100644 --- a/typescript/appsync-graphql-typescript-resolver/resolvers/package.json +++ b/typescript/appsync-graphql-typescript-resolver/resolvers/package.json @@ -15,14 +15,14 @@ "devDependencies": { "@aws-appsync/eslint-plugin": "^1.2.1", "@aws-appsync/utils": "^1.2.5", - "@types/jest": "^29.5.2", + "@types/jest": "^29.5.14", "@typescript-eslint/eslint-plugin": "^5.60.0", "@typescript-eslint/parser": "^5.60.0", "esbuild": "^0.18.6", "eslint": "^8.43.0", - "jest": "^29.5.0", - "ts-jest": "^29.1.0", - "ts-node": "^10.9.1", - "typescript": "^4.9.5" + "jest": "^29.7.0", + "ts-jest": "^29.2.5", + "ts-node": "^10.9.2", + "typescript": "~5.6.3" } } diff --git a/typescript/aspects/package.json b/typescript/aspects/package.json index 855fce811a..e08bf72808 100644 --- a/typescript/aspects/package.json +++ b/typescript/aspects/package.json @@ -12,22 +12,22 @@ "cdk": "cdk" }, "devDependencies": { - "@types/jest": "^29.5.8", - "@types/node": "20.9.0", + "@types/jest": "^29.5.14", + "@types/node": "22.7.9", "@typescript-eslint/eslint-plugin": "^6.11.0", - "aws-cdk": "2.106.1", + "aws-cdk": "2.1004.0", "eslint": "^8.54.0", "eslint-config-standard-with-typescript": "^40.0.0", "eslint-plugin-import": "^2.29.0", "eslint-plugin-n": "^16.3.1", "eslint-plugin-promise": "^6.1.1", "jest": "^29.7.0", - "ts-jest": "^29.1.1", - "ts-node": "^10.9.1", - "typescript": "~5.2.2" + "ts-jest": "^29.2.5", + "ts-node": "^10.9.2", + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "2.106.1", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0", "source-map-support": "^0.5.21" } diff --git a/typescript/aws-codepipeline-ecs-lambda/package.json b/typescript/aws-codepipeline-ecs-lambda/package.json index f5353766df..02255cd3a7 100644 --- a/typescript/aws-codepipeline-ecs-lambda/package.json +++ b/typescript/aws-codepipeline-ecs-lambda/package.json @@ -11,13 +11,13 @@ }, "devDependencies": { "@types/aws-lambda": "^8.10.140", - "@types/node": "20.14.2", - "aws-cdk": "2.162.1", + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", "ts-node": "^10.9.2", - "typescript": "~5.4.5" + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "2.162.1", + "aws-cdk-lib": "2.185.0", "@types/aws-lambda": "^8.10.140", "constructs": "^10.0.0", "source-map-support": "^0.5.21" diff --git a/typescript/aws-transfer-sftp-server/jest.config.js b/typescript/aws-transfer-sftp-server/jest.config.js new file mode 100644 index 0000000000..c4f95b0aea --- /dev/null +++ b/typescript/aws-transfer-sftp-server/jest.config.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const config = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; +exports.default = config; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiamVzdC5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJqZXN0LmNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLE1BQU0sTUFBTSxHQUFXO0lBQ3JCLGVBQWUsRUFBRSxNQUFNO0lBQ3ZCLEtBQUssRUFBRSxDQUFDLGdCQUFnQixDQUFDO0lBQ3pCLFNBQVMsRUFBRSxDQUFDLGNBQWMsQ0FBQztJQUMzQixTQUFTLEVBQUU7UUFDVCxhQUFhLEVBQUUsU0FBUztLQUN6QjtDQUNGLENBQUM7QUFDRixrQkFBZSxNQUFNLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IENvbmZpZyB9IGZyb20gJ2plc3QnO1xuXG5jb25zdCBjb25maWc6IENvbmZpZyA9IHtcbiAgdGVzdEVudmlyb25tZW50OiAnbm9kZScsXG4gIHJvb3RzOiBbJzxyb290RGlyPi90ZXN0J10sXG4gIHRlc3RNYXRjaDogWycqKi8qLnRlc3QudHMnXSxcbiAgdHJhbnNmb3JtOiB7XG4gICAgJ14uK1xcXFwudHN4PyQnOiAndHMtamVzdCdcbiAgfVxufTtcbmV4cG9ydCBkZWZhdWx0IGNvbmZpZztcbiJdfQ== \ No newline at end of file diff --git a/typescript/aws-transfer-sftp-server/package.json b/typescript/aws-transfer-sftp-server/package.json index 29c0033459..94c4a3ee79 100644 --- a/typescript/aws-transfer-sftp-server/package.json +++ b/typescript/aws-transfer-sftp-server/package.json @@ -19,16 +19,16 @@ "cdk": "cdk" }, "devDependencies": { - "@types/jest": "^29.5.2", - "@types/node": "18.15.11", - "aws-cdk": "*", - "jest": "^29.5.0", - "ts-jest": "^29.1.0", - "ts-node": "^10.9.1", - "typescript": "~5.1.6" + "@types/jest": "^29.5.14", + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", + "jest": "^29.7.0", + "ts-jest": "^29.2.5", + "ts-node": "^10.9.2", + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "^2.0.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0" } } diff --git a/typescript/aws-transfer-sftp-server/test/aws-transfer-sftp-server.test.ts b/typescript/aws-transfer-sftp-server/test/aws-transfer-sftp-server.test.ts index 3ed27f187a..dedeb1f3d2 100644 --- a/typescript/aws-transfer-sftp-server/test/aws-transfer-sftp-server.test.ts +++ b/typescript/aws-transfer-sftp-server/test/aws-transfer-sftp-server.test.ts @@ -4,6 +4,7 @@ import { } from 'aws-cdk-lib'; import { Template } from 'aws-cdk-lib/assertions'; import { SftpServerStack } from '../aws-transfer-sftp-server'; +import { normalizeTemplate } from '../../test-utils/normalize-template'; test('SftpServerStack has required resources', () => { const app = new cdk.App(); @@ -23,5 +24,8 @@ test('SftpServerStack has required resources', () => { dataBucket: bucket, }); - expect(Template.fromStack(stack)).toMatchSnapshot(); + // Normalize the template before snapshot comparison + const template = Template.fromStack(stack); + const normalizedTemplate = normalizeTemplate(template.toJSON()); + expect(normalizedTemplate).toMatchSnapshot(); }); diff --git a/typescript/backup-s3/package.json b/typescript/backup-s3/package.json index f51898e68e..74caa81be5 100644 --- a/typescript/backup-s3/package.json +++ b/typescript/backup-s3/package.json @@ -10,16 +10,16 @@ "cdk": "cdk" }, "devDependencies": { - "@types/jest": "^26.0.10", - "@types/node": "10.17.27", - "jest": "^26.4.2", - "ts-jest": "^26.2.0", - "aws-cdk": "2.80.0", - "ts-node": "^9.0.0", - "typescript": "~4.6.0" + "@types/jest": "^29.5.14", + "@types/node": "22.7.9", + "jest": "^29.7.0", + "ts-jest": "^29.2.5", + "aws-cdk": "2.1004.0", + "ts-node": "^10.9.2", + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "2.80.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0", "source-map-support": "^0.5.16" } diff --git a/typescript/bedrock-kb-agent/.gitignore b/typescript/bedrock-kb-agent/.gitignore deleted file mode 100644 index 8952faab5f..0000000000 --- a/typescript/bedrock-kb-agent/.gitignore +++ /dev/null @@ -1,81 +0,0 @@ -# Build, dist and temp folders -build/ -dist/ -tmp/ -temp/ - -# Compiled source -*.com -*.class -*.dll -*.exe -*.o -*.so -*.py[cod] -*$py.class - -# npm & bower -bower_components -node_modules -npm-debug.log -.npmignore - -# Caches # -*.sass-cache - -# Logs and databases -*.log -*.sql -*.sqlite - -# OS generated files -.DS_Store -Desktop.ini - -# Thumbnails -._* -ehthumbs.db -*[Tt]humbs.db - -# Files that might appear on external disks -.Spotlight-V100 -.Trashes - -# Packages # -# It's better to unpack these files and commit the raw source because -# git has its own built in compression methods. -*.7z -*.jar -*.rar -*.zip -*.gz -*.bzip -*.xz -*.lzma - -#packing-only formats -*.iso -*.tar - -# IDEs stuff -.idea - -#package management formats -*.dmg -*.xpi -*.gem -*.egg -*.deb -*.rpm - -# python -__pycache__/ - -# python environments -.env -.venv -env/ -venv/ - -# cdk -cdk.out diff --git a/typescript/bedrock-kb-agent/.npmignore b/typescript/bedrock-kb-agent/.npmignore deleted file mode 100644 index c1d6d45dcf..0000000000 --- a/typescript/bedrock-kb-agent/.npmignore +++ /dev/null @@ -1,6 +0,0 @@ -*.ts -!*.d.ts - -# CDK asset staging directory -.cdk.staging -cdk.out diff --git a/typescript/bedrock-kb-agent/README.md b/typescript/bedrock-kb-agent/README.md deleted file mode 100644 index 83b4af2648..0000000000 --- a/typescript/bedrock-kb-agent/README.md +++ /dev/null @@ -1,230 +0,0 @@ -## Intro - -### Knowledge bases and agents - -Knowledge bases and agents are essential tools in enhancing organizational efficiency and decision-making. Knowledge bases serve as centralized repositories of information, enabling quick access to accurate data, which reduces search times, improves productivity, and supports consistent communication across teams. Knowledge-based agents, on the other hand, utilize this information to make informed decisions and take appropriate actions based on their understanding of the environment, thereby facilitating learning and adaptation through continuous knowledge updates. Together, they streamline operations, reduce training time for new employees, and enhance customer support by providing instant access to resources, ultimately leading to improved organizational performance. - -### Amazon Bedrock Knowledge bases and agents - -Amazon Bedrock Knowledge Bases and Agents offer powerful tools for ingesting documentation and answering questions intelligently. This example demonstrates how to leverage these capabilities effectively. -Amazon Bedrock Knowledge Bases are used for implementing Retrieval Augmented Generation (RAG), a technique that enhances AI responses by incorporating contextual information from your company's data sources. The knowledge base ingests and processes your documentation, converting it into vector embeddings that can be efficiently searched. -Amazon Bedrock Agents, built on top of the knowledge base, provide a natural language interface to interact with the stored information3. These agents can be configured to not only retrieve relevant data but also to execute various actions, including leveraging third-party APIs, in response to user queries. - -By combining these technologies, you can create a system that: - -1. Ingests and processes your documentation semi-automatically -2. Retrieves relevant information based on user queries -3. Generates accurate and contextually appropriate responses -4. Performs additional actions or API calls as needed to supplement the information - -This approach enables the creation of highly customized, intelligent applications that can efficiently answer questions about your specific documentation while also integrating external data sources when necessary - -#### AWS Powertools - -For this examples we leverage [AWS Powertools](https://docs.powertools.aws.dev/lambda/python/latest/) for lambda. -The reason is, AWS Powertools is essential for optimizing AWS Lambda functions, providing a suite of utilities that enhance performance, monitoring, and security while reducing the complexity of serverless application development. -By implementing best practices such as structured logging, custom metrics, and distributed tracing, AWS Powertools enables developers to focus on business logic rather than boilerplate code, streamlining the development process. -Additionally, it supports multiple programming languages and integrates seamlessly with AWS services, making it a versatile tool for teams looking to improve their serverless architecture and operational efficiency. - -### Architecture - -![Architecture](architecture.drawio.png "Architecture") - -To understand the architecture of this solution, let’s break it down step by step, moving from left to right in the system's workflow: - -#### **User Interaction and REST API** - -- **User Interaction**: The system begins with user interaction through REST API calls. These calls can be initiated from a **Web UI**, enabling users to interact with the solution seamlessly. -- **Security with Amazon Cognito**: All REST API interactions are secured using **Amazon Cognito**. This ensures that only authenticated and authorized users can access the APIs. Users must register and authenticate via Cognito to obtain valid tokens, which are included in the API requests for validation. - -#### **API Gateway** - -- The **Amazon API Gateway** acts as the entry point for all REST API requests. It exposes the system's functionality by routing requests to the appropriate backend services. -- API Gateway integrates with Amazon Cognito to validate user credentials before forwarding requests, ensuring robust security and fine-grained access control. - -#### **AWS Lambda** - -- Once the request passes through the API Gateway, it is processed by **AWS Lambda** functions. These serverless functions handle business logic and interact with other components of the architecture. -- Lambda functions are responsible for querying and interacting with: - - **Knowledge Bases**: These could include databases or other structured repositories of information. - - **Agents**: External or internal systems that perform specific tasks or provide additional data. - -#### **Data Storage in Amazon S3** - -- The data to be processed or searched by the knowledge base and agents is stored in an **Amazon S3 bucket**. -- For this specific use case, it is assumed that users manually upload documents to the S3 bucket. This allows the system to process these documents as part of its operations. - -#### **Key Features of the Architecture** - -1. **Security**: By leveraging Amazon Cognito and JWT-based authentication, the architecture ensures secure access to APIs without requiring session management overhead[6]. -2. **Scalability**: With serverless components like AWS Lambda and S3, the system is highly scalable, capable of handling varying workloads efficiently. -3. **Modularity**: Each component (API Gateway, Lambda, Cognito, S3) is independent yet seamlessly integrated, allowing for easy updates or replacements without disrupting the entire system. - -This architecture demonstrates a robust design for handling secure user interactions, efficient data processing, and scalable storage using AWS services. It aligns with best practices in solution architecture by ensuring security, scalability, and maintainability. - -## Deploying the CDK Stack: A Step-by-Step Guide - -Follow the these steps to install the example: - -### 1. Install Dependencies - -Begin by installing the necessary npm dependencies. Navigate to the top-level directory of your project in your terminal and run: - -``` -npm install -``` - -This command will fetch and install all required packages specified in your `package.json` file. - -### 2. Bootstrap CDK - -Before deploying your stack, you need to bootstrap CDK in your AWS account and region. This process sets up the necessary resources for CDK to manage deployments. Execute the following command, replacing `` and `` with your AWS account ID and desired region: - -``` -npx cdk bootstrap aws:/// -``` - -For example, if your account ID is 123456789012 and you're deploying to us-east-1, the command would be: - -``` -npx cdk bootstrap aws://123456789012/us-east-1 -``` - -### 3. Deploy the Stack - -With the dependencies installed and CDK bootstrapped, you're ready to deploy your stack. Simply run: - -``` -npx cdk deploy -``` - -This command synthesizes a CloudFormation template from your CDK code and deploys it to your AWS account, creating or updating the specified resources. - -By following these steps, you'll have your CDK stack up and running in your AWS environment, ready to manage your infrastructure efficiently. - -### 4. Knowledge Base Configuration and Document Processing - -After completing the installation process, you'll be able to access your newly created Knowledge Base within Amazon Bedrock. To view and manage your Knowledge Base: - -1. Navigate to the AWS Management Console -2. Locate and select the Amazon Bedrock service -3. Choose "Knowledge and datasource" from the sidebar menu - -This will display your Knowledge Base and its associated S3 bucket, which serves as the repository for documents used in your Knowledge Base. - -![Knowledge Base and its associated S3 bucket](./media/kb-s3.png "Knowledge Base and its associated S3 bucket") - -### Document Upload and Synchronization - -To populate your Knowledge Base with relevant information: - -1. Select an appropriate document for ingestion. For this exercise, we recommend using the [Amazon S3 user manual](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-userguide.pdf), which provides a comprehensive overview of S3 functionality. -2. Upload the chosen document to the S3 bucket directory specified in your Knowledge Base configuration. -3. After successful upload, locate and press the "Synchronize" button within the Bedrock console. - -The synchronization process is a crucial step that accomplishes the following: - -- It triggers the creation of embeddings for the uploaded documents. -- These embeddings are vector representations of the text content, enabling efficient semantic search and retrieval. -- The system uses a specified embeddings model to convert the textual data into these high-dimensional vectors. - -### Behind the Scenes - -During synchronization, Amazon Bedrock performs several important tasks: - -1. **Document Chunking**: The uploaded content is divided into manageable chunks, optimizing it for processing and retrieval. - -2. **Embedding Generation**: Each chunk is processed through the selected embeddings model, transforming the text into numerical vectors. - -3. **Vector Storage**: The generated embeddings are stored in the configured vector database, which could be Amazon OpenSearch Serverless or another compatible service. - -4. **Metadata Association**: Any relevant metadata from the original documents is preserved and associated with the corresponding embeddings. - -This process enables your Knowledge Base to efficiently search and retrieve relevant information when queried, forming the foundation for powerful retrieval-augmented generation (RAG) applications. - -By following these steps, you'll have successfully initialized and populated your Amazon Bedrock Knowledge Base, preparing it for use in various AI-driven tasks and applications. - -### Test the Knowledge Base with the AWS console - -To interact with Amazon Bedrock Knowledge Bases using the AWS console, you first need to navigate to the Amazon Bedrock service and select "Knowledge bases" from the left navigation pane. -You will notice the new created knowledge base named: `KBBedrockKb.....` select it and then use the knowledge base chat to query information or generate AI responses based on the ingested data. For this step you need to choose a model, for this exercise we sued Anthropic/Claude 3 Haiku. - -If you choose to use the `Amazon S3 user manual` you can test the Knowledge Base you can ask for questions about S3. - -#### Amazon Bedrock Model access - -In order to be able to use foundation models you need to request access for. In order to do this navigate to the Amazon Bedrock service and select "Model access" from the left navigation pane. -On the Model access page, users can review the End User License Agreement (EULA) for each model and then click "Modify model access" to select the desired models. After selecting the models, users should review the terms, submit any required use case details (such as for Anthropic models), and click "Submit" to request access, which may take several minutes to be reflected in the console. - -For this exercise we recommend you to request access for all Anthropic/Claude 3 models. - -## Test it with AWS Console - -The interaction/functionality from section `Test the Knowledge Base with the AWS console` is can be done programmatic as well as code. -For this exercise we prepared two distinct functionalities, the Knowledge Base RAG query and Agent query. -The codebase provide two Amazon Powertools lambdas let's try to explore them. - -### Knowledge Base RAG query and agent query - -The code is available in the `api/example-powertools/example.py` file. - -The AWS Lambda function in this code is designed to be triggered by API Gateway when a REST request is received. -API Gateway routes incoming HTTP requests to the appropriate Lambda function based on the configured API endpoints. -When a request matches a defined route, API Gateway invokes the Lambda function, passing the request details as an event object. - -You can test the lambda function from the AWS console using the event the predefined events with the Lambda AWS console as follow steps. - -First navigate to the Lambda function `BedrockKbAgentStack-ExampleLambdaFn....` in the AWS console and click on the "Test" tab. -Create a new test event by selecting "Configure test event" from the dropdown menu, then enter `..test/lambda-events/ask-kb.json` JSON payload in the event body. Finally, click "Test" to execute the Lambda function with your specified event, and view the results in the console's execution output. - -Same logic for agent query, with the difference that for the agent query you need to use `..test/lambda-events/ask-agent.json` - -This is how the `ask-kb` event response looks like - -```json -{ - "statusCode": 200, - "body": "{\"conv_id\":\"0b772d3a-3b56-4bb4-ac8f-ee5d4272dc89\",\"question\":\"what is s3?\",\"answer\":\"Amazon S3 (Simple Storage Service) is an object storage service offered by Amazon Web Services (AWS). It allows users to store and retrieve data of any amount or type, from anywhere on the web. Some key features of Amazon S3 include:\\n\\n- Storage classes: S3 offers a range of storage classes designed for different use cases, such as frequently accessed data, infrequently accessed data, and archived data.\\n- Data protection: S3 is designed to provide 99.999999999% durability and 99.99% availability of objects over a given year.\\n- Lifecycle management: Users can configure lifecycle rules to automatically transition objects between storage classes or expire objects that reach the end of their lifetimes.\\n- Access management: S3 provides various access management tools to control who can access the stored data and what actions they can perform.\"}", - "isBase64Encoded": false, - "headers": { - "Content-Type": "application/json" - }, - "cookies": [] -} -``` - -Let's try to understand it - -1. for the exercise we ingest the `S3 user manual` and this means we can ask S# specific questions, for this case we send the question: `what is s3?` -2. the Knowledge Base RAG answer is: `Amazon S3 (Simple Storage Service) is an object storage ....` -3. the method returns also conversation id, useful if you want to create a longer conversation. - -This is how the `ask-kb` event response looks like - -```json -{ - "statusCode": 200, - "body": "{\"conv_id\":\"adc56b78a40b4a99bf82c5236596bc01\",\"question\":\"how is the weather in London?\",\"answer\":\"The weather in London is rainy\"}", - "isBase64Encoded": false, - "headers": { - "Content-Type": "application/json" - }, - "cookies": [] -} -``` - -Let's try to understand it. - -This event is routed to the Knowledge Base Agent, the source code for this is in the `./api/example-agent/example.py`. -This file s is simple implementation of a Knowledge Base Agent. - -The Knowledge Base Agent, implemented in example.py, is designed to recognize and respond to weather-related questions for specific locations. This agent utilizes a llm mechanisms to determine the location mentioned in a query and provides a mock weather forecast for that location. -The implementation includes a /weather endpoint that generates a random weather condition (either "rainy" or "sunny") for the given location, demonstrating a basic example of how a knowledge-based system can process and respond to user queries. - -## Destroy - -In order to remove this example run: - -``` -npx cdk destroy -``` diff --git a/typescript/bedrock-kb-agent/api/example-agent/example.py b/typescript/bedrock-kb-agent/api/example-agent/example.py deleted file mode 100644 index 534c891f1a..0000000000 --- a/typescript/bedrock-kb-agent/api/example-agent/example.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -Copyright 2023 Amazon.com, Inc. and its affiliates. All Rights Reserved. - -Licensed under the Amazon Software License (the "License"). -You may not use this file except in compliance with the License. -A copy of the License is located at - - http://aws.amazon.com/asl/ - -or in the "license" file accompanying this file. This file is distributed -on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -express or implied. See the License for the specific language governing -permissions and limitations under the License. -""" - -from aws_lambda_powertools import Logger, Tracer -from aws_lambda_powertools.event_handler import BedrockAgentResolver -from aws_lambda_powertools.utilities.data_masking import DataMasking -from aws_lambda_powertools.utilities.typing import LambdaContext -from aws_lambda_powertools.event_handler.openapi.params import Body, Query -import json -import sys -from typing import Annotated -import random - -tracer = Tracer() -logger = Logger() -data_masker = DataMasking() -app = BedrockAgentResolver() - - -@app.get( - "/weather", - description="Provides the weather forecast for a given location", -) -@tracer.capture_method -def describe_scrap( - location: Annotated[ - str, - Query(description="The location form where the weather will be forecasted."), - ] -) -> Annotated[str, Body(description="The weather forecast for a given location")]: - weather = "rainy" if bool(random.getrandbits(1)) else "sunny" - return f"The weather in {location} is {weather}" - - -@logger.inject_lambda_context -@tracer.capture_lambda_handler -@tracer.capture_method -def lambda_handler(event: dict, context: LambdaContext): - logger.info(event) - return app.resolve(event, context) - - -if __name__ == "__main__": - openapi_file = "openapi.json" - with open(openapi_file, "w") as f: - f.write(app.get_openapi_json_schema()) - print(f"\n\n{openapi_file} was generated.\n\n") diff --git a/typescript/bedrock-kb-agent/api/example-agent/openapi.json b/typescript/bedrock-kb-agent/api/example-agent/openapi.json deleted file mode 100644 index 79555da5d1..0000000000 --- a/typescript/bedrock-kb-agent/api/example-agent/openapi.json +++ /dev/null @@ -1 +0,0 @@ -{"openapi": "3.0.3", "info": {"title": "Powertools API", "version": "1.0.0"}, "servers": [{"url": "/"}], "paths": {"/weather": {"get": {"summary": "GET /weather", "description": "Provides the weather forecast for a given location", "operationId": "describe_scrap_weather_get", "parameters": [{"description": "The location form where the weather will be forecasted.", "required": true, "schema": {"type": "string", "title": "Location", "description": "The location form where the weather will be forecasted."}, "name": "location", "in": "query"}], "responses": {"422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}, "200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "string", "title": "Return", "description": "The weather forecast for a given location"}}}}}}}}, "components": {"schemas": {"HTTPValidationError": {"properties": {"detail": {"items": {"$ref": "#/components/schemas/ValidationError"}, "type": "array", "title": "Detail"}}, "type": "object", "title": "HTTPValidationError"}, "ValidationError": {"properties": {"loc": {"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "type": "array", "title": "Location"}, "type": {"type": "string", "title": "Error Type"}}, "type": "object", "required": ["loc", "msg", "type"], "title": "ValidationError"}}}} \ No newline at end of file diff --git a/typescript/bedrock-kb-agent/api/example-agent/requirements.dev.txt b/typescript/bedrock-kb-agent/api/example-agent/requirements.dev.txt deleted file mode 100644 index 1db657b6b3..0000000000 --- a/typescript/bedrock-kb-agent/api/example-agent/requirements.dev.txt +++ /dev/null @@ -1 +0,0 @@ -boto3 \ No newline at end of file diff --git a/typescript/bedrock-kb-agent/api/example-agent/requirements.txt b/typescript/bedrock-kb-agent/api/example-agent/requirements.txt deleted file mode 100644 index 905ca38ded..0000000000 --- a/typescript/bedrock-kb-agent/api/example-agent/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -aws-lambda-powertools -pydantic -aws_xray_sdk -jsonpath_ng diff --git a/typescript/bedrock-kb-agent/api/example-powertools/example.py b/typescript/bedrock-kb-agent/api/example-powertools/example.py deleted file mode 100644 index 7981de52d7..0000000000 --- a/typescript/bedrock-kb-agent/api/example-powertools/example.py +++ /dev/null @@ -1,258 +0,0 @@ -""" -Copyright 2024 Amazon.com, Inc. and its affiliates. All Rights Reserved. - -Licensed under the Amazon Software License (the "License"). -You may not use this file except in compliance with the License. -A copy of the License is located at - - http://aws.amazon.com/asl/ - -or in the "license" file accompanying this file. This file is distributed -on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -express or implied. See the License for the specific language governing -permissions and limitations under the License. -""" -import os -from typing import Optional -import boto3 -import json -import traceback -from aws_lambda_powertools.utilities.parser import ( - BaseModel, - Field, - parse -) - -from aws_lambda_powertools.event_handler import APIGatewayHttpResolver -from aws_lambda_powertools import Logger, Tracer -from aws_lambda_powertools.utilities.data_masking import DataMasking -from aws_lambda_powertools.utilities.typing import LambdaContext -from aws_lambda_powertools.logging import correlation_paths -import uuid - -# Docs: https://docs.powertools.aws.dev/lambda/python/latest/ - -tracer = Tracer() -logger = Logger() -data_masker = DataMasking() -app = APIGatewayHttpResolver() -bedrock_agent_runtime = boto3.client('bedrock-agent-runtime') - -class Question(BaseModel): - """Example response model""" - conv_id: Optional[str] = Field(description="The unique id for the conversation", - default=None) - question: str = Field(description="The text of the question") - - -class Answer(BaseModel): - """Example response model""" - conv_id: str = Field(description="The unique id for the conversation ") - question: str = Field(description="The text of the question") - answer: str = Field(description="The text of the response") - - -def _ask_rag(question: str, - conversation_id: str, - knowledge_base_id: str): - - model_arn = "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-haiku-20240307-v1:0" - - response = ( - bedrock_agent_runtime.retrieve_and_generate( - sessionId=conversation_id, - input={"text": "What is S3?"}, - retrieveAndGenerateConfiguration={ - "type": "KNOWLEDGE_BASE", - "knowledgeBaseConfiguration": { - "knowledgeBaseId": knowledge_base_id, - "modelArn": model_arn, - "retrievalConfiguration": { - "vectorSearchConfiguration": {"numberOfResults": 10} - }, - }, - }, - ) - if conversation_id - else bedrock_agent_runtime.retrieve_and_generate( - input={"text": question}, - retrieveAndGenerateConfiguration={ - "type": "KNOWLEDGE_BASE", - "knowledgeBaseConfiguration": { - "knowledgeBaseId": knowledge_base_id, - "modelArn": model_arn, - "retrievalConfiguration": { - "vectorSearchConfiguration": {"numberOfResults": 10} - }, - }, - }, - ) - ) - - # Make the API call - # print(f"response=\n {json.dumps(response)}") - - answer = response.get("output", {}).get("text", "Not Found") - - citations = response.get("citations", []) - - citations = [ {"generatedResponse": citation.get("generatedResponsePart", {}).get("textResponsePart", {}).get("text", ""), - "retrievedReferences": [{"text": reference.get("content", {}).get("text"), - "location": reference.get("location",) - } for reference in citation.get("retrievedReferences", [])]} - for citation in citations] - - print(f"citations=\n {json.dumps(citations)}") - conversation_id = response.get("sessionId") - - return answer, citations, conversation_id - - -@app.post("/api/v1/example") -@tracer.capture_method -def answer_to_message() : - """ - Function to handle the POST request - """ - logger.info("Answer question form kb") - question_request = parse(event=app.current_event.json_body, model=Question) - - knowledge_base_id = os.environ.get("KNOWLEDGE_BASE_ID", None) - if knowledge_base_id is None: - return "Server Error", 500 - - try: - rag_answer = _ask_rag(question=question_request.question, - conversation_id=question_request.conv_id, - knowledge_base_id="AWXM2RSO70") - - return (Answer(conv_id=rag_answer[2], - question=question_request.question, - answer=rag_answer[0]), - 200) - except Exception as ex: - logger.error(traceback.format_exc()) - return "Server Error", 500 - - -@app.post("/api/v1/weather") -@tracer.capture_method -def get_weather() : - """ - Function to handle the POST request - """ - logger.info("Get weather form the agent") - - aget_id = os.environ.get("AGENT_ID") - if not aget_id: - raise ValueError("AGENT_ID environment variable is not set") - - aget_alias_id = os.environ.get("AGENT_ALIAS_ID") - if not aget_alias_id: - raise ValueError("AGENT_ALIAS_ID environment variable is not set") - - question_request = parse(event=app.current_event.json_body, model=Question) - - conv_id = question_request.conv_id - if not conv_id: - conv_id = uuid.uuid4().hex - - - agent_response = bedrock_agent_runtime.invoke_agent( - agentId=aget_id, - agentAliasId=aget_alias_id, - sessionId=conv_id, - inputText=question_request.question, - enableTrace=True) - - traces = [] - chunks = [] - for event in agent_response.get("completion"): - if "trace" in event: - traces.append(event["trace"]) - if "chunk" in event: - chunks.append(event["chunk"]["bytes"].decode()) - - answer = "".join(chunks) - return Answer(conv_id=conv_id, - question=question_request.question, - answer=answer) ,200 - - -@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_HTTP) -@tracer.capture_lambda_handler -def lambda_handler(event: dict, context: LambdaContext): - """ - Lambda handler entry point - """ - # remove sensitive information from the event before writing to CloudWatch - # logger.info( - # data_masker.erase( - # event, - # fields=[ - # "headers.authorization", - # ], - # ) - # ) - logger.info(event) - return app.resolve(event, context) - - -if __name__ == "__main__": - # event = { - # "version": "2.0", - # "routeKey": "POST /api/v1/example", - # "rawPath": "/api/v1/example", - # "rawQueryString": "", - # "headers": { - # "accept-encoding": "gzip", - # "authorization": "AAA", - # }, - # "requestContext": { - # "authorizer": {}, - # "http": { - # "method": "POST", - # "path": "/api/v1/example", - # "protocol": "HTTP/1.1", - # }, - # "routeKey": "POST /api/v1/example", - # "stage": "$default", - # }, - # "body": "{\"question\":\"what is S3?\"}", - # } - - event = { - "version": "2.0", - "routeKey": "POST /api/v1/weather", - "rawPath": "/api/v1/weather", - "rawQueryString": "", - "headers": { - "accept-encoding": "gzip", - "authorization": "AAA", - }, - "requestContext": { - "authorizer": {}, - "http": { - "method": "POST", - "path": "/api/v1/weather", - "protocol": "HTTP/1.1", - }, - "routeKey": "POST /api/v1/weather", - "stage": "$default", - }, - "body": "{\"question\":\"How is the weather in London?\"}", - } - - class MockContext: - def __init__(self): - self.function_name = "test_function" - self.memory_limit_in_mb = 128 - self.invoked_function_arn = ( - "arn:aws:lambda:us-east-1:123456789012:function:test_function" - ) - self.aws_request_id = "test_request_id" - - context = MockContext() - - response = lambda_handler(event, context) - print(json.dumps(response)) diff --git a/typescript/bedrock-kb-agent/api/example-powertools/requirements.dev.txt b/typescript/bedrock-kb-agent/api/example-powertools/requirements.dev.txt deleted file mode 100644 index 1db657b6b3..0000000000 --- a/typescript/bedrock-kb-agent/api/example-powertools/requirements.dev.txt +++ /dev/null @@ -1 +0,0 @@ -boto3 \ No newline at end of file diff --git a/typescript/bedrock-kb-agent/api/example-powertools/requirements.txt b/typescript/bedrock-kb-agent/api/example-powertools/requirements.txt deleted file mode 100644 index 905ca38ded..0000000000 --- a/typescript/bedrock-kb-agent/api/example-powertools/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -aws-lambda-powertools -pydantic -aws_xray_sdk -jsonpath_ng diff --git a/typescript/bedrock-kb-agent/architecture.drawio.png b/typescript/bedrock-kb-agent/architecture.drawio.png deleted file mode 100644 index b108ff3e72..0000000000 Binary files a/typescript/bedrock-kb-agent/architecture.drawio.png and /dev/null differ diff --git a/typescript/bedrock-kb-agent/bin/bedrock-kb-agent.ts b/typescript/bedrock-kb-agent/bin/bedrock-kb-agent.ts deleted file mode 100644 index 05852440ba..0000000000 --- a/typescript/bedrock-kb-agent/bin/bedrock-kb-agent.ts +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env node -import * as cdk from 'aws-cdk-lib'; -import { BedrockKbAgentStack } from '../lib/bedrock-kb-agent-stack'; - -const app = new cdk.App(); -new BedrockKbAgentStack(app, 'BedrockKbAgentStack', { - /* If you don't specify 'env', this stack will be environment-agnostic. - * Account/Region-dependent features and context lookups will not work, - * but a single synthesized template can be deployed anywhere. */ - - /* Uncomment the next line to specialize this stack for the AWS Account - * and Region that are implied by the current CLI configuration. */ - // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, - - /* Uncomment the next line if you know exactly what Account and Region you - * want to deploy the stack to. */ - // env: { account: '123456789012', region: 'us-east-1' }, - - /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ -}); \ No newline at end of file diff --git a/typescript/bedrock-kb-agent/cdk.json b/typescript/bedrock-kb-agent/cdk.json deleted file mode 100644 index 76e9a1c819..0000000000 --- a/typescript/bedrock-kb-agent/cdk.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "app": "npx ts-node --prefer-ts-exts bin/bedrock-kb-agent.ts", - "watch": { - "include": [ - "**" - ], - "exclude": [ - "README.md", - "cdk*.json", - "**/*.d.ts", - "**/*.js", - "tsconfig.json", - "package*.json", - "yarn.lock", - "node_modules", - "test" - ] - }, - "context": { - "@aws-cdk/aws-lambda:recognizeLayerVersion": true, - "@aws-cdk/core:checkSecretUsage": true, - "@aws-cdk/core:target-partitions": [ - "aws", - "aws-cn" - ], - "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, - "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, - "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, - "@aws-cdk/aws-iam:minimizePolicies": true, - "@aws-cdk/core:validateSnapshotRemovalPolicy": true, - "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, - "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, - "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, - "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, - "@aws-cdk/core:enablePartitionLiterals": true, - "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, - "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, - "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, - "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, - "@aws-cdk/aws-route53-patters:useCertificate": true, - "@aws-cdk/customresources:installLatestAwsSdkDefault": false, - "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, - "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, - "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, - "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, - "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, - "@aws-cdk/aws-redshift:columnId": true, - "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, - "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, - "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, - "@aws-cdk/aws-kms:aliasNameRef": true, - "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, - "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, - "@aws-cdk/aws-efs:denyAnonymousAccess": true, - "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, - "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, - "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, - "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, - "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, - "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, - "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true, - "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true, - "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true, - "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true, - "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true, - "@aws-cdk/aws-eks:nodegroupNameAttribute": true, - "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, - "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, - "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, - "@aws-cdk/aws-s3:keepNotificationInImportedBucket": false, - "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true, - "@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true, - "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true, - "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true, - "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true, - "@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true, - "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true, - "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true, - "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true - } -} diff --git a/typescript/bedrock-kb-agent/lib/bedrock-kb-agent-stack.ts b/typescript/bedrock-kb-agent/lib/bedrock-kb-agent-stack.ts deleted file mode 100644 index fdf05cd5ec..0000000000 --- a/typescript/bedrock-kb-agent/lib/bedrock-kb-agent-stack.ts +++ /dev/null @@ -1,213 +0,0 @@ -/** - * Copyright 2023 Amazon.com, Inc. and its affiliates. All Rights Reserved. - * - * Licensed under the Amazon Software License (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/asl/ - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -import * as cdk from "aws-cdk-lib"; -import * as lambdaPython from "@aws-cdk/aws-lambda-python-alpha"; -import * as genai from "@cdklabs/generative-ai-cdk-constructs"; -import { ApiGatewayV2CloudFrontConstruct } from "./constructs/apigatewayv2-cloudfront-construct"; -import { ApiGatewayV2LambdaConstruct } from "./constructs/apigatewayv2-lambda-construct"; -import { CloudFrontS3WebSiteConstruct } from "./constructs/cloudfront-s3-website-construct"; -import { Construct } from "constructs"; -import { CognitoWebNativeConstruct } from "./constructs/cognito-web-native-construct"; -import { S3Construct } from "./constructs/s3-construct"; -import { NagSuppressions } from "cdk-nag"; - -export class BedrockKbAgentStack extends cdk.Stack { - constructor(scope: Construct, id: string, props: cdk.StackProps) { - super(scope, id, props); - - const cognito = new CognitoWebNativeConstruct(this, "Cognito", props); - - const webAppBuildPath = "./web-app/dist"; - - const website = new CloudFrontS3WebSiteConstruct(this, "WebApp", { - userPoolId: cognito.userPool.userPoolId, - appClientId: cognito.webClientId, - identityPoolId: cognito.identityPoolId, - webSiteBuildPath: webAppBuildPath, - withApi: true, - // If you have created a bucket to be used as Storage for amplify - // use the following prop - // storageBucketName: myBucket.bucketName - }); - - const api = new ApiGatewayV2CloudFrontConstruct(this, "Api", { - cloudFrontDistribution: website.cloudFrontDistribution, - userPool: cognito.userPool, - userPoolClient: cognito.webClientUserPool, - }); - - const exampleFn = new lambdaPython.PythonFunction(this, "ExampleLambdaFn", { - runtime: cdk.aws_lambda.Runtime.PYTHON_3_10, - handler: "lambda_handler", - index: "example.py", - entry: "./api/example-powertools", - timeout: cdk.Duration.seconds(60), - environment: {}, - initialPolicy: [ - new cdk.aws_iam.PolicyStatement({ - effect: cdk.aws_iam.Effect.ALLOW, - actions: ["bedrock:RetrieveAndGenerate", "bedrock:Retrieve", "bedrock:InvokeModel", "bedrock:InvokeAgent"], - resources: ["*"], - }), - ], - - }); - - new ApiGatewayV2LambdaConstruct(this, "ExampleKBGateway", { - lambdaFn: exampleFn, - routePath: "/api/v1/example", - methods: [ - cdk.aws_apigatewayv2.HttpMethod.POST, - ], - api: api.apiGatewayV2, - }); - - new ApiGatewayV2LambdaConstruct(this, "ExampleAgentGateway", { - lambdaFn: exampleFn, - routePath: "/api/v1/weather", - methods: [ - cdk.aws_apigatewayv2.HttpMethod.POST, - ], - api: api.apiGatewayV2, - }); - const knowledgeBaseBucket = new S3Construct( - this, - `KnowledgeBase-${this.stackName}`, - {}, - ); - const knowledgeBase = this.buildKnowledgeBase({ - bucket: knowledgeBaseBucket.dataBucket, - knowledgeBaseFolder: "knowledge-base/", - }); - - exampleFn.addEnvironment("KNOWLEDGE_BASE_ID", knowledgeBase.knowledgeBaseId); - - const agent = this.buildAgent([knowledgeBase]); - - if (!agent.aliasName) { - throw Error("The agent must have a valid alias"); - } - if (!agent.aliasId) { - throw Error("The agent must have a valid alias"); - } - - exampleFn.addEnvironment("AGENT_ID", agent.agentId); - exampleFn.addEnvironment("AGENT_ALIAS_ID", agent.aliasId); - } - - private buildKnowledgeBase({ - bucket, - knowledgeBaseFolder, - }: { - bucket: cdk.aws_s3.Bucket; - knowledgeBaseFolder: string; - }): genai.bedrock.KnowledgeBase { - const knowledgeBase = new genai.bedrock.KnowledgeBase( - this, - "BedrockKnowledgeBase", - { - embeddingsModel: - genai.bedrock.BedrockFoundationModel.TITAN_EMBED_TEXT_V1, - instruction: - "Use this knowledge base to answer questions about Amazon Web Services products.", - }, - ); - - new genai.bedrock.S3DataSource(this, "DataSource", { - bucket: bucket, - knowledgeBase: knowledgeBase, - dataSourceName: `DataSource-${this.stackName}`, - inclusionPrefixes: [knowledgeBaseFolder], - chunkingStrategy: genai.bedrock.ChunkingStrategy.FIXED_SIZE, - }); - - NagSuppressions.addResourceSuppressionsByPath( - this, - `/${this.node.id}/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/DefaultPolicy/Resource`, - [ - { - id: "AwsSolutions-IAM5", - reason: - "Resource automatically created by the Bedrock constructs and cannot be modified", - appliesTo: ["Resource::*"], - }, - ], - true, - ); - - return knowledgeBase; - } - - private buildAgent(knowledgeBases: genai.bedrock.KnowledgeBase[]): genai.bedrock.Agent { - const fn = new lambdaPython.PythonFunction(this, "agent", { - runtime: cdk.aws_lambda.Runtime.PYTHON_3_10, - handler: "lambda_handler", - index: "example.py", - entry: "./api/example-agent", - description: `Agent lambda for [${this.stackName}]`, - timeout: cdk.Duration.minutes(4), - memorySize: 10240, - - initialPolicy: [ - new cdk.aws_iam.PolicyStatement({ - effect: cdk.aws_iam.Effect.ALLOW, - actions: ["bedrock:InvokeModel"], - resources: ["*"], - }), - ], - reservedConcurrentExecutions: 1, - }); - - const timestamp = new Date().toISOString().replace(/[-:T]/g, "").split(".")[0]; - const aliasName = `agent-${timestamp}`; - - const agent = new genai.bedrock.Agent(this, "Agent", { - name: `Agent-${this.stackName}`, - aliasName: aliasName, - foundationModel: - genai.bedrock.BedrockFoundationModel.ANTHROPIC_CLAUDE_V2_1, - instruction: - "You are a helpful and friendly agent that answers weather related questions.", - knowledgeBases: knowledgeBases, - }); - - const actionGroup = new genai.bedrock.AgentActionGroup( - this, - "ActionGroup", - { - actionGroupName: "weatherAG", - description: - "Use these functions to get information about weather in a given location.", - actionGroupExecutor: { - lambda: fn, - }, - actionGroupState: "ENABLED", - apiSchema: genai.bedrock.ApiSchema.fromAsset("./api/example-agent/openapi.json",), - }, - ); - - agent.addActionGroup(actionGroup); - new cdk.CfnOutput(this, "AgentId", { value: agent.agentId }); - if (agent.aliasId) { - new cdk.CfnOutput(this, "AgentAliasId", { value: agent.aliasId }); - } - if (agent.aliasName) { - new cdk.CfnOutput(this, "AgentAliasName", { value: agent.aliasName }); - } - - return agent; - } -} diff --git a/typescript/bedrock-kb-agent/lib/constructs/apigatewayv2-cloudfront-construct.ts b/typescript/bedrock-kb-agent/lib/constructs/apigatewayv2-cloudfront-construct.ts deleted file mode 100644 index 654fb6f4a2..0000000000 --- a/typescript/bedrock-kb-agent/lib/constructs/apigatewayv2-cloudfront-construct.ts +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Copyright 2023 Amazon.com, Inc. and its affiliates. All Rights Reserved. - * - * Licensed under the Amazon Software License (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/asl/ - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -import * as cdk from "aws-cdk-lib"; -import { Construct } from "constructs"; - -export interface ApiGatewayV2CloudFrontProps extends cdk.StackProps { - /** - * The Cognito UserPool to use for the default authorizer - */ - readonly userPool: cdk.aws_cognito.UserPool; - /** - * The Cognito UserPoolClient to use for the default authorizer - */ - readonly userPoolClient: cdk.aws_cognito.UserPoolClient; - /** - * The CloudFront Distribution to attach the `/api/*` behavior - */ - readonly cloudFrontDistribution: cdk.aws_cloudfront.Distribution; -} - -const defaultProps: Partial = {}; - -/** - * Deploys Api gateway proxied through a CloudFront distribution at route `/api` - * - * Any Api's attached to the gateway should be located at `/api/*` so that requests are correctly proxied. - * Make sure Api's return the header `"Cache-Control" = "no-cache, no-store"` or CloudFront will cache responses - * - * CORS: allowed origins for local development: - * - https://example.com:3000, http://example.com:3000 - * - * For a more relaxed CORS posture, you can set `allowCredentials: false`, then set `allowOrigins: ["*"]` - * - * Creates: - * - ApiGatewayV2 HttpApi - */ -export class ApiGatewayV2CloudFrontConstruct extends Construct { - /** - * Returns the ApiGatewayV2 instance to attach lambdas or other routes - */ - public apiGatewayV2: cdk.aws_apigatewayv2.HttpApi; - - constructor(parent: Construct, name: string, props: ApiGatewayV2CloudFrontProps) { - super(parent, name); - - props = { ...defaultProps, ...props }; - - // get the parent stack reference for the stackName and the aws region - const stack = cdk.Stack.of(this); - - // init cognito authorizer - const cognitoAuth = new cdk.aws_apigatewayv2_authorizers.HttpUserPoolAuthorizer( - "apiAuth", - props.userPool, - { - userPoolClients: [props.userPoolClient], - }, - ); - - // init api gateway - const api = new cdk.aws_apigatewayv2.HttpApi(this, "Api", { - apiName: `${stack.stackName}Api`, - // corsPreflight: { - // allowHeaders: [ - // "Authorization", - // "Content-Type", - // "Origin", - // "X-Amz-Date", - // "X-Api-Key", - // "X-Amz-Security-Token", - // "X-Amz-User-Agent", - // ], - // allowMethods: [ - // // remove methods you don't use for tighter security - // cdk.aws_apigatewayv2.CorsHttpMethod.DELETE, - // cdk.aws_apigatewayv2.CorsHttpMethod.GET, - // cdk.aws_apigatewayv2.CorsHttpMethod.HEAD, - // cdk.aws_apigatewayv2.CorsHttpMethod.OPTIONS, - // cdk.aws_apigatewayv2.CorsHttpMethod.PATCH, - // cdk.aws_apigatewayv2.CorsHttpMethod.POST, - // cdk.aws_apigatewayv2.CorsHttpMethod.PUT, - // ], - // // allow origins for development. no origin is needed for cloudfront - // allowOrigins: ["*"], - // exposeHeaders: ["Access-Control-Allow-Origin"], - // maxAge: cdk.Duration.hours(1), - // allowCredentials: false, - // }, - defaultAuthorizer: cognitoAuth, - }); - - const apiUrl = `${api.httpApiId}.execute-api.${stack.region}.amazonaws.com`; - this.addBehaviorToCloudFrontDistribution(props.cloudFrontDistribution, apiUrl); - - // export any cf outputs - new cdk.CfnOutput(this, "GatewayUrl", { - value: `https://${apiUrl}`, - }); - - // assign public properties - this.apiGatewayV2 = api; - } - - /** - * Adds a proxy route from CloudFront /api to the api gateway url - * @param cloudFrontDistribution - * @param apiUrl - */ - private addBehaviorToCloudFrontDistribution( - cloudFrontDistribution: cdk.aws_cloudfront.Distribution, - apiUrl: string, - ) { - cloudFrontDistribution.addBehavior( - "/api/*", - new cdk.aws_cloudfront_origins.HttpOrigin(apiUrl, { - originSslProtocols: [cdk.aws_cloudfront.OriginSslPolicy.TLS_V1_2], - protocolPolicy: cdk.aws_cloudfront.OriginProtocolPolicy.HTTPS_ONLY, - }), - { - cachePolicy: new cdk.aws_cloudfront.CachePolicy(this, "CachePolicy", { - // required or CloudFront will strip the Authorization token from the request. - // must be in the cache policy - headerBehavior: - cdk.aws_cloudfront.CacheHeaderBehavior.allowList("Authorization"), - queryStringBehavior: cdk.aws_cloudfront.CacheQueryStringBehavior.all(), - enableAcceptEncodingGzip: true, - minTtl: cdk.Duration.seconds(0), - defaultTtl: cdk.Duration.seconds(0), - }), - originRequestPolicy: new cdk.aws_cloudfront.OriginRequestPolicy( - this, - "OriginRequestPolicy", - { - headerBehavior: cdk.aws_cloudfront.OriginRequestHeaderBehavior.allowList( - "User-Agent", - "Referer", - ), - // required or CloudFront will strip all query strings off the request - queryStringBehavior: - cdk.aws_cloudfront.OriginRequestQueryStringBehavior.all(), - }, - ), - allowedMethods: cdk.aws_cloudfront.AllowedMethods.ALLOW_ALL, - viewerProtocolPolicy: cdk.aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, - }, - ); - } -} diff --git a/typescript/bedrock-kb-agent/lib/constructs/apigatewayv2-lambda-construct.ts b/typescript/bedrock-kb-agent/lib/constructs/apigatewayv2-lambda-construct.ts deleted file mode 100644 index 6d04381d26..0000000000 --- a/typescript/bedrock-kb-agent/lib/constructs/apigatewayv2-lambda-construct.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright 2023 Amazon.com, Inc. and its affiliates. All Rights Reserved. - * - * Licensed under the Amazon Software License (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/asl/ - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -import * as cdk from "aws-cdk-lib"; -import { Construct } from "constructs"; - -export interface ApiGatewayV2LambdaConstructProps extends cdk.StackProps { - /** - * The lambda function - */ - readonly lambdaFn: cdk.aws_lambda.Function; - /** - * The apigatewayv2 route path - */ - readonly routePath: string; - /** - * Api methods supported by this API - */ - readonly methods: Array; - /** - * The ApiGatewayV2 HttpApi to attach the lambda - */ - readonly api: cdk.aws_apigatewayv2.HttpApi; -} - -const defaultProps: Partial = {}; - -/** - * Deploys a lambda and attaches it to a route on the apigatewayv2 - */ -export class ApiGatewayV2LambdaConstruct extends Construct { - constructor(parent: Construct, name: string, props: ApiGatewayV2LambdaConstructProps) { - super(parent, name); - - props = { ...defaultProps, ...props }; - - // add lambda policies - props.lambdaFn.grantInvoke(new cdk.aws_iam.ServicePrincipal("apigateway.amazonaws.com")); - - // add lambda integration - const lambdaFnIntegration = new cdk.aws_apigatewayv2_integrations.HttpLambdaIntegration( - "apiInt", - props.lambdaFn, - {}, - ); - - // add route to the api gateway - props.api.addRoutes({ - path: props.routePath, - methods: props.methods, - integration: lambdaFnIntegration, - }); - } -} diff --git a/typescript/bedrock-kb-agent/lib/constructs/cloudfront-s3-website-construct.ts b/typescript/bedrock-kb-agent/lib/constructs/cloudfront-s3-website-construct.ts deleted file mode 100644 index 476ebefeb5..0000000000 --- a/typescript/bedrock-kb-agent/lib/constructs/cloudfront-s3-website-construct.ts +++ /dev/null @@ -1,225 +0,0 @@ -/** - * Copyright 2024 Amazon.com, Inc. and its affiliates. All Rights Reserved. - * - * Licensed under the Amazon Software License (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/asl/ - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -import * as cdk from "aws-cdk-lib"; -import { Construct } from "constructs"; -import * as cloudfront_origins from "aws-cdk-lib/aws-cloudfront-origins"; - -export interface CloudFrontS3WebSiteConstructProps extends cdk.StackProps { - /** - * The path to the build directory of the web site, relative to the project root - * ex: "./app/dist" - */ - readonly webSiteBuildPath: string; - - /** - * The Arn of the WafV2 WebAcl. - */ - readonly webAclArn?: string; - /** - * The Cognito UserPoolId to authenticate users in the front-end - */ - readonly userPoolId: string; - - /** - * The Cognito AppClientId to authenticate users in the front-end - */ - readonly appClientId: string; - - /** - * The Cognito IdentityPoolId to authenticate users in the front-end - */ - readonly identityPoolId: string; - - /** - * The name of the bucket to use as storage using Amplify SDK if required - */ - readonly storageBucketName?: string; - - /** - * If you use Amplify SDK to access an API serving under /api path set this boolean to true - */ - readonly withApi?: boolean; -} - -const defaultProps: Partial = {}; - -/** - * Deploys a CloudFront Distribution pointing to an S3 bucket containing the deployed web application {webSiteBuildPath}. - * Creates: - * - S3 bucket - * - CloudFrontDistribution - * - OriginAccessIdentity - * - * On redeployment, will automatically invalidate the CloudFront distribution cache - */ -export class CloudFrontS3WebSiteConstruct extends Construct { - /** - * The origin access identity used to access the S3 website - */ - public originAccessIdentity: cdk.aws_cloudfront.OriginAccessIdentity; - - /** - * The cloud front distribution to attach additional behaviors like `/api` - */ - public cloudFrontDistribution: cdk.aws_cloudfront.Distribution; - - /** - * The name of the bucket where the frontend assets are stored - */ - public siteBucket: cdk.aws_s3.Bucket; - - constructor( - parent: Construct, - name: string, - props: CloudFrontS3WebSiteConstructProps, - ) { - super(parent, name); - - props = { ...defaultProps, ...props }; - const stack = cdk.Stack.of(this); - const region = stack.region; - - // When using Distribution, do not set the s3 bucket website documents - // if these are set then the distribution origin is configured for HTTP communication with the - // s3 bucket and won't configure the cloudformation correctly. - this.siteBucket = new cdk.aws_s3.Bucket(this, "WebApp", { - encryption: cdk.aws_s3.BucketEncryption.S3_MANAGED, - autoDeleteObjects: true, - blockPublicAccess: cdk.aws_s3.BlockPublicAccess.BLOCK_ALL, - removalPolicy: cdk.RemovalPolicy.DESTROY, - enforceSSL: true, - }); - - this.siteBucket.addToResourcePolicy( - new cdk.aws_iam.PolicyStatement({ - sid: "EnforceTLS", - effect: cdk.aws_iam.Effect.DENY, - principals: [new cdk.aws_iam.AnyPrincipal()], - actions: ["s3:*"], - resources: [ - this.siteBucket.bucketArn, - this.siteBucket.bucketArn + "/*", - ], - conditions: { Bool: { "aws:SecureTransport": "false" } }, - }), - ); - - const originAccessIdentity = new cdk.aws_cloudfront.OriginAccessIdentity( - this, - "OriginAccessIdentity", - ); - this.siteBucket.grantRead(originAccessIdentity); - - // const s3origin = new cdk.aws_cloudfront_origins.S3Origin(this.siteBucket, { - // originAccessIdentity: originAccessIdentity, - // }); - - const cloudFrontDistribution = new cdk.aws_cloudfront.Distribution( - this, - "WebAppDistribution", - { - defaultBehavior: { - // origin: s3origin, - origin: cloudfront_origins.S3BucketOrigin.withOriginAccessControl( - this.siteBucket, - ), - - cachePolicy: new cdk.aws_cloudfront.CachePolicy(this, "CachePolicy", { - defaultTtl: cdk.Duration.hours(1), - }), - allowedMethods: cdk.aws_cloudfront.AllowedMethods.ALLOW_ALL, - viewerProtocolPolicy: - cdk.aws_cloudfront.ViewerProtocolPolicy.HTTPS_ONLY, - }, - errorResponses: [ - { - httpStatus: 404, - ttl: cdk.Duration.hours(0), - responseHttpStatus: 200, - responsePagePath: "/index.html", - }, - ], - defaultRootObject: "index.html", - webAclId: props.webAclArn, - minimumProtocolVersion: - cdk.aws_cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021, // Required by security - }, - ); - - const amplifyConfig: any = { - Auth: { - Cognito: { - allowGuestAccess: false, - region: region, - userPoolId: props.userPoolId, - userPoolClientId: props.appClientId, - identityPoolId: props.identityPoolId, - }, - }, - }; - if (props.withApi) { - // Add or remove extra config properties you may need here like API or storage - - amplifyConfig.API = { - REST: { - api: { - endpoint: `./api`, - region: region, - }, - }, - }; - } - if (props.storageBucketName) { - amplifyConfig.Storage = { - S3: { - region: region, - bucket: props.storageBucketName, - }, - }; - } - - new cdk.aws_s3_deployment.BucketDeployment(this, "DeployWithInvalidation", { - sources: [ - cdk.aws_s3_deployment.Source.asset(props.webSiteBuildPath), // Main webapp from root directory - cdk.aws_s3_deployment.Source.jsonData("config.json", amplifyConfig), // Amplify config file - ], - destinationBucket: this.siteBucket, - distribution: cloudFrontDistribution, // this assignment, on redeploy, will automatically invalidate the cloudfront cache - distributionPaths: ["/*"], - // default of 128 isn't large enough for larger website deployments. More memory doesn't improve the performance. - // You want just enough memory to guarantee deployment - memoryLimit: 512, - }); - - // export any cf outputs - new cdk.CfnOutput(this, "SiteBucket", { - value: this.siteBucket.bucketName, - }); - new cdk.CfnOutput(this, "CloudFrontDistributionId", { - value: cloudFrontDistribution.distributionId, - }); - new cdk.CfnOutput(this, "CloudFrontDistributionDomainName", { - value: cloudFrontDistribution.distributionDomainName, - }); - new cdk.CfnOutput(this, "CloudFrontDistributionUrl", { - value: `https://${cloudFrontDistribution.distributionDomainName}`, - }); - - // assign public properties - this.originAccessIdentity = originAccessIdentity; - this.cloudFrontDistribution = cloudFrontDistribution; - } -} diff --git a/typescript/bedrock-kb-agent/lib/constructs/cognito-web-native-construct.ts b/typescript/bedrock-kb-agent/lib/constructs/cognito-web-native-construct.ts deleted file mode 100644 index 7e7c2ee9b3..0000000000 --- a/typescript/bedrock-kb-agent/lib/constructs/cognito-web-native-construct.ts +++ /dev/null @@ -1,208 +0,0 @@ -/** - * Copyright 2023 Amazon.com, Inc. and its affiliates. All Rights Reserved. - * - * Licensed under the Amazon Software License (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/asl/ - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -import * as cdk from "aws-cdk-lib"; -import { Construct } from "constructs"; - -/* eslint-disable @typescript-eslint/no-empty-interface */ -export interface CognitoWebNativeConstructProps extends cdk.StackProps {} - -const defaultProps: Partial = {}; - -/** - * Deploys Cognito with an Authenticated & UnAuthenticated Role with a Web and Native client - */ -export class CognitoWebNativeConstruct extends Construct { - public userPool: cdk.aws_cognito.UserPool; - public webClientUserPool: cdk.aws_cognito.UserPoolClient; - public nativeClientUserPool: cdk.aws_cognito.UserPoolClient; - public userPoolId: string; - public identityPoolId: string; - public webClientId: string; - public nativeClientId: string; - public authenticatedRole: cdk.aws_iam.Role; - public unauthenticatedRole: cdk.aws_iam.Role; - - constructor( - parent: Construct, - name: string, - props: CognitoWebNativeConstructProps, - ) { - super(parent, name); - - /* eslint-disable @typescript-eslint/no-unused-vars */ - props = { ...defaultProps, ...props }; - - const stack = cdk.Stack.of(this); - const stackName = stack.stackName; - - const userPool = new cdk.aws_cognito.UserPool(this, "UserPool", { - userPoolName: `UserPool${stackName}`, - selfSignUpEnabled: false, // Prototype front-ends that are public to the internet should keep this value as false - autoVerify: { email: true }, - userVerification: { - emailSubject: "Verify your email the app!", - emailBody: - "Hello {username}, Thanks for signing up to the app! Your verification code is {####}", - emailStyle: cdk.aws_cognito.VerificationEmailStyle.CODE, - smsMessage: - "Hello {username}, Thanks for signing up to app! Your verification code is {####}", - }, - passwordPolicy: { - minLength: 8, - requireDigits: true, - requireUppercase: true, - requireSymbols: true, - requireLowercase: true, - }, - removalPolicy: cdk.RemovalPolicy.DESTROY, - }); - - const userPoolWebClient = new cdk.aws_cognito.UserPoolClient( - this, - "UserPoolWebClient", - { - generateSecret: false, - userPool: userPool, - userPoolClientName: "WebClient", - authFlows: { - userPassword: true, - userSrp: true, - custom: true, - }, - }, - ); - - const userPoolNativeClient = new cdk.aws_cognito.UserPoolClient( - this, - "UserPoolNativeClient", - { - generateSecret: true, - userPool: userPool, - userPoolClientName: "NativeClient", - }, - ); - - const identityPool = new cdk.aws_cognito.CfnIdentityPool( - this, - "IdentityPool", - { - allowUnauthenticatedIdentities: false, - cognitoIdentityProviders: [ - { - clientId: userPoolWebClient.userPoolClientId, - providerName: userPool.userPoolProviderName, - }, - { - clientId: userPoolNativeClient.userPoolClientId, - providerName: userPool.userPoolProviderName, - }, - ], - }, - ); - - const unauthenticatedRole = new cdk.aws_iam.Role( - this, - "DefaultUnauthenticatedRole", - { - assumedBy: new cdk.aws_iam.FederatedPrincipal( - "cognito-identity.amazonaws.com", - { - StringEquals: { - "cognito-identity.amazonaws.com:aud": identityPool.ref, - }, - "ForAnyValue:StringLike": { - "cognito-identity.amazonaws.com:amr": "unauthenticated", - }, - }, - "sts:AssumeRoleWithWebIdentity", - ), - }, - ); - - const authenticatedRole = new cdk.aws_iam.Role( - this, - "DefaultAuthenticatedRole", - { - assumedBy: new cdk.aws_iam.FederatedPrincipal( - "cognito-identity.amazonaws.com", - { - StringEquals: { - "cognito-identity.amazonaws.com:aud": identityPool.ref, - }, - "ForAnyValue:StringLike": { - "cognito-identity.amazonaws.com:amr": "authenticated", - }, - }, - "sts:AssumeRoleWithWebIdentity", - ), - }, - ); - - new cdk.aws_cognito.CfnIdentityPoolRoleAttachment( - this, - "IdentityPoolRoleAttachment", - { - identityPoolId: identityPool.ref, - roles: { - unauthenticated: unauthenticatedRole.roleArn, - authenticated: authenticatedRole.roleArn, - }, - }, - ); - - // Assign Cfn Outputs - new cdk.CfnOutput(this, "UserPoolId", { - value: userPool.userPoolId, - }); - new cdk.CfnOutput(this, "IdentityPoolId", { - value: identityPool.ref, - }); - new cdk.CfnOutput(this, "WebClientId", { - value: userPoolWebClient.userPoolClientId, - }); - new cdk.CfnOutput(this, "NativeClientId", { - value: userPoolNativeClient.userPoolClientId, - }); - - // Add SSM Parameters - new cdk.aws_ssm.StringParameter(this, "COGNITO_USER_POOL_ID", { - stringValue: userPool.userPoolId, - }); - - new cdk.aws_ssm.StringParameter(this, "COGNITO_IDENTITY_POOL_ID", { - stringValue: identityPool.ref, - }); - - new cdk.aws_ssm.StringParameter(this, "COGNITO_WEB_CLIENT_ID", { - stringValue: userPoolWebClient.userPoolClientId, - }); - - new cdk.aws_ssm.StringParameter(this, "COGNITO_NATIVE_CLIENT_ID", { - stringValue: userPoolNativeClient.userPoolClientId, - }); - - // assign public properties - this.userPool = userPool; - this.webClientUserPool = userPoolWebClient; - this.nativeClientUserPool = userPoolNativeClient; - this.authenticatedRole = authenticatedRole; - this.unauthenticatedRole = unauthenticatedRole; - this.userPoolId = userPool.userPoolId; - this.identityPoolId = identityPool.ref; - this.webClientId = userPoolWebClient.userPoolClientId; - this.nativeClientId = userPoolNativeClient.userPoolClientId; - } -} diff --git a/typescript/bedrock-kb-agent/lib/constructs/s3-construct.ts b/typescript/bedrock-kb-agent/lib/constructs/s3-construct.ts deleted file mode 100755 index d67358d395..0000000000 --- a/typescript/bedrock-kb-agent/lib/constructs/s3-construct.ts +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright (c) 2024 Amazon.com, Inc. and its affiliates. - * All Rights Reserved. - * - * Licensed under the Amazon Software License (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/asl/ - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ -import * as cdk from "aws-cdk-lib"; -import { Construct } from "constructs"; - -export interface S3ConstructProps extends cdk.StackProps { - /** - * Is versioning enabled on this bucket ? - * - * @default true - */ - readonly versioned?: boolean; - - /** - * What is the role of this bucket, general use or for VPC, S3 logging etc. - * Selecting 'logging' will add LOG_DELIVERY_WRITE access control and will also - * enable tiered storage fro 30/90 archive/deep archive of logs. - * - * @default general - */ - readonly config?: "general" | "logging"; -} - -const defaultProps: Partial = { - versioned: true, - config: "general", -}; - -/** - * Deploys the s3 bucket - */ -export class S3Construct extends Construct { - public dataBucket: cdk.aws_s3.Bucket; - - constructor(parent: Construct, name: string, props: S3ConstructProps) { - super(parent, name); - - props = { ...defaultProps, ...props }; - - this.dataBucket = new cdk.aws_s3.Bucket(this, "Bucket", { - encryption: cdk.aws_s3.BucketEncryption.S3_MANAGED, - blockPublicAccess: cdk.aws_s3.BlockPublicAccess.BLOCK_ALL, - enforceSSL: true, - versioned: props.versioned, - autoDeleteObjects: true, // NOT recommended for production code - removalPolicy: cdk.RemovalPolicy.DESTROY, // NOT recommended for production code - accessControl: - props.config === "logging" - ? cdk.aws_s3.BucketAccessControl.LOG_DELIVERY_WRITE - : undefined, - intelligentTieringConfigurations: - props.config === "logging" - ? [ - { - name: "archive", - archiveAccessTierTime: cdk.Duration.days(90), //Days specified in ARCHIVE_ACCESS tier should not be less than 90 - deepArchiveAccessTierTime: cdk.Duration.days(180), //Days specified in DEEP_ARCHIVE_ACCESS should not be less than 180, and be greater than days specified in ARCHIVE_ACCESS - }, - ] - : undefined, - }); - - new cdk.CfnOutput(this, `BucketName`, { - value: `${this.dataBucket.bucketName}`, - }); - - new cdk.CfnOutput(this, `BucketArn`, { - value: `${this.dataBucket.bucketArn}`, - }); - } -} diff --git a/typescript/bedrock-kb-agent/media/kb-s3.png b/typescript/bedrock-kb-agent/media/kb-s3.png deleted file mode 100644 index 9ebe819972..0000000000 Binary files a/typescript/bedrock-kb-agent/media/kb-s3.png and /dev/null differ diff --git a/typescript/bedrock-kb-agent/package.json b/typescript/bedrock-kb-agent/package.json deleted file mode 100644 index bddbf1c961..0000000000 --- a/typescript/bedrock-kb-agent/package.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "bedrock-kb-agent", - "version": "0.1.0", - "bin": { - "bedrock-kb-agent": "bin/bedrock-kb-agent.js" - }, - "scripts": { - "build": "tsc", - "watch": "tsc -w", - "test": "jest", - "cdk": "cdk" - }, - "devDependencies": { - "@types/jest": "^29.5.14", - "@types/node": "22.7.9", - "jest": "^29.7.0", - "ts-jest": "^29.2.5", - "aws-cdk": "2.173.2", - "ts-node": "^10.9.2", - "typescript": "~5.6.3" - }, - "dependencies": { - "aws-cdk-lib": "2.173.2", - "constructs": "^10.0.0" - }, - "devDependencies": { - "@types/jest": "^29.5.11", - "@types/node": "20.10.4", - "@typescript-eslint/eslint-plugin": "^7.0.0", - "@typescript-eslint/parser": "^7.0.0", - "aws-cdk": "^2.173.2", - "aws-cdk-lib": "^2.173.2", - "aws-sdk": "^2.1518.0", - "cdk-nag": "^2.27.216", - "constructs": "^10.3.0", - "eslint": "^8.9.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.1.3", - "git-branch": "^1.0.0", - "git-repo-name": "^1.0.1", - "jest": "^29.7.0", - "source-map-support": "^0.5.16", - "ts-node": "^10.9.2", - "typescript": "~4.9.4", - "@aws-cdk/aws-lambda-python-alpha": "^2.114.1-alpha.0", - "@cdklabs/generative-ai-cdk-constructs": "^0.1.278", - "jest": "^29.0.0", - "@types/jest": "^29.0.0", - "ts-jest": "^29.0.0" - }, - "jest": { - "testEnvironment": "node", - "roots": ["/test"], - "testMatch": ["**/*.test.ts"], - "transform": { - "^.+\\.tsx?$": "ts-jest" - } - } -} diff --git a/typescript/bedrock-kb-agent/test/bedrock-kb-agent-stack.test.ts b/typescript/bedrock-kb-agent/test/bedrock-kb-agent-stack.test.ts deleted file mode 100644 index fc155df33a..0000000000 --- a/typescript/bedrock-kb-agent/test/bedrock-kb-agent-stack.test.ts +++ /dev/null @@ -1,104 +0,0 @@ -import * as cdk from "aws-cdk-lib"; -import { Template } from "aws-cdk-lib/assertions"; -import { BedrockKbAgentStack } from "../lib/bedrock-kb-agent-stack"; - -describe("BedrockKbAgentStack", () => { - let app: cdk.App; - let stack: BedrockKbAgentStack; - let template: Template; - - beforeEach(() => { - app = new cdk.App(); - stack = new BedrockKbAgentStack(app, "TestStack", { - env: { account: "123456789012", region: "us-east-1" }, - }); - template = Template.fromStack(stack); - }); - - test("Cognito User Pool is created with correct name", () => { - template.hasResourceProperties("AWS::Cognito::UserPool", { - UserPoolName: "UserPoolTestStack", - }); - }); - - test("API Gateway HTTP API is created", () => { - template.hasResourceProperties("AWS::ApiGatewayV2::Api", { - ProtocolType: "HTTP", - }); - }); - - test("Lambda function is created with correct configuration", () => { - template.hasResourceProperties("AWS::Lambda::Function", { - Handler: "example.lambda_handler", - Runtime: "python3.10", - Timeout: 60, - }); - }); - test("At least one IAM policy includes required Bedrock permissions", () => { - const requiredActions = [ - "bedrock:RetrieveAndGenerate", - "bedrock:Retrieve", - "bedrock:InvokeModel", - "bedrock:InvokeAgent", - ]; - - const allPolicies = template.findResources("AWS::IAM::Policy"); - - const hasBedrockPermissions = Object.values(allPolicies).some((policy) => { - const statements = policy.Properties.PolicyDocument.Statement; - return statements.some( - (statement: any) => - statement.Effect === "Allow" && - Array.isArray(statement.Action) && - requiredActions.every((action) => statement.Action.includes(action)), - ); - }); - - expect(hasBedrockPermissions).toBe(true); - }); - - test("S3 bucket for knowledge base is created", () => { - template.hasResourceProperties("AWS::S3::Bucket", { - BucketEncryption: { - ServerSideEncryptionConfiguration: [ - { - ServerSideEncryptionByDefault: { - SSEAlgorithm: "AES256", - }, - }, - ], - }, - }); - }); - - test("Bedrock Knowledge Base is created", () => { - template.hasResourceProperties("AWS::Bedrock::KnowledgeBase", { - KnowledgeBaseConfiguration: { - Type: "VECTOR", - }, - }); - }); - - /// - - test("Bedrock Agent is created", () => { - template.hasResourceProperties("AWS::Bedrock::Agent", { - AgentName: "Agent-TestStack", - }); - }); - - test("API Gateway routes are created", () => { - template.hasResourceProperties("AWS::ApiGatewayV2::Route", { - RouteKey: "POST /api/v1/example", - }); - - template.hasResourceProperties("AWS::ApiGatewayV2::Route", { - RouteKey: "POST /api/v1/weather", - }); - }); - - test("Stack has the expected number of resources", () => { - const resources = template.toJSON().Resources; - expect(Object.keys(resources).length).toBeGreaterThan(0); - }); -}); diff --git a/typescript/bedrock-kb-agent/tsconfig.json b/typescript/bedrock-kb-agent/tsconfig.json deleted file mode 100644 index aaa7dc510f..0000000000 --- a/typescript/bedrock-kb-agent/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "lib": [ - "es2020", - "dom" - ], - "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, - "typeRoots": [ - "./node_modules/@types" - ] - }, - "exclude": [ - "node_modules", - "cdk.out" - ] -} diff --git a/typescript/bedrock-kb-agent/web-app/.gitkeep b/typescript/bedrock-kb-agent/web-app/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/typescript/bedrock-kb-agent/web-app/dist/.gitkeep b/typescript/bedrock-kb-agent/web-app/dist/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/typescript/cdkpipeline-ecs/package.json b/typescript/cdkpipeline-ecs/package.json index 76adf362fc..5ac127f793 100644 --- a/typescript/cdkpipeline-ecs/package.json +++ b/typescript/cdkpipeline-ecs/package.json @@ -10,16 +10,16 @@ "cdk": "cdk" }, "devDependencies": { - "@types/jest": "^29.5.12", - "@types/node": "20.12.7", + "@types/jest": "^29.5.14", + "@types/node": "22.7.9", "jest": "^29.7.0", - "ts-jest": "^29.1.2", - "aws-cdk": "2.148.1", + "ts-jest": "^29.2.5", + "aws-cdk": "2.1004.0", "ts-node": "^10.9.2", - "typescript": "~5.4.5" + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "2.143.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0", "source-map-support": "^0.5.21" } diff --git a/typescript/classic-load-balancer/package.json b/typescript/classic-load-balancer/package.json index 6ad58ec599..a31a32f065 100644 --- a/typescript/classic-load-balancer/package.json +++ b/typescript/classic-load-balancer/package.json @@ -15,12 +15,12 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/node": "^10.17.0", - "aws-cdk": "*", - "typescript": "~5.1.6" + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "^2.0.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0" } } diff --git a/typescript/cloudfront-functions/package.json b/typescript/cloudfront-functions/package.json index 180eee5ab6..306f0023d3 100644 --- a/typescript/cloudfront-functions/package.json +++ b/typescript/cloudfront-functions/package.json @@ -11,17 +11,17 @@ "cdk": "cdk" }, "devDependencies": { - "@types/jest": "^29.5.11", - "@types/node": "20.11.6", + "@types/jest": "^29.5.14", + "@types/node": "22.7.9", "jest": "^29.7.0", - "ts-jest": "^29.1.2", - "aws-cdk": "2.123.0", + "ts-jest": "^29.2.5", + "aws-cdk": "2.1004.0", "ts-node": "^10.9.2", - "typescript": "~5.3.3" + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "2.123.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0", "source-map-support": "^0.5.21" } -} \ No newline at end of file +} diff --git a/typescript/cloudwatch/evidently-client-side-evaluation-ecs/local-image/package.json b/typescript/cloudwatch/evidently-client-side-evaluation-ecs/local-image/package.json index 5da92025e1..1373037e17 100644 --- a/typescript/cloudwatch/evidently-client-side-evaluation-ecs/local-image/package.json +++ b/typescript/cloudwatch/evidently-client-side-evaluation-ecs/local-image/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@types/express": "^4.17.15", - "@types/node": "^10.17.0", - "typescript": "~5.1.6" + "@types/node": "22.7.9", + "typescript": "~5.6.3" } } diff --git a/typescript/cloudwatch/evidently-client-side-evaluation-ecs/package.json b/typescript/cloudwatch/evidently-client-side-evaluation-ecs/package.json index 8a55b935e1..a7e98f5585 100644 --- a/typescript/cloudwatch/evidently-client-side-evaluation-ecs/package.json +++ b/typescript/cloudwatch/evidently-client-side-evaluation-ecs/package.json @@ -12,13 +12,13 @@ "license": "Apache-2.0", "devDependencies": { "@types/express": "^4.17.15", - "@types/node": "^8.10.38", - "aws-cdk": "*", - "typescript": "~5.1.6" + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", + "typescript": "~5.6.3" }, "dependencies": { "@aws-sdk/client-evidently": "^3.245.0", - "aws-cdk-lib": "^2.0.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0", "express": "^4.17.1" } diff --git a/typescript/cloudwatch/evidently-client-side-evaluation-lambda/package.json b/typescript/cloudwatch/evidently-client-side-evaluation-lambda/package.json index b7b6349e75..b4eba4a708 100644 --- a/typescript/cloudwatch/evidently-client-side-evaluation-lambda/package.json +++ b/typescript/cloudwatch/evidently-client-side-evaluation-lambda/package.json @@ -15,12 +15,12 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/node": "^20.5.7", - "aws-cdk": "^2.93.0", - "typescript": "~5.2.2" + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "^2.93.0", - "constructs": "^10.2.70" + "aws-cdk-lib": "2.185.0", + "constructs": "^10.0.0" } } diff --git a/typescript/codepipeline-build-deploy/package.json b/typescript/codepipeline-build-deploy/package.json index 7553b34fc2..9793f5c335 100644 --- a/typescript/codepipeline-build-deploy/package.json +++ b/typescript/codepipeline-build-deploy/package.json @@ -11,16 +11,16 @@ "cdk": "cdk" }, "devDependencies": { - "@types/jest": "^29.4.0", - "@types/node": "18.14.6", - "aws-cdk": "2.72.1", - "jest": "^29.5.0", - "ts-jest": "^29.0.5", - "ts-node": "^10.9.1", - "typescript": "~5.1.6" + "@types/jest": "^29.5.14", + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", + "jest": "^29.7.0", + "ts-jest": "^29.2.5", + "ts-node": "^10.9.2", + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "2.177.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0", "jest-junit": "^16.0.0", "source-map-support": "^0.5.21" diff --git a/typescript/codepipeline-glue-deploy/package.json b/typescript/codepipeline-glue-deploy/package.json index 37c6b6e60e..25b1182516 100644 --- a/typescript/codepipeline-glue-deploy/package.json +++ b/typescript/codepipeline-glue-deploy/package.json @@ -10,13 +10,13 @@ "cdk": "cdk" }, "devDependencies": { - "@types/node": "20.4.5", - "aws-cdk": "^2.130.0", - "ts-node": "^10.9.1", - "typescript": "~5.1.6" + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", + "ts-node": "^10.9.2", + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "^2.130.0", + "aws-cdk-lib": "2.185.0", "cdk": "^2.130.0", "constructs": "^10.0.0", "source-map-support": "^0.5.21" diff --git a/typescript/codewhisperer-cloudwatch/package.json b/typescript/codewhisperer-cloudwatch/package.json index 10babf680c..ad1604a320 100644 --- a/typescript/codewhisperer-cloudwatch/package.json +++ b/typescript/codewhisperer-cloudwatch/package.json @@ -11,16 +11,16 @@ "cdk": "cdk" }, "devDependencies": { - "@types/jest": "^29.5.5", - "@types/node": "20.7.1", + "@types/jest": "^29.5.14", + "@types/node": "22.7.9", "jest": "^29.7.0", - "ts-jest": "^29.1.1", - "aws-cdk": "2.102.0", - "ts-node": "^10.9.1", - "typescript": "~5.2.2" + "ts-jest": "^29.2.5", + "aws-cdk": "2.1004.0", + "ts-node": "^10.9.2", + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "2.102.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0", "source-map-support": "^0.5.21" } diff --git a/typescript/cognito-api-lambda/package.json b/typescript/cognito-api-lambda/package.json index 1396309b38..07628b8692 100644 --- a/typescript/cognito-api-lambda/package.json +++ b/typescript/cognito-api-lambda/package.json @@ -13,12 +13,12 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/node": "^10.17.0", - "aws-cdk": "*", - "typescript": "~5.1.6" + "@types/node": "22.7.9", + "aws-cdk": "2.1004.0", + "typescript": "~5.6.3" }, "dependencies": { - "aws-cdk-lib": "^2.0.0", + "aws-cdk-lib": "2.185.0", "constructs": "^10.0.0" } } diff --git a/typescript/ec2-ssm-local-zone/jest.config.js b/typescript/ec2-ssm-local-zone/jest.config.js new file mode 100644 index 0000000000..c4f95b0aea --- /dev/null +++ b/typescript/ec2-ssm-local-zone/jest.config.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const config = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; +exports.default = config; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiamVzdC5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJqZXN0LmNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLE1BQU0sTUFBTSxHQUFXO0lBQ3JCLGVBQWUsRUFBRSxNQUFNO0lBQ3ZCLEtBQUssRUFBRSxDQUFDLGdCQUFnQixDQUFDO0lBQ3pCLFNBQVMsRUFBRSxDQUFDLGNBQWMsQ0FBQztJQUMzQixTQUFTLEVBQUU7UUFDVCxhQUFhLEVBQUUsU0FBUztLQUN6QjtDQUNGLENBQUM7QUFDRixrQkFBZSxNQUFNLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IENvbmZpZyB9IGZyb20gJ2plc3QnO1xuXG5jb25zdCBjb25maWc6IENvbmZpZyA9IHtcbiAgdGVzdEVudmlyb25tZW50OiAnbm9kZScsXG4gIHJvb3RzOiBbJzxyb290RGlyPi90ZXN0J10sXG4gIHRlc3RNYXRjaDogWycqKi8qLnRlc3QudHMnXSxcbiAgdHJhbnNmb3JtOiB7XG4gICAgJ14uK1xcXFwudHN4PyQnOiAndHMtamVzdCdcbiAgfVxufTtcbmV4cG9ydCBkZWZhdWx0IGNvbmZpZztcbiJdfQ== \ No newline at end of file diff --git a/typescript/ec2-ssm-local-zone/test/__snapshots__/example-localzone-stack.test.ts.snap b/typescript/ec2-ssm-local-zone/test/__snapshots__/example-localzone-stack.test.ts.snap index 10462f64b4..3d32063fad 100644 --- a/typescript/ec2-ssm-local-zone/test/__snapshots__/example-localzone-stack.test.ts.snap +++ b/typescript/ec2-ssm-local-zone/test/__snapshots__/example-localzone-stack.test.ts.snap @@ -376,6 +376,12 @@ sudo iptables-save", "Ref": "VpcprivateSubnet1SubnetCEAD3716", }, ], + "Tags": [ + { + "Key": "Name", + "Value": "ExampleLocalZoneStack/Vpc", + }, + ], "VpcEndpointType": "Interface", "VpcId": { "Ref": "Vpc8378EB38", @@ -703,6 +709,12 @@ sudo iptables-save", "Ref": "VpcprivateSubnet1SubnetCEAD3716", }, ], + "Tags": [ + { + "Key": "Name", + "Value": "ExampleLocalZoneStack/Vpc", + }, + ], "VpcEndpointType": "Interface", "VpcId": { "Ref": "Vpc8378EB38", @@ -788,6 +800,12 @@ sudo iptables-save", "Ref": "VpcprivateSubnet1SubnetCEAD3716", }, ], + "Tags": [ + { + "Key": "Name", + "Value": "ExampleLocalZoneStack/Vpc", + }, + ], "VpcEndpointType": "Interface", "VpcId": { "Ref": "Vpc8378EB38", diff --git a/typescript/ec2-ssm-local-zone/test/example-localzone-stack.test.ts b/typescript/ec2-ssm-local-zone/test/example-localzone-stack.test.ts index 34b897a6f1..b1588dd90c 100644 --- a/typescript/ec2-ssm-local-zone/test/example-localzone-stack.test.ts +++ b/typescript/ec2-ssm-local-zone/test/example-localzone-stack.test.ts @@ -1,11 +1,14 @@ import * as cdk from 'aws-cdk-lib'; import { Template } from 'aws-cdk-lib/assertions'; import { ExampleLocalZoneStack } from '../example-localzone-stack'; +import { normalizeTemplate } from '../../test-utils/normalize-template'; test('ExampleLocalZoneStack has required resources', () => { const app = new cdk.App(); // WHEN const stack = new ExampleLocalZoneStack(app, 'ExampleLocalZoneStack', { }); - expect(Template.fromStack(stack)).toMatchSnapshot(); + const template = Template.fromStack(stack); + const normalizedTemplate = normalizeTemplate(template.toJSON()); + expect(normalizedTemplate).toMatchSnapshot(); }); diff --git a/typescript/imagebuilder/package.json b/typescript/imagebuilder/package.json index eea263c864..2415d112d8 100644 --- a/typescript/imagebuilder/package.json +++ b/typescript/imagebuilder/package.json @@ -17,7 +17,7 @@ "typescript": "5.3.3" }, "dependencies": { - "aws-cdk-lib": "2.184.0", + "aws-cdk-lib": "2.122.0", "cdk-nag": "2.28.27", "constructs": "10.3.0", "source-map-support": "0.5.21" diff --git a/typescript/inspector2/jest.config.js b/typescript/inspector2/jest.config.js new file mode 100644 index 0000000000..c4f95b0aea --- /dev/null +++ b/typescript/inspector2/jest.config.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const config = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; +exports.default = config; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiamVzdC5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJqZXN0LmNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLE1BQU0sTUFBTSxHQUFXO0lBQ3JCLGVBQWUsRUFBRSxNQUFNO0lBQ3ZCLEtBQUssRUFBRSxDQUFDLGdCQUFnQixDQUFDO0lBQ3pCLFNBQVMsRUFBRSxDQUFDLGNBQWMsQ0FBQztJQUMzQixTQUFTLEVBQUU7UUFDVCxhQUFhLEVBQUUsU0FBUztLQUN6QjtDQUNGLENBQUM7QUFDRixrQkFBZSxNQUFNLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IENvbmZpZyB9IGZyb20gJ2plc3QnO1xuXG5jb25zdCBjb25maWc6IENvbmZpZyA9IHtcbiAgdGVzdEVudmlyb25tZW50OiAnbm9kZScsXG4gIHJvb3RzOiBbJzxyb290RGlyPi90ZXN0J10sXG4gIHRlc3RNYXRjaDogWycqKi8qLnRlc3QudHMnXSxcbiAgdHJhbnNmb3JtOiB7XG4gICAgJ14uK1xcXFwudHN4PyQnOiAndHMtamVzdCdcbiAgfVxufTtcbmV4cG9ydCBkZWZhdWx0IGNvbmZpZztcbiJdfQ== \ No newline at end of file diff --git a/typescript/inspector2/test/__snapshots__/inspector2-enable-delegated-admin-account-resource.test.ts.snap b/typescript/inspector2/test/__snapshots__/inspector2-enable-delegated-admin-account-resource.test.ts.snap index 771eefa2e5..83cd46def2 100644 --- a/typescript/inspector2/test/__snapshots__/inspector2-enable-delegated-admin-account-resource.test.ts.snap +++ b/typescript/inspector2/test/__snapshots__/inspector2-enable-delegated-admin-account-resource.test.ts.snap @@ -122,7 +122,7 @@ exports[`Inspector2EnableDelegatedAdminAccountResource creates required resource "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "bb459fac5f6b4b052aac9803443226d161a5cfe96f4648b21f9e4912c698bf30.zip", + "S3Key": "NORMALIZED_ASSET_HASH.zip", }, "Handler": "index.handler", "Role": { diff --git a/typescript/inspector2/test/__snapshots__/inspector2-enable-resources.test.ts.snap b/typescript/inspector2/test/__snapshots__/inspector2-enable-resources.test.ts.snap index 841830cd08..eecfddd703 100644 --- a/typescript/inspector2/test/__snapshots__/inspector2-enable-resources.test.ts.snap +++ b/typescript/inspector2/test/__snapshots__/inspector2-enable-resources.test.ts.snap @@ -122,7 +122,7 @@ exports[`Inspector2EnableResource creates required resources 1`] = ` "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "bb459fac5f6b4b052aac9803443226d161a5cfe96f4648b21f9e4912c698bf30.zip", + "S3Key": "NORMALIZED_ASSET_HASH.zip", }, "Handler": "index.handler", "Role": { @@ -236,7 +236,7 @@ exports[`Inspector2EnableResource creates required resources 1`] = ` "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "5fa1330271b8967d9254ba2d4a07144f8acefe8b77e6d6bba38261373a50d5f8.zip", + "S3Key": "NORMALIZED_ASSET_HASH.zip", }, "Handler": "index.handler", "Role": { diff --git a/typescript/inspector2/test/__snapshots__/inspector2-monitoring-resource.test.ts.snap b/typescript/inspector2/test/__snapshots__/inspector2-monitoring-resource.test.ts.snap index 0c8df3d09d..c5b7b1874b 100644 --- a/typescript/inspector2/test/__snapshots__/inspector2-monitoring-resource.test.ts.snap +++ b/typescript/inspector2/test/__snapshots__/inspector2-monitoring-resource.test.ts.snap @@ -122,7 +122,7 @@ exports[`Inspector2MonitoringResource creates required resources 1`] = ` "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "387c7c1f429cca9662171575d60cec88ab73d6da0e9d197558cbc4bbd665bb6e.zip", + "S3Key": "NORMALIZED_ASSET_HASH.zip", }, "Environment": { "Variables": { @@ -204,7 +204,7 @@ exports[`Inspector2MonitoringResource creates required resources 1`] = ` "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "08c8f1505ccfb98c41cb39e43e5ec867fcb42aee41bba9ea3bb3ca27a3025c0c.zip", + "S3Key": "NORMALIZED_ASSET_HASH.zip", }, "Environment": { "Variables": { @@ -385,7 +385,7 @@ exports[`Inspector2MonitoringResource creates required resources 1`] = ` "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "5fa1330271b8967d9254ba2d4a07144f8acefe8b77e6d6bba38261373a50d5f8.zip", + "S3Key": "NORMALIZED_ASSET_HASH.zip", }, "Handler": "index.handler", "Role": { diff --git a/typescript/inspector2/test/__snapshots__/inspector2-update-org-config-resource.test.ts.snap b/typescript/inspector2/test/__snapshots__/inspector2-update-org-config-resource.test.ts.snap index 837e5d18d6..ad94afa11f 100644 --- a/typescript/inspector2/test/__snapshots__/inspector2-update-org-config-resource.test.ts.snap +++ b/typescript/inspector2/test/__snapshots__/inspector2-update-org-config-resource.test.ts.snap @@ -122,7 +122,7 @@ exports[`Inspector2UpdateOrganizationConfigurationResource creates required reso "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "bb459fac5f6b4b052aac9803443226d161a5cfe96f4648b21f9e4912c698bf30.zip", + "S3Key": "NORMALIZED_ASSET_HASH.zip", }, "Handler": "index.handler", "Role": { diff --git a/typescript/inspector2/test/inspector2-enable-delegated-admin-account-resource.test.ts b/typescript/inspector2/test/inspector2-enable-delegated-admin-account-resource.test.ts index 686505aaf7..dd67f31891 100644 --- a/typescript/inspector2/test/inspector2-enable-delegated-admin-account-resource.test.ts +++ b/typescript/inspector2/test/inspector2-enable-delegated-admin-account-resource.test.ts @@ -1,6 +1,7 @@ import * as cdk from 'aws-cdk-lib'; import { Template } from 'aws-cdk-lib/assertions'; import { Inspector2EnableDelegatedAdminAccountResource } from '../lib/inspector2-enable-delegated-admin-account-resource'; +import { normalizeTemplate } from '../../test-utils/normalize-template'; test('Inspector2EnableDelegatedAdminAccountResource creates required resources', () => { const app = new cdk.App(); @@ -18,5 +19,7 @@ test('Inspector2EnableDelegatedAdminAccountResource creates required resources', }, }); - expect(Template.fromStack(stack)).toMatchSnapshot(); + const template = Template.fromStack(stack); + const normalizedTemplate = normalizeTemplate(template.toJSON()); + expect(normalizedTemplate).toMatchSnapshot(); }); diff --git a/typescript/inspector2/test/inspector2-enable-resources.test.ts b/typescript/inspector2/test/inspector2-enable-resources.test.ts index 24c6d29c4b..1cda44d775 100644 --- a/typescript/inspector2/test/inspector2-enable-resources.test.ts +++ b/typescript/inspector2/test/inspector2-enable-resources.test.ts @@ -1,6 +1,7 @@ import * as cdk from 'aws-cdk-lib'; import { Template } from 'aws-cdk-lib/assertions'; import { Inspector2EnableResource } from '../lib/inspector2-enable-resource'; +import { normalizeTemplate } from '../../test-utils/normalize-template'; test('Inspector2EnableResource creates required resources', () => { const app = new cdk.App(); @@ -11,5 +12,7 @@ test('Inspector2EnableResource creates required resources', () => { logRetention: cdk.aws_logs.RetentionDays.ONE_DAY, }); - expect(Template.fromStack(stack)).toMatchSnapshot(); + const template = Template.fromStack(stack); + const normalizedTemplate = normalizeTemplate(template.toJSON()); + expect(normalizedTemplate).toMatchSnapshot(); }); diff --git a/typescript/inspector2/test/inspector2-monitoring-resource.test.ts b/typescript/inspector2/test/inspector2-monitoring-resource.test.ts index 6b8fbd1657..6385c39446 100644 --- a/typescript/inspector2/test/inspector2-monitoring-resource.test.ts +++ b/typescript/inspector2/test/inspector2-monitoring-resource.test.ts @@ -1,6 +1,7 @@ import * as cdk from 'aws-cdk-lib'; import { Template } from 'aws-cdk-lib/assertions'; import { Inspector2MonitoringResource } from '../lib/inspector2-monitoring-resource'; +import { normalizeTemplate } from '../../test-utils/normalize-template'; test('Inspector2MonitoringResource creates required resources', () => { const app = new cdk.App(); @@ -8,5 +9,7 @@ test('Inspector2MonitoringResource creates required resources', () => { const stack = new cdk.Stack(app, 'Inspector2MonitoringStack', { }); new Inspector2MonitoringResource(stack, 'Inspector2Monitoring', { }); - expect(Template.fromStack(stack)).toMatchSnapshot(); + const template = Template.fromStack(stack); + const normalizedTemplate = normalizeTemplate(template.toJSON()); + expect(normalizedTemplate).toMatchSnapshot(); }); diff --git a/typescript/inspector2/test/inspector2-update-org-config-resource.test.ts b/typescript/inspector2/test/inspector2-update-org-config-resource.test.ts index 7662c58608..b6f6fd8828 100644 --- a/typescript/inspector2/test/inspector2-update-org-config-resource.test.ts +++ b/typescript/inspector2/test/inspector2-update-org-config-resource.test.ts @@ -1,6 +1,7 @@ import * as cdk from 'aws-cdk-lib'; import { Template } from 'aws-cdk-lib/assertions'; import { Inspector2UpdateOrganizationConfigurationResource } from '../lib/inspector2-update-org-config-resource'; +import { normalizeTemplate } from '../../test-utils/normalize-template'; test('Inspector2UpdateOrganizationConfigurationResource creates required resources', () => { const app = new cdk.App(); @@ -15,5 +16,7 @@ test('Inspector2UpdateOrganizationConfigurationResource creates required resourc }, }); - expect(Template.fromStack(stack)).toMatchSnapshot(); + const template = Template.fromStack(stack); + const normalizedTemplate = normalizeTemplate(template.toJSON()); + expect(normalizedTemplate).toMatchSnapshot(); }); diff --git a/typescript/opensearch/os_vpc_provision/package.json b/typescript/opensearch/os_vpc_provision/package.json index fbf97247e6..ade93c4746 100644 --- a/typescript/opensearch/os_vpc_provision/package.json +++ b/typescript/opensearch/os_vpc_provision/package.json @@ -21,7 +21,7 @@ }, "dependencies": { "aws-cdk": "^2.102.0", - "aws-cdk-lib": "2.177.0", + "aws-cdk-lib": "2.90.0", "constructs": "^10.0.0", "source-map-support": "^0.5.21" } diff --git a/typescript/s3-kms-cross-account-replication/jest.config.js b/typescript/s3-kms-cross-account-replication/jest.config.js new file mode 100644 index 0000000000..c4f95b0aea --- /dev/null +++ b/typescript/s3-kms-cross-account-replication/jest.config.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const config = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; +exports.default = config; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiamVzdC5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJqZXN0LmNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLE1BQU0sTUFBTSxHQUFXO0lBQ3JCLGVBQWUsRUFBRSxNQUFNO0lBQ3ZCLEtBQUssRUFBRSxDQUFDLGdCQUFnQixDQUFDO0lBQ3pCLFNBQVMsRUFBRSxDQUFDLGNBQWMsQ0FBQztJQUMzQixTQUFTLEVBQUU7UUFDVCxhQUFhLEVBQUUsU0FBUztLQUN6QjtDQUNGLENBQUM7QUFDRixrQkFBZSxNQUFNLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IENvbmZpZyB9IGZyb20gJ2plc3QnO1xuXG5jb25zdCBjb25maWc6IENvbmZpZyA9IHtcbiAgdGVzdEVudmlyb25tZW50OiAnbm9kZScsXG4gIHJvb3RzOiBbJzxyb290RGlyPi90ZXN0J10sXG4gIHRlc3RNYXRjaDogWycqKi8qLnRlc3QudHMnXSxcbiAgdHJhbnNmb3JtOiB7XG4gICAgJ14uK1xcXFwudHN4PyQnOiAndHMtamVzdCdcbiAgfVxufTtcbmV4cG9ydCBkZWZhdWx0IGNvbmZpZztcbiJdfQ== \ No newline at end of file diff --git a/typescript/test-utils/normalize-template.ts b/typescript/test-utils/normalize-template.ts new file mode 100644 index 0000000000..ee048ef978 --- /dev/null +++ b/typescript/test-utils/normalize-template.ts @@ -0,0 +1,40 @@ +/** + * Utility function to normalize CloudFormation templates for consistent snapshot testing + * across different environments (local vs CI/CD). + * + * This function normalizes: + * - Asset hashes in S3Key properties + * - Any other environment-specific values that might change between runs + */ +export function normalizeTemplate(template: any): any { + // Create a deep copy of the template to avoid modifying the original + const templateCopy = JSON.parse(JSON.stringify(template)); + + // Function to recursively traverse the template and normalize values + function normalizeValues(obj: any) { + if (!obj || typeof obj !== 'object') return; + + // Process object properties + for (const key in obj) { + // Normalize S3Key asset hashes + if (key === 'S3Key' && typeof obj[key] === 'string' && /^[a-f0-9]{64}\.zip$/.test(obj[key])) { + obj[key] = 'NORMALIZED_ASSET_HASH.zip'; + } + // Normalize S3Key asset hashes with different patterns + else if (key === 'S3Key' && typeof obj[key] === 'string' && /^[a-f0-9]{64}$/.test(obj[key])) { + obj[key] = 'NORMALIZED_ASSET_HASH'; + } + // Normalize Docker image digests + else if (key === 'ImageDigest' && typeof obj[key] === 'string' && obj[key].startsWith('sha256:')) { + obj[key] = 'NORMALIZED_IMAGE_DIGEST'; + } + // Recursively process nested objects and arrays + else if (typeof obj[key] === 'object') { + normalizeValues(obj[key]); + } + } + } + + normalizeValues(templateCopy); + return templateCopy; +}