There are more AWS SDK examples available in the AWS Doc SDK Examples
Using property variables in CloudWatch dashboards to monitor multiple Lambda functions
The following code example shows how to:
Create Lambda functions for monitoring
Create a CloudWatch dashboard
Add a property variable to the dashboard
Clean up resources
- Bash
-
- AWS CLI with Bash script
-
Note
There's more on GitHub. Find the complete example and learn how to set up and run in the Sample developer tutorials
repository. #!/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 --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") 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") 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-
For API details, see the following topics in AWS CLI Command Reference.
-
Use the Neptune API to query graph data
Write custom activity data with a Lambda function after Amazon Cognito user authentication