함수 이름을 변수로 사용한 모니터링 대시보드 생성하기

함수 이름을 변수로 사용한 모니터링 대시보드 생성하기

이 코드 예제에서는 Lambda 함수 이름을 변수로 사용하는 CloudWatch 대시보드를 만드는 방법을 보여드려요.

출처: 문서

본문

다음 작업을 다루어요.

  • CloudWatch 대시보드 생성
  • 함수 이름 변수를 사용한 Lambda 메트릭 위젯 추가
  • 대시보드 검증
  • 리소스 정리

Bash (AWS CLI with Bash script)

Note GitHub에 더 많은 내용이 있어요. 전체 예제와 설정·실행 방법은 Sample developer tutorials 저장소에서 확인할 수 있어요.

이 스크립트는 Lambda 함수 이름을 변수(property variable)로 사용해 서로 다른 Lambda 함수를 전환할 수 있는 CloudWatch 대시보드를 만들어요.

#!/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!"

API 상세는 AWS CLI Command Reference의 다음 항목을 참고해요.

  • AttachRolePolicy, CreateFunction, CreateRole, DeleteDashboards, DeleteFunction, DeleteRole, DetachRolePolicy, GetCallerIdentity, GetDashboard, Invoke, ListDashboards, ListFunctions, PutDashboard

더 알아보기 (Learn more)