View a markdown version of this page

搭配 Bash 指令碼使用 AWS CLI 的 Lambda 範例 - AWS SDK 程式碼範例

文件 AWS 開發套件範例 GitHub 儲存庫中有更多可用的 AWS SDK 範例

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

搭配 Bash 指令碼使用 AWS CLI 的 Lambda 範例

下列程式碼範例示範如何使用 AWS Command Line Interface 搭配 Bash 指令碼搭配 Lambda 來執行動作和實作常見案例。

案例是向您展示如何呼叫服務中的多個函數或與其他 AWS 服務組合來完成特定任務的程式碼範例。

每個範例均包含完整原始碼的連結,您可在連結中找到如何設定和執行內容中程式碼的相關指示。

主題

案例

以下程式碼範例顯示做法:

  • 為 Lambda 執行建立 IAM 角色

  • 建立和部署 Lambda 函數

  • 建立 REST API

  • 設定 Lambda 代理整合

  • 部署和測試 API

  • 清除資源

AWS CLI 使用 Bash 指令碼
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在範例開發人員教學課程儲存庫中設定和執行。

#!/bin/bash set -euo pipefail # Simple API Gateway Lambda Integration Script # This script creates a REST API with Lambda proxy integration # Generate random identifiers FUNCTION_NAME="GetStartedLambdaProxyIntegration-$(openssl rand -hex 4)" ROLE_NAME="GetStartedLambdaBasicExecutionRole-$(openssl rand -hex 4)" API_NAME="LambdaProxyAPI-$(openssl rand -hex 4)" # Get AWS account info ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) REGION=$(aws configure get region || echo "us-east-1") # Validate inputs if [[ -z "$ACCOUNT_ID" ]] || [[ -z "$REGION" ]]; then echo "Error: Failed to retrieve AWS account information" >&2 exit 1 fi echo "Creating Lambda function code..." # Create Lambda function code with input validation cat > lambda_function.py << 'EOF' import json import logging logger = logging.getLogger() logger.setLevel(logging.INFO) def lambda_handler(event, context): try: logger.info("Received event: %s", json.dumps(event)) greeter = 'World' # Safely retrieve greeter from query string parameters query_params = event.get('queryStringParameters') or {} if isinstance(query_params, dict) and 'greeter' in query_params: greeter_value = query_params.get('greeter') if isinstance(greeter_value, str) and greeter_value: greeter = greeter_value # Safely retrieve greeter from multi-value headers multi_headers = event.get('multiValueHeaders') or {} if isinstance(multi_headers, dict) and 'greeter' in multi_headers: greeter_list = multi_headers.get('greeter', []) if isinstance(greeter_list, list) and greeter_list: greeter = " and ".join(str(g) for g in greeter_list if g) # Safely retrieve greeter from headers headers = event.get('headers') or {} if isinstance(headers, dict) and 'greeter' in headers: greeter_value = headers.get('greeter') if isinstance(greeter_value, str) and greeter_value: greeter = greeter_value # Safely retrieve greeter from body body = event.get('body') if body and isinstance(body, str): try: body_dict = json.loads(body) if isinstance(body_dict, dict) and 'greeter' in body_dict: greeter_value = body_dict.get('greeter') if isinstance(greeter_value, str) and greeter_value: greeter = greeter_value except (json.JSONDecodeError, ValueError) as e: logger.warning("Failed to parse body: %s", str(e)) # Sanitize greeter to prevent injection greeter = greeter.replace('"', '\\"').replace("'", "\\'") response = { "statusCode": 200, "headers": { "Content-Type": "application/json" }, "body": json.dumps({"message": f"Hello, {greeter}!"}) } logger.info("Response: %s", json.dumps(response)) return response except Exception as e: logger.error("Unexpected error: %s", str(e), exc_info=True) return { "statusCode": 500, "headers": { "Content-Type": "application/json" }, "body": json.dumps({"error": "Internal server error"}) } EOF # Create deployment package zip -q function.zip lambda_function.py || { echo "Error: Failed to create function.zip" >&2 exit 1 } echo "Creating IAM role..." # Create IAM trust policy cat > trust-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF # Create IAM role with error handling aws iam create-role \ --role-name "$ROLE_NAME" \ --assume-role-policy-document file://trust-policy.json \ --description "Temporary role for Lambda execution" || { echo "Error: Failed to create IAM role" >&2 exit 1 } aws iam tag-role --role-name "$ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=apigateway-lambda-integration # Attach execution policy aws iam attach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" || { echo "Error: Failed to attach IAM policy" >&2 exit 1 } # Wait for role propagation sleep 15 echo "Creating Lambda function..." # Create Lambda function with Python 3.11 (more recent runtime) aws lambda create-function \ --function-name "$FUNCTION_NAME" \ --runtime python3.11 \ --role "arn:aws:iam::$ACCOUNT_ID:role/$ROLE_NAME" \ --handler lambda_function.lambda_handler \ --zip-file fileb://function.zip \ --timeout 30 \ --memory-size 128 \ --environment "Variables={LOG_LEVEL=INFO}" \ --tags project=doc-smith,tutorial=apigateway-lambda-integration || { echo "Error: Failed to create Lambda function" >&2 exit 1 } echo "Creating API Gateway..." # Create REST API with minimum logging API_RESPONSE=$(aws apigateway create-rest-api \ --name "$API_NAME" \ --endpoint-configuration types=REGIONAL \ --description "API for Lambda proxy integration tutorial" \ --tags project=doc-smith,tutorial=apigateway-lambda-integration \ --output json) API_ID=$(echo "$API_RESPONSE" | grep -o '"id": "[^"]*"' | head -1 | cut -d'"' -f4) if [[ -z "$API_ID" ]]; then echo "Error: Failed to create API Gateway" >&2 exit 1 fi # Get root resource ID ROOT_RESOURCE_ID=$(aws apigateway get-resources --rest-api-id "$API_ID" --query 'items[?path==`/`].id' --output text) # Create helloworld resource aws apigateway create-resource \ --rest-api-id "$API_ID" \ --parent-id "$ROOT_RESOURCE_ID" \ --path-part helloworld || { echo "Error: Failed to create resource" >&2 exit 1 } # Get resource ID RESOURCE_ID=$(aws apigateway get-resources --rest-api-id "$API_ID" --query "items[?pathPart=='helloworld'].id" --output text) # Create ANY method with no authorization (intentional for tutorial) aws apigateway put-method \ --rest-api-id "$API_ID" \ --resource-id "$RESOURCE_ID" \ --http-method ANY \ --authorization-type NONE || { echo "Error: Failed to create method" >&2 exit 1 } # Set up Lambda proxy integration LAMBDA_URI="arn:aws:apigateway:$REGION:lambda:path/2015-03-31/functions/arn:aws:lambda:$REGION:$ACCOUNT_ID:function:$FUNCTION_NAME/invocations" aws apigateway put-integration \ --rest-api-id "$API_ID" \ --resource-id "$RESOURCE_ID" \ --http-method ANY \ --type AWS_PROXY \ --integration-http-method POST \ --uri "$LAMBDA_URI" || { echo "Error: Failed to create integration" >&2 exit 1 } # Grant API Gateway permission to invoke Lambda STATEMENT_ID="apigateway-invoke-$(openssl rand -hex 4)" SOURCE_ARN="arn:aws:execute-api:$REGION:$ACCOUNT_ID:$API_ID/*/*" aws lambda add-permission \ --function-name "$FUNCTION_NAME" \ --statement-id "$STATEMENT_ID" \ --action lambda:InvokeFunction \ --principal apigateway.amazonaws.com \ --source-arn "$SOURCE_ARN" || { echo "Error: Failed to add Lambda permission" >&2 exit 1 } # Deploy API aws apigateway create-deployment \ --rest-api-id "$API_ID" \ --stage-name test \ --description "Test deployment" || { echo "Error: Failed to deploy API" >&2 exit 1 } echo "Testing API..." # Test the API INVOKE_URL="https://$API_ID.execute-api.$REGION.amazonaws.com/test/helloworld" echo "API URL: $INVOKE_URL" # Test with query parameter (with proper URL encoding) echo "Testing with query parameter:" curl -s -X GET "$INVOKE_URL?greeter=John" | jq . 2>/dev/null || curl -s -X GET "$INVOKE_URL?greeter=John" echo "" # Test with header echo "Testing with header:" curl -s -X GET "$INVOKE_URL" \ -H 'content-type: application/json' \ -H 'greeter: John' | jq . 2>/dev/null || curl -s -X GET "$INVOKE_URL" \ -H 'content-type: application/json' \ -H 'greeter: John' echo "" # Test with body echo "Testing with POST body:" curl -s -X POST "$INVOKE_URL" \ -H 'content-type: application/json' \ -d '{"greeter": "John"}' | jq . 2>/dev/null || curl -s -X POST "$INVOKE_URL" \ -H 'content-type: application/json' \ -d '{"greeter": "John"}' echo "" echo "Tutorial completed! API is available at: $INVOKE_URL" # Cleanup echo "Cleaning up resources..." # Delete API aws apigateway delete-rest-api --rest-api-id "$API_ID" || echo "Warning: Failed to delete API" >&2 # Delete Lambda function aws lambda delete-function --function-name "$FUNCTION_NAME" || echo "Warning: Failed to delete Lambda function" >&2 # Detach policy and delete role aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" || echo "Warning: Failed to detach policy" >&2 aws iam delete-role --role-name "$ROLE_NAME" || echo "Warning: Failed to delete role" >&2 # Clean up local files securely rm -f lambda_function.py function.zip trust-policy.json echo "Cleanup completed!"

以下程式碼範例顯示做法:

  • 建立 CloudWatch 儀表板

  • 使用函數名稱變數新增 Lambda 指標小工具

  • 驗證儀表板

  • 清除資源

AWS CLI 使用 Bash 指令碼
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在範例開發人員教學課程儲存庫中設定和執行。

#!/bin/bash # Script to create a CloudWatch dashboard with Lambda function name as a variable # This script creates a CloudWatch dashboard that allows you to switch between different Lambda functions # Set up logging with secure permissions LOG_FILE="${HOME}/.cloudwatch-dashboard-script.log" touch "$LOG_FILE" && chmod 600 "$LOG_FILE" exec > >(tee -a "$LOG_FILE") 2>&1 echo "$(date): Starting CloudWatch dashboard creation script" # Security: Set strict error handling set -uo pipefail trap 'handle_error "Script failed at line $LINENO"' ERR # Function to handle errors handle_error() { local error_msg="${1:-Unknown error}" echo "ERROR: $error_msg" >&2 echo "Resources created:" echo "- CloudWatch Dashboard: LambdaMetricsDashboard" echo "" echo "===========================================" echo "CLEANUP CONFIRMATION" echo "===========================================" echo "An error occurred. Proceeding with automatic cleanup..." echo "Cleaning up resources..." aws cloudwatch delete-dashboards --dashboard-names LambdaMetricsDashboard 2>/dev/null || true # Clean up temporary files if [ -n "${TEMP_DIR:-}" ] && [ -d "$TEMP_DIR" ]; then rm -rf "$TEMP_DIR" fi rm -f dashboard-body.json echo "Cleanup complete." exit 1 } # Security: Validate AWS CLI is installed if ! command -v aws &> /dev/null; then handle_error "AWS CLI is not installed. Please install it and try again." fi # Check if AWS CLI is installed and configured echo "Checking AWS CLI configuration..." if ! aws sts get-caller-identity > /dev/null 2>&1; then handle_error "AWS CLI is not properly configured. Please configure it with 'aws configure' and try again." fi # Get the current region securely REGION=$(aws configure get region 2>/dev/null || echo "") if [ -z "$REGION" ]; then REGION="us-east-1" echo "No region found in AWS config, defaulting to $REGION" fi echo "Using region: $REGION" # Validate region format if ! [[ "$REGION" =~ ^[a-z]{2}-[a-z]+-[0-9]{1}$ ]]; then handle_error "Invalid AWS region format: $REGION" fi # Check if there are any Lambda functions in the account echo "Checking for Lambda functions..." LAMBDA_FUNCTIONS=$(aws lambda list-functions --region "$REGION" --query "Functions[*].FunctionName" --output text 2>/dev/null || echo "") if [ -z "$LAMBDA_FUNCTIONS" ]; then echo "No Lambda functions found in your account. Creating a simple test function..." # Create a temporary directory for Lambda function code with secure permissions TEMP_DIR=$(mktemp -d) chmod 700 "$TEMP_DIR" trap 'rm -rf "$TEMP_DIR"' EXIT # Create a simple Lambda function cat > "$TEMP_DIR/index.js" << 'EOF' exports.handler = async (event) => { console.log('Event:', JSON.stringify(event, null, 2)); return { statusCode: 200, body: JSON.stringify('Hello from Lambda!'), }; }; EOF # Zip the function code if ! cd "$TEMP_DIR"; then handle_error "Failed to change to temporary directory" fi if ! zip -q function.zip index.js; then handle_error "Failed to create zip file" fi # Create a role for the Lambda function with restricted trust policy ROLE_NAME="LambdaDashboardTestRole-$(date +%s)" TRUST_POLICY='{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]}' if ! ROLE_ARN=$(aws iam create-role \ --role-name "$ROLE_NAME" \ --assume-role-policy-document "$TRUST_POLICY" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudwatch-dynamicdash \ --query "Role.Arn" \ --output text 2>/dev/null); then handle_error "Failed to create IAM role for Lambda function" fi echo "Waiting for role to be available..." sleep 10 # Attach basic Lambda execution policy if ! aws iam attach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"; then aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null || true handle_error "Failed to attach policy to IAM role" fi # Create the Lambda function FUNCTION_NAME="DashboardTestFunction-$(date +%s)" if ! aws lambda create-function \ --function-name "$FUNCTION_NAME" \ --runtime nodejs18.x \ --role "$ROLE_ARN" \ --handler index.handler \ --zip-file fileb://function.zip \ --tags project=doc-smith,tutorial=cloudwatch-dynamicdash \ --region "$REGION" > /dev/null 2>&1; then aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 2>/dev/null || true aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null || true handle_error "Failed to create Lambda function" fi # Invoke the function to generate some metrics echo "Invoking Lambda function to generate metrics..." for i in {1..5}; do aws lambda invoke --function-name "$FUNCTION_NAME" --payload '{}' /dev/null --region "$REGION" > /dev/null 2>&1 || true sleep 1 done # Go back to original directory cd - > /dev/null # Set the function name for the dashboard DEFAULT_FUNCTION="$FUNCTION_NAME" else # Use the first Lambda function as default DEFAULT_FUNCTION=$(echo "$LAMBDA_FUNCTIONS" | awk '{print $1}') echo "Found Lambda functions. Using $DEFAULT_FUNCTION as default." FUNCTION_NAME="" ROLE_NAME="" fi # Create a dashboard with Lambda metrics and a function name variable echo "Creating CloudWatch dashboard with Lambda function name variable..." # Create a JSON file for the dashboard body with secure permissions DASHBOARD_JSON="dashboard-body-$$.json" touch "$DASHBOARD_JSON" && chmod 600 "$DASHBOARD_JSON" # Escape special characters in region and function name for JSON REGION_ESCAPED=$(printf '%s\n' "$REGION" | sed 's:[\/&]:\\&:g') FUNCTION_ESCAPED=$(printf '%s\n' "$DEFAULT_FUNCTION" | sed 's:[\/&]:\\&:g') cat > "$DASHBOARD_JSON" << EOF { "widgets": [ { "type": "metric", "x": 0, "y": 0, "width": 12, "height": 6, "properties": { "metrics": [ [ "AWS/Lambda", "Invocations", "FunctionName", "\${FunctionName}" ], [ ".", "Errors", ".", "." ], [ ".", "Throttles", ".", "." ] ], "view": "timeSeries", "stacked": false, "region": "$REGION_ESCAPED", "title": "Lambda Function Metrics for \${FunctionName}", "period": 300 } }, { "type": "metric", "x": 0, "y": 6, "width": 12, "height": 6, "properties": { "metrics": [ [ "AWS/Lambda", "Duration", "FunctionName", "\${FunctionName}", { "stat": "Average" } ] ], "view": "timeSeries", "stacked": false, "region": "$REGION_ESCAPED", "title": "Duration for \${FunctionName}", "period": 300 } }, { "type": "metric", "x": 12, "y": 0, "width": 12, "height": 6, "properties": { "metrics": [ [ "AWS/Lambda", "ConcurrentExecutions", "FunctionName", "\${FunctionName}" ] ], "view": "timeSeries", "stacked": false, "region": "$REGION_ESCAPED", "title": "Concurrent Executions for \${FunctionName}", "period": 300 } } ], "periodOverride": "auto", "variables": [ { "type": "property", "id": "FunctionName", "property": "FunctionName", "label": "Lambda Function", "inputType": "select", "values": [ { "value": "$FUNCTION_ESCAPED", "label": "$FUNCTION_ESCAPED" } ] } ] } EOF # Validate JSON before sending if ! jq empty "$DASHBOARD_JSON" 2>/dev/null; then handle_error "Invalid JSON generated for dashboard" fi # Create the dashboard using the JSON file DASHBOARD_NAME="LambdaMetricsDashboard-$(date +%s)" if ! DASHBOARD_RESULT=$(aws cloudwatch put-dashboard \ --dashboard-name "$DASHBOARD_NAME" \ --dashboard-body file://"$DASHBOARD_JSON" \ --region "$REGION" 2>&1); then # If we created resources, clean them up if [ -n "${FUNCTION_NAME:-}" ]; then aws lambda delete-function --function-name "$FUNCTION_NAME" --region "$REGION" 2>/dev/null || true aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 2>/dev/null || true aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null || true fi handle_error "Failed to create CloudWatch dashboard." fi # Display any validation messages but continue if echo "$DASHBOARD_RESULT" | grep -q "DashboardValidationMessages"; then echo "Dashboard created with validation messages:" echo "$DASHBOARD_RESULT" echo "These validation messages are warnings and the dashboard should still function." else echo "Dashboard created successfully!" fi # Verify the dashboard was created echo "Verifying dashboard creation..." if ! DASHBOARD_INFO=$(aws cloudwatch get-dashboard --dashboard-name "$DASHBOARD_NAME" --region "$REGION" 2>&1); then # If we created resources, clean them up if [ -n "${FUNCTION_NAME:-}" ]; then aws lambda delete-function --function-name "$FUNCTION_NAME" --region "$REGION" 2>/dev/null || true aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 2>/dev/null || true aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null || true fi handle_error "Failed to verify dashboard creation." fi echo "Dashboard verification successful!" echo "Dashboard details:" echo "$DASHBOARD_INFO" # List all dashboards to confirm echo "Listing all dashboards:" if ! DASHBOARDS=$(aws cloudwatch list-dashboards --region "$REGION" 2>&1); then # If we created resources, clean them up if [ -n "${FUNCTION_NAME:-}" ]; then aws lambda delete-function --function-name "$FUNCTION_NAME" --region "$REGION" 2>/dev/null || true aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 2>/dev/null || true aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null || true fi handle_error "Failed to list dashboards." fi echo "$DASHBOARDS" # Show instructions for accessing the dashboard echo "" echo "Dashboard created successfully! To access it:" echo "1. Open the CloudWatch console at https://console.aws.amazon.com/cloudwatch/" echo "2. In the navigation pane, choose Dashboards" echo "3. Select $DASHBOARD_NAME" echo "4. You should see a dropdown menu labeled 'Lambda Function' at the top of the dashboard" echo "5. Use this dropdown to select different Lambda functions and see their metrics" echo "" # Create a list of resources for cleanup RESOURCES=("- CloudWatch Dashboard: $DASHBOARD_NAME") if [ -n "${FUNCTION_NAME:-}" ]; then RESOURCES+=("- Lambda Function: $FUNCTION_NAME") RESOURCES+=("- IAM Role: $ROLE_NAME") fi # Prompt for cleanup with automatic yes echo "===========================================" echo "CLEANUP CONFIRMATION" echo "===========================================" echo "Resources created:" for resource in "${RESOURCES[@]}"; do echo "$resource" done echo "" echo "Proceeding with automatic cleanup..." CLEANUP_CHOICE="y" if [[ "${CLEANUP_CHOICE,,}" == "y" ]]; then echo "Cleaning up resources..." # Delete the dashboard if aws cloudwatch delete-dashboards --dashboard-names "$DASHBOARD_NAME" --region "$REGION" 2>/dev/null; then echo "Dashboard deleted successfully." else echo "WARNING: Failed to delete dashboard. You may need to delete it manually." fi # If we created a Lambda function, delete it and its role if [ -n "${FUNCTION_NAME:-}" ]; then echo "Deleting Lambda function..." if aws lambda delete-function --function-name "$FUNCTION_NAME" --region "$REGION" 2>/dev/null; then echo "Lambda function deleted successfully." else echo "WARNING: Failed to delete Lambda function. You may need to delete it manually." fi echo "Detaching role policy..." if aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 2>/dev/null; then echo "Role policy detached successfully." else echo "WARNING: Failed to detach role policy. You may need to detach it manually." fi echo "Deleting IAM role..." if aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null; then echo "IAM role deleted successfully." else echo "WARNING: Failed to delete IAM role. You may need to delete it manually." fi fi # Clean up the JSON file rm -f "$DASHBOARD_JSON" echo "Cleanup complete." fi echo "Script completed successfully!"

以下程式碼範例顯示做法:

  • 建立 Lambda 的 IAM 角色

  • 建立函數程式碼

  • 建立 Lambda 函式

  • 測試您的 Lambda 函數

  • 清除資源

AWS CLI 搭配 Bash 指令碼
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在範例開發人員教學課程儲存庫中設定和執行。

#!/bin/bash # AWS Lambda - Create Your First Function # This script creates a Lambda function, invokes it with a test event, # views CloudWatch logs, and cleans up all resources. # # Source: https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html # # Resources created: # - IAM role (Lambda execution role with basic logging permissions) # - Lambda function (Python 3.13 or Node.js 22.x runtime) # - CloudWatch log group (created automatically by Lambda on invocation) set -eE -o pipefail ############################################################################### # Setup ############################################################################### UNIQUE_ID=$(head -c 8 /dev/urandom | od -An -tx1 | tr -d ' ') FUNCTION_NAME="my-lambda-function-${UNIQUE_ID}" ROLE_NAME="lambda-execution-role-${UNIQUE_ID}" LOG_GROUP_NAME="/aws/lambda/${FUNCTION_NAME}" TEMP_DIR=$(mktemp -d) readonly TEMP_DIR LOG_FILE="${TEMP_DIR}/lambda-gettingstarted.log" exec > >(tee -a "$LOG_FILE") 2>&1 declare -a CREATED_RESOURCES ############################################################################### # Helper functions ############################################################################### cleanup_resources() { # Disable error trap to prevent recursion during cleanup trap - ERR set +eE echo "" echo "Cleaning up resources..." echo "" for ((i=${#CREATED_RESOURCES[@]}-1; i>=0; i--)); do local RESOURCE="${CREATED_RESOURCES[$i]}" local TYPE="${RESOURCE%%:*}" local NAME="${RESOURCE#*:}" case "$TYPE" in log-group) echo "Deleting CloudWatch log group: ${NAME}" aws logs delete-log-group \ --log-group-name "$NAME" 2>&1 || echo " WARNING: Could not delete log group ${NAME}." ;; lambda-function) echo "Deleting Lambda function: ${NAME}" aws lambda delete-function \ --function-name "$NAME" 2>&1 || echo " WARNING: Could not delete Lambda function ${NAME}." echo " Waiting for function deletion to complete..." local DELETE_WAIT=0 while aws lambda get-function --function-name "$NAME" > /dev/null 2>&1; do sleep 2 DELETE_WAIT=$((DELETE_WAIT + 2)) if [ "$DELETE_WAIT" -ge 60 ]; then echo " WARNING: Timed out waiting for function deletion." break fi done ;; iam-role-policy) local ROLE_PART="${NAME%%|*}" local POLICY_PART="${NAME#*|}" echo "Detaching policy from role: ${ROLE_PART}" aws iam detach-role-policy \ --role-name "$ROLE_PART" \ --policy-arn "$POLICY_PART" 2>&1 || echo " WARNING: Could not detach policy from role ${ROLE_PART}." ;; iam-role) echo "Deleting IAM role: ${NAME}" aws iam delete-role \ --role-name "$NAME" 2>&1 || echo " WARNING: Could not delete IAM role ${NAME}." ;; esac done if [ -d "$TEMP_DIR" ]; then rm -rf "$TEMP_DIR" fi echo "" echo "Cleanup complete." } handle_error() { echo "" echo "===========================================" echo "ERROR: Script failed at $1" echo "===========================================" echo "" if [ ${#CREATED_RESOURCES[@]} -gt 0 ]; then echo "Attempting to clean up ${#CREATED_RESOURCES[@]} resource(s)..." cleanup_resources fi exit 1 } trap 'handle_error "line $LINENO"' ERR wait_for_resource() { local DESCRIPTION="$1" local COMMAND="$2" local TARGET_VALUE="$3" local TIMEOUT=300 local ELAPSED=0 local INTERVAL=5 echo "Waiting for ${DESCRIPTION}..." while true; do local RESULT RESULT=$(eval "$COMMAND" 2>&1) || true if echo "$RESULT" | grep -q "$TARGET_VALUE"; then echo " ${DESCRIPTION} is ready." return 0 fi if [ "$ELAPSED" -ge "$TIMEOUT" ]; then echo "ERROR: Timed out waiting for ${DESCRIPTION} after ${TIMEOUT} seconds." return 1 fi sleep "$INTERVAL" ELAPSED=$((ELAPSED + INTERVAL)) done } validate_input() { local input="$1" local pattern="$2" if ! [[ "$input" =~ $pattern ]]; then echo "ERROR: Invalid input: $input" return 1 fi return 0 } ############################################################################### # Region pre-check ############################################################################### CONFIGURED_REGION=$(aws configure get region 2>/dev/null || true) if [ -z "$CONFIGURED_REGION" ] && [ -z "$AWS_DEFAULT_REGION" ] && [ -z "$AWS_REGION" ]; then echo "ERROR: No AWS region configured." echo "Run 'aws configure set region <region>' or export AWS_DEFAULT_REGION." exit 1 fi ############################################################################### # Runtime selection ############################################################################### echo "" echo "===========================================" echo "AWS Lambda - Create Your First Function" echo "===========================================" echo "" echo "Select a runtime for your Lambda function:" echo " 1) Python 3.13" echo " 2) Node.js 22.x" echo "" echo "Using default: Python 3.13" RUNTIME_CHOICE="1" case "$RUNTIME_CHOICE" in 1) RUNTIME="python3.13" HANDLER="lambda_function.lambda_handler" CODE_FILE="lambda_function.py" cat > "${TEMP_DIR}/${CODE_FILE}" << 'PYTHON_EOF' import json import logging logger = logging.getLogger() logger.setLevel(logging.INFO) def lambda_handler(event, context): if not isinstance(event, dict) or 'length' not in event or 'width' not in event: raise ValueError('Event must contain length and width') try: length = float(event['length']) width = float(event['width']) if length < 0 or width < 0: raise ValueError('Length and width must be non-negative') area = calculate_area(length, width) print(f'The area is {area}') logger.info(f'CloudWatch logs group: {context.log_group_name}') return json.dumps({'area': area}) except (TypeError, ValueError) as e: logger.error(f'Error processing input: {str(e)}') raise def calculate_area(length, width): return length * width PYTHON_EOF echo "Selected runtime: Python 3.13" ;; 2) RUNTIME="nodejs22.x" HANDLER="index.handler" CODE_FILE="index.mjs" cat > "${TEMP_DIR}/${CODE_FILE}" << 'NODEJS_EOF' export const handler = async (event, context) => { if (!event || typeof event.length !== 'number' || typeof event.width !== 'number') { throw new Error('Event must contain numeric length and width'); } if (event.length < 0 || event.width < 0) { throw new Error('Length and width must be non-negative'); } const area = event.length * event.width; console.log(`The area is ${area}`); console.log('CloudWatch log group: ', context.logGroupName); return JSON.stringify({area}); }; NODEJS_EOF echo "Selected runtime: Node.js 22.x" ;; *) echo "ERROR: Invalid choice. Please enter 1 or 2." exit 1 ;; esac ############################################################################### # Step 1: Create IAM execution role ############################################################################### echo "" echo "===========================================" echo "Step 1: Create IAM execution role" echo "===========================================" echo "" TRUST_POLICY='{ "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }' echo "Creating IAM role: ${ROLE_NAME}" ROLE_OUTPUT=$(aws iam create-role \ --role-name "$ROLE_NAME" \ --assume-role-policy-document "$TRUST_POLICY" \ --query 'Role.Arn' \ --output text 2>&1) if ! validate_input "$ROLE_OUTPUT" "^arn:aws:iam::[0-9]+:role/"; then echo "ERROR: Failed to create IAM role" exit 1 fi echo "$ROLE_OUTPUT" ROLE_ARN="$ROLE_OUTPUT" CREATED_RESOURCES+=("iam-role:${ROLE_NAME}") echo "Role ARN: ${ROLE_ARN}" aws iam tag-role \ --role-name "$ROLE_NAME" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=lambda-gettingstarted echo "" echo "Attaching AWSLambdaBasicExecutionRole policy..." aws iam attach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 2>&1 CREATED_RESOURCES+=("iam-role-policy:${ROLE_NAME}|arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole") echo "Policy attached." # IAM roles can take a few seconds to propagate echo "Waiting for IAM role to propagate..." sleep 10 ############################################################################### # Step 2: Create Lambda function ############################################################################### echo "" echo "===========================================" echo "Step 2: Create Lambda function" echo "===========================================" echo "" echo "Creating deployment package..." ORIGINAL_DIR=$(pwd) cd "$TEMP_DIR" || exit 1 zip -j function.zip "$CODE_FILE" > /dev/null 2>&1 || { echo "ERROR: Failed to create deployment package" exit 1 } cd "$ORIGINAL_DIR" || exit 1 if [ ! -f "${TEMP_DIR}/function.zip" ]; then echo "ERROR: Deployment package creation failed" exit 1 fi echo "Creating Lambda function: ${FUNCTION_NAME}" echo " Runtime: ${RUNTIME}" echo " Handler: ${HANDLER}" echo "" CREATE_OUTPUT=$(aws lambda create-function \ --function-name "$FUNCTION_NAME" \ --runtime "$RUNTIME" \ --role "$ROLE_ARN" \ --handler "$HANDLER" \ --architectures x86_64 \ --zip-file "fileb://${TEMP_DIR}/function.zip" \ --tags project=doc-smith,tutorial=lambda-gettingstarted \ --query '[FunctionName, FunctionArn, Runtime, State]' \ --output text 2>&1) if [ -z "$CREATE_OUTPUT" ]; then echo "ERROR: Failed to create Lambda function" exit 1 fi echo "$CREATE_OUTPUT" CREATED_RESOURCES+=("lambda-function:${FUNCTION_NAME}") wait_for_resource "Lambda function to become Active" \ "aws lambda get-function-configuration --function-name ${FUNCTION_NAME} --query State --output text" \ "Active" ############################################################################### # Step 3: Invoke the function ############################################################################### echo "" echo "===========================================" echo "Step 3: Invoke the function" echo "===========================================" echo "" TEST_EVENT='{"length": 6, "width": 7}' echo "Invoking function with test event: ${TEST_EVENT}" echo "" echo "$TEST_EVENT" > "${TEMP_DIR}/test-event.json" if ! validate_input "$TEST_EVENT" '"length": [0-9]+, "width": [0-9]+'; then echo "ERROR: Invalid test event format" exit 1 fi INVOKE_OUTPUT=$(aws lambda invoke \ --function-name "$FUNCTION_NAME" \ --payload "fileb://${TEMP_DIR}/test-event.json" \ --cli-read-timeout 30 \ "${TEMP_DIR}/response.json" 2>&1) echo "$INVOKE_OUTPUT" if [ ! -f "${TEMP_DIR}/response.json" ]; then echo "ERROR: No response file generated" exit 1 fi RESPONSE=$(cat "${TEMP_DIR}/response.json") echo "" echo "Function response: ${RESPONSE}" echo "" if echo "$INVOKE_OUTPUT" | grep -qi "functionerror"; then echo "WARNING: Function returned an error." fi ############################################################################### # Step 4: View CloudWatch logs ############################################################################### echo "" echo "===========================================" echo "Step 4: View CloudWatch Logs" echo "===========================================" echo "" echo "Log group: ${LOG_GROUP_NAME}" echo "" echo "Waiting for CloudWatch logs to be available..." LOG_STREAMS="" for i in $(seq 1 6); do LOG_STREAMS=$(aws logs describe-log-streams \ --log-group-name "$LOG_GROUP_NAME" \ --order-by LastEventTime \ --descending \ --query 'logStreams[0].logStreamName' \ --output text 2>/dev/null) || true if [ -n "$LOG_STREAMS" ] && [ "$LOG_STREAMS" != "None" ]; then break fi LOG_STREAMS="" sleep 5 done if [ -n "$LOG_STREAMS" ] && [ "$LOG_STREAMS" != "None" ]; then echo "Latest log stream: ${LOG_STREAMS}" echo "" echo "--- Log events ---" LOG_EVENTS=$(aws logs get-log-events \ --log-group-name "$LOG_GROUP_NAME" \ --log-stream-name "$LOG_STREAMS" \ --query 'events[].message' \ --output text 2>&1) || true echo "$LOG_EVENTS" echo "--- End of log events ---" else echo "No log streams found yet. Logs may take a moment to appear." echo "You can view them in the CloudWatch console:" echo " Log group: ${LOG_GROUP_NAME}" fi CREATED_RESOURCES+=("log-group:${LOG_GROUP_NAME}") aws logs tag-log-group \ --log-group-name "$LOG_GROUP_NAME" \ --tags project=doc-smith,tutorial=lambda-gettingstarted ############################################################################### # Summary and cleanup ############################################################################### echo "" echo "===========================================" echo "SUMMARY" echo "===========================================" echo "" echo "Resources created:" echo " IAM role: ${ROLE_NAME}" echo " Lambda function: ${FUNCTION_NAME}" echo " CloudWatch logs: ${LOG_GROUP_NAME}" echo "" echo "===========================================" echo "CLEANUP" echo "===========================================" echo "" echo "Cleaning up all created resources..." cleanup_resources echo "" echo "Done."

以下程式碼範例顯示做法:

  • 建立 Lambda 函數以進行監控

  • 建立 CloudWatch 儀表板

  • 將屬性變數新增至儀表板

  • 清除資源

AWS CLI 搭配 Bash 指令碼
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在範例開發人員教學課程儲存庫中設定和執行。

#!/bin/bash # CloudWatch Dashboard with Lambda Function Variable Script # This script creates a CloudWatch dashboard with a property variable for Lambda function names set -euo pipefail # Security: Set restrictive umask umask 0077 # Set up logging with secure permissions LOG_FILE="cloudwatch-dashboard-script-v4.log" touch "$LOG_FILE" chmod 600 "$LOG_FILE" echo "Starting script execution at $(date)" >> "$LOG_FILE" # Function to log commands and their output (with sensitive data sanitization) log_cmd() { local cmd="$1" local sanitized_cmd="${cmd//--password*/--password [REDACTED]}" sanitized_cmd="${sanitized_cmd//--secret*/--secret [REDACTED]}" echo "$(date): Running command: $sanitized_cmd" >> "$LOG_FILE" eval "$cmd" 2>&1 | tee -a "$LOG_FILE" return ${PIPESTATUS[0]} } # Function to check for errors in command output check_error() { local cmd_output="$1" local cmd_status="$2" local error_msg="$3" if [ $cmd_status -ne 0 ] || echo "$cmd_output" | grep -qi "error"; then echo "ERROR: $error_msg" | tee -a "$LOG_FILE" # Sanitize output before logging local sanitized_output="${cmd_output//arn:aws:iam::[0-9]*/arn:aws:iam::ACCOUNT_ID}" echo "Command output: $sanitized_output" | tee -a "$LOG_FILE" cleanup_resources exit 1 fi } # Trap errors and cleanup trap 'cleanup_resources' EXIT ERR INT TERM # Function to clean up resources cleanup_resources() { local exit_code=$? echo "" | tee -a "$LOG_FILE" echo "==========================================" | tee -a "$LOG_FILE" echo "CLEANUP PROCESS" | tee -a "$LOG_FILE" echo "==========================================" | tee -a "$LOG_FILE" if [ -n "${DASHBOARD_NAME:-}" ]; then echo "Deleting CloudWatch dashboard: $DASHBOARD_NAME" | tee -a "$LOG_FILE" aws cloudwatch delete-dashboards --dashboard-names "$DASHBOARD_NAME" 2>&1 >> "$LOG_FILE" || true fi if [ -n "${LAMBDA_FUNCTION1:-}" ]; then echo "Deleting Lambda function: $LAMBDA_FUNCTION1" | tee -a "$LOG_FILE" aws lambda delete-function --function-name "$LAMBDA_FUNCTION1" 2>&1 >> "$LOG_FILE" || true fi if [ -n "${LAMBDA_FUNCTION2:-}" ]; then echo "Deleting Lambda function: $LAMBDA_FUNCTION2" | tee -a "$LOG_FILE" aws lambda delete-function --function-name "$LAMBDA_FUNCTION2" 2>&1 >> "$LOG_FILE" || true fi if [ -n "${ROLE_NAME:-}" ]; then echo "Detaching policy from role: $ROLE_NAME" | tee -a "$LOG_FILE" aws iam detach-role-policy --role-name "$ROLE_NAME" --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 2>&1 >> "$LOG_FILE" || true echo "Deleting IAM role: $ROLE_NAME" | tee -a "$LOG_FILE" aws iam delete-role --role-name "$ROLE_NAME" 2>&1 >> "$LOG_FILE" || true fi # Clean up temporary files securely shred -vfz -n 3 trust-policy.json lambda_function.py lambda_function.zip 2>/dev/null || rm -f trust-policy.json lambda_function.py lambda_function.zip echo "Cleanup completed." | tee -a "$LOG_FILE" return $exit_code } # Validate AWS CLI is installed and authenticated if ! command -v aws &> /dev/null; then echo "ERROR: AWS CLI is not installed" | tee -a "$LOG_FILE" exit 1 fi if ! aws sts get-caller-identity &> /dev/null; then echo "ERROR: AWS CLI is not properly authenticated" | tee -a "$LOG_FILE" exit 1 fi # Get AWS region with validation AWS_REGION=$(aws configure get region 2>/dev/null || echo "") if [ -z "$AWS_REGION" ]; then AWS_REGION="us-east-1" echo "No region found in AWS config, defaulting to $AWS_REGION" | tee -a "$LOG_FILE" else echo "Using AWS region: $AWS_REGION" | tee -a "$LOG_FILE" fi # Validate region format if ! [[ "$AWS_REGION" =~ ^[a-z]{2}-[a-z]+-[0-9]$ ]]; then echo "ERROR: Invalid AWS region format: $AWS_REGION" | tee -a "$LOG_FILE" exit 1 fi # Generate unique identifiers using secure random with validation RANDOM_ID=$(openssl rand -hex 6) if [ -z "$RANDOM_ID" ] || [ ${#RANDOM_ID} -ne 12 ]; then echo "ERROR: Failed to generate valid random identifier" | tee -a "$LOG_FILE" exit 1 fi DASHBOARD_NAME="LambdaMetricsDashboard-${RANDOM_ID}" LAMBDA_FUNCTION1="TestFunction1-${RANDOM_ID}" LAMBDA_FUNCTION2="TestFunction2-${RANDOM_ID}" ROLE_NAME="LambdaExecutionRole-${RANDOM_ID}" # Validate resource names don't exceed AWS limits if [ ${#DASHBOARD_NAME} -gt 128 ] || [ ${#LAMBDA_FUNCTION1} -gt 64 ] || [ ${#ROLE_NAME} -gt 64 ]; then echo "ERROR: Generated resource names exceed AWS limits" | tee -a "$LOG_FILE" exit 1 fi echo "Using random identifier: $RANDOM_ID" | tee -a "$LOG_FILE" echo "Dashboard name: $DASHBOARD_NAME" | tee -a "$LOG_FILE" echo "Lambda function names: $LAMBDA_FUNCTION1, $LAMBDA_FUNCTION2" | tee -a "$LOG_FILE" echo "IAM role name: $ROLE_NAME" | tee -a "$LOG_FILE" # Create IAM role for Lambda functions echo "Creating IAM role for Lambda..." | tee -a "$LOG_FILE" TRUST_POLICY='{ "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }' echo "$TRUST_POLICY" > trust-policy.json chmod 600 trust-policy.json # Validate JSON before use if ! python3 -m json.tool trust-policy.json > /dev/null 2>&1; then echo "ERROR: Invalid trust policy JSON" | tee -a "$LOG_FILE" exit 1 fi ROLE_OUTPUT=$(log_cmd "aws iam create-role --role-name '$ROLE_NAME' --assume-role-policy-document file://trust-policy.json --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudwatch-streams --output json") check_error "$ROLE_OUTPUT" $? "Failed to create IAM role" ROLE_ARN=$(echo "$ROLE_OUTPUT" | python3 -c "import sys, json; print(json.load(sys.stdin)['Role']['Arn'])" 2>/dev/null) if [ -z "$ROLE_ARN" ]; then echo "ERROR: Failed to extract Role ARN" | tee -a "$LOG_FILE" exit 1 fi echo "Role ARN: $ROLE_ARN" | tee -a "$LOG_FILE" # Attach Lambda basic execution policy to the role echo "Attaching Lambda execution policy to role..." | tee -a "$LOG_FILE" POLICY_OUTPUT=$(log_cmd "aws iam attach-role-policy --role-name '$ROLE_NAME' --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole") check_error "$POLICY_OUTPUT" $? "Failed to attach policy to role" # Wait for role to propagate echo "Waiting for IAM role to propagate..." | tee -a "$LOG_FILE" sleep 10 # Create simple Python Lambda function code with security validation echo "Creating Lambda function code..." | tee -a "$LOG_FILE" cat > lambda_function.py << 'LAMBDA_EOF' def handler(event, context): print("Lambda function executed successfully") return { 'statusCode': 200, 'body': 'Success' } LAMBDA_EOF chmod 600 lambda_function.py # Validate Python syntax if ! python3 -m py_compile lambda_function.py 2>/dev/null; then echo "ERROR: Invalid Python syntax in Lambda function" | tee -a "$LOG_FILE" exit 1 fi # Zip the Lambda function code zip -j -q lambda_function.zip lambda_function.py if [ ! -f lambda_function.zip ]; then echo "ERROR: Failed to create lambda_function.zip" | tee -a "$LOG_FILE" exit 1 fi chmod 600 lambda_function.zip # Validate zip file integrity if ! unzip -t lambda_function.zip > /dev/null 2>&1; then echo "ERROR: Created zip file is corrupted" | tee -a "$LOG_FILE" exit 1 fi # Create first Lambda function echo "Creating first Lambda function: $LAMBDA_FUNCTION1..." | tee -a "$LOG_FILE" LAMBDA1_OUTPUT=$(log_cmd "aws lambda create-function --function-name '$LAMBDA_FUNCTION1' --runtime python3.11 --role '$ROLE_ARN' --handler lambda_function.handler --zip-file fileb://lambda_function.zip --timeout 30 --memory-size 128 --tags project=doc-smith,tutorial=cloudwatch-streams") check_error "$LAMBDA1_OUTPUT" $? "Failed to create first Lambda function" # Create second Lambda function echo "Creating second Lambda function: $LAMBDA_FUNCTION2..." | tee -a "$LOG_FILE" LAMBDA2_OUTPUT=$(log_cmd "aws lambda create-function --function-name '$LAMBDA_FUNCTION2' --runtime python3.11 --role '$ROLE_ARN' --handler lambda_function.handler --zip-file fileb://lambda_function.zip --timeout 30 --memory-size 128 --tags project=doc-smith,tutorial=cloudwatch-streams") check_error "$LAMBDA2_OUTPUT" $? "Failed to create second Lambda function" # Invoke Lambda functions to generate some metrics echo "Invoking Lambda functions to generate metrics..." | tee -a "$LOG_FILE" log_cmd "aws lambda invoke --function-name '$LAMBDA_FUNCTION1' --payload '{}' /dev/null" || true log_cmd "aws lambda invoke --function-name '$LAMBDA_FUNCTION2' --payload '{}' /dev/null" || true # Create CloudWatch dashboard with property variable echo "Creating CloudWatch dashboard with property variable..." | tee -a "$LOG_FILE" # Create dashboard body with proper escaping and validation DASHBOARD_BODY=$(cat <<EOF { "widgets": [ { "type": "metric", "x": 0, "y": 0, "width": 12, "height": 6, "properties": { "metrics": [ [ "AWS/Lambda", "Invocations", "FunctionName", "$LAMBDA_FUNCTION1" ] ], "view": "timeSeries", "stacked": false, "region": "$AWS_REGION", "title": "Lambda Invocations", "period": 300, "stat": "Sum" } } ] } EOF ) # Validate JSON before sending if ! echo "$DASHBOARD_BODY" | python3 -m json.tool > /dev/null 2>&1; then echo "ERROR: Dashboard body is not valid JSON" | tee -a "$LOG_FILE" exit 1 fi # First create a basic dashboard without variables echo "Creating initial dashboard without variables..." | tee -a "$LOG_FILE" DASHBOARD_OUTPUT=$(aws cloudwatch put-dashboard --dashboard-name "$DASHBOARD_NAME" --dashboard-body "$DASHBOARD_BODY" --output json 2>&1) check_error "$DASHBOARD_OUTPUT" $? "Failed to create initial CloudWatch dashboard" # Now let's try to add a property variable using the console instructions echo "To complete the tutorial, please follow these steps in the CloudWatch console:" | tee -a "$LOG_FILE" echo "1. Open the CloudWatch console at https://console.aws.amazon.com/cloudwatch/" | tee -a "$LOG_FILE" echo "2. Navigate to Dashboards and select your dashboard: $DASHBOARD_NAME" | tee -a "$LOG_FILE" echo "3. Choose Actions > Variables > Create a variable" | tee -a "$LOG_FILE" echo "4. Choose Property variable" | tee -a "$LOG_FILE" echo "5. For Property that the variable changes, choose FunctionName" | tee -a "$LOG_FILE" echo "6. For Input type, choose Select menu (dropdown)" | tee -a "$LOG_FILE" echo "7. Choose Use the results of a metric search" | tee -a "$LOG_FILE" echo "8. Choose Pre-built queries > Lambda > Errors" | tee -a "$LOG_FILE" echo "9. Choose By Function Name and then choose Search" | tee -a "$LOG_FILE" echo "10. (Optional) Configure any secondary settings as desired" | tee -a "$LOG_FILE" echo "11. Choose Add variable" | tee -a "$LOG_FILE" echo "" | tee -a "$LOG_FILE" echo "The dashboard has been created and can be accessed at:" | tee -a "$LOG_FILE" echo "https://console.aws.amazon.com/cloudwatch/home#dashboards:name=$DASHBOARD_NAME" | tee -a "$LOG_FILE" # Verify dashboard creation echo "Verifying dashboard creation..." | tee -a "$LOG_FILE" VERIFY_OUTPUT=$(aws cloudwatch get-dashboard --dashboard-name "$DASHBOARD_NAME" --output json 2>&1) check_error "$VERIFY_OUTPUT" $? "Failed to verify dashboard creation" echo "" | tee -a "$LOG_FILE" echo "==========================================" | tee -a "$LOG_FILE" echo "DASHBOARD CREATED SUCCESSFULLY" | tee -a "$LOG_FILE" echo "==========================================" | tee -a "$LOG_FILE" echo "Dashboard Name: $DASHBOARD_NAME" | tee -a "$LOG_FILE" echo "Lambda Functions: $LAMBDA_FUNCTION1, $LAMBDA_FUNCTION2" | tee -a "$LOG_FILE" echo "" | tee -a "$LOG_FILE" echo "You can view your dashboard in the CloudWatch console:" | tee -a "$LOG_FILE" echo "https://console.aws.amazon.com/cloudwatch/home#dashboards:name=$DASHBOARD_NAME" | tee -a "$LOG_FILE" echo "" | tee -a "$LOG_FILE" # Auto-confirm cleanup echo "" | tee -a "$LOG_FILE" echo "==========================================" | tee -a "$LOG_FILE" echo "CLEANUP CONFIRMATION" | tee -a "$LOG_FILE" echo "==========================================" | tee -a "$LOG_FILE" echo "The following resources were created:" | tee -a "$LOG_FILE" echo "- CloudWatch Dashboard: $DASHBOARD_NAME" | tee -a "$LOG_FILE" echo "- Lambda Function: $LAMBDA_FUNCTION1" | tee -a "$LOG_FILE" echo "- Lambda Function: $LAMBDA_FUNCTION2" | tee -a "$LOG_FILE" echo "- IAM Role: $ROLE_NAME" | tee -a "$LOG_FILE" echo "" | tee -a "$LOG_FILE" echo "Auto-confirming cleanup of all created resources..." | tee -a "$LOG_FILE" echo "Script completed successfully." | tee -a "$LOG_FILE" exit 0