diff --git a/.doc_gen/metadata/sns_metadata.yaml b/.doc_gen/metadata/sns_metadata.yaml index 39eded3d823..ef5ec1d41f6 100644 --- a/.doc_gen/metadata/sns_metadata.yaml +++ b/.doc_gen/metadata/sns_metadata.yaml @@ -1393,3 +1393,42 @@ sns_UsageSmsTopic: - sns.java2.PublishTextSMS.main services: sns: {} + +sns_Scenario_CloudWatchSnsMonitoring: + title: Monitor infrastructure with &CW; alarms and &SNS; notifications using an &AWS; SDK + title_abbrev: Monitor infrastructure with &CW; and &SNS; + synopsis: create &CW; alarms that send &SNS; notifications when infrastructure metrics breach thresholds. + category: Scenarios + languages: + Python: + versions: + - sdk_version: 3 + github: python/example_code/sns + excerpts: + - description: Create an &SNS; topic and email subscription for alarm notifications. + snippet_tags: + - python.example_code.sns.CreateTopicForAlarm + - description: Create a &CW; alarm with an &SNS; action. + snippet_tags: + - python.example_code.cloudwatch.PutMetricAlarmWithSns + - description: Create a &CW; dashboard to visualize metrics. + snippet_tags: + - python.example_code.cloudwatch.PutDashboard + - description: Publish metric data to trigger the alarm. + snippet_tags: + - python.example_code.cloudwatch.PutMetricDataForAlarm + - description: Check the alarm state. + snippet_tags: + - python.example_code.cloudwatch.DescribeAlarmsState + - description: Retrieve alarm history. + snippet_tags: + - python.example_code.cloudwatch.DescribeAlarmHistory + - description: Clean up monitoring resources. + snippet_tags: + - python.example_code.cloudwatch.DeleteMonitoringResources + - description: Run the complete scenario. + snippet_tags: + - python.example_code.sns.Scenario_CloudWatchSnsMonitoring + services: + sns: {CreateTopic, Subscribe, Unsubscribe, DeleteTopic} + cloudwatch: {PutMetricAlarm, PutMetricData, DescribeAlarms, DescribeAlarmHistory, DeleteAlarms, PutDashboard, DeleteDashboards} diff --git a/python/example_code/cloudwatch/README.md b/python/example_code/cloudwatch/README.md index 04fe0842019..b3e344a5875 100644 --- a/python/example_code/cloudwatch/README.md +++ b/python/example_code/cloudwatch/README.md @@ -53,6 +53,7 @@ Code examples that show you how to accomplish a specific task by calling multipl functions within the same service. - [Manage metrics and alarms](cloudwatch_basics.py) +- [Monitor infrastructure with CloudWatch and Amazon SNS](../sns/scenario_cloudwatch_sns_monitoring.py) @@ -90,6 +91,24 @@ python cloudwatch_basics.py +#### Monitor infrastructure with CloudWatch and Amazon SNS + +This example shows you how to create CloudWatch alarms that send Amazon SNS notifications when infrastructure metrics breach thresholds. + + + + + +Start the example by running the following at a command prompt: + +``` +python ../sns/scenario_cloudwatch_sns_monitoring.py +``` + + + + + ### Tests ⚠ Running tests might result in charges to your AWS account. @@ -116,4 +135,4 @@ in the `python` folder. Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 \ No newline at end of file +SPDX-License-Identifier: Apache-2.0 diff --git a/python/example_code/sns/README.md b/python/example_code/sns/README.md index 469fd5d3aa5..2b13f7b0457 100644 --- a/python/example_code/sns/README.md +++ b/python/example_code/sns/README.md @@ -55,6 +55,7 @@ functions within the same service. - [Create an Amazon Textract explorer application](../../cross_service/textract_explorer) - [Create and publish to a FIFO topic](sns_fifo_topic.py) - [Detect people and objects in a video](../../example_code/rekognition) +- [Monitor infrastructure with CloudWatch and Amazon SNS](scenario_cloudwatch_sns_monitoring.py) - [Publish an SMS text message](sns_basics.py) - [Publish messages to queues](../../cross_service/topics_and_queues/topics_and_queues_scenario.py) - [Use API Gateway to invoke a Lambda function](../../example_code/lambda) @@ -121,6 +122,24 @@ This example shows you how to detect people and objects in a video with Amazon R +#### Monitor infrastructure with CloudWatch and Amazon SNS + +This example shows you how to create CloudWatch alarms that send Amazon SNS notifications when infrastructure metrics breach thresholds. + + + + + +Start the example by running the following at a command prompt: + +``` +python scenario_cloudwatch_sns_monitoring.py +``` + + + + + #### Publish an SMS text message This example shows you how to publish SMS messages using Amazon SNS. diff --git a/python/example_code/sns/scenario_cloudwatch_sns_monitoring.py b/python/example_code/sns/scenario_cloudwatch_sns_monitoring.py new file mode 100644 index 00000000000..7e86a0be525 --- /dev/null +++ b/python/example_code/sns/scenario_cloudwatch_sns_monitoring.py @@ -0,0 +1,288 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Purpose + +Shows how to use Amazon CloudWatch to monitor infrastructure metrics and send +alerts via Amazon SNS when thresholds are breached. + +This scenario demonstrates: +1. Creating an SNS topic and email subscription for alarm notifications +2. Creating a CloudWatch alarm that monitors a custom metric +3. Publishing metric data to trigger alarm state changes +4. Retrieving alarm history to view state transitions +5. Cleaning up all created resources +""" + +import logging +import sys +import time +from datetime import datetime, timezone + +import boto3 +from botocore.exceptions import ClientError + +sys.path.append("../..") +from demo_tools import question as q + +logger = logging.getLogger(__name__) + + +# snippet-start:[python.example_code.sns.Scenario_CloudWatchSnsMonitoring] +class CloudWatchSnsMonitoringScenario: + """Runs an interactive scenario demonstrating CloudWatch monitoring with SNS alerts.""" + + def __init__(self, sns_resource, cloudwatch_client): + """ + :param sns_resource: A Boto3 SNS resource. + :param cloudwatch_client: A Boto3 CloudWatch client. + """ + self.sns_resource = sns_resource + self.cloudwatch_client = cloudwatch_client + self.topic = None + self.subscription = None + self.alarm_name = None + self.namespace = None + self.metric_name = "ErrorCount" + self.dashboard_name = None + + def run_scenario(self): + """Runs the CloudWatch SNS monitoring scenario.""" + print("-" * 88) + print("Welcome to the CloudWatch Monitoring with SNS Alerts Scenario.") + print("-" * 88) + print( + "This scenario demonstrates how to:\n" + "1. Create an SNS topic for alarm notifications\n" + "2. Create a CloudWatch alarm that monitors a custom metric\n" + "3. Publish metric data to trigger the alarm\n" + "4. Receive email notifications when the alarm state changes\n" + ) + + try: + self._setup_phase() + self._publish_metrics_phase() + self._demonstrate_alarm_phase() + except Exception as e: + logger.exception("Scenario failed: %s", e) + print(f"\nThe scenario encountered an error: {e}") + finally: + self._cleanup_phase() + + def _setup_phase(self): + """Setup phase: Create SNS topic, subscription, alarm, and dashboard.""" + # snippet-start:[python.example_code.sns.CreateTopicForAlarm] + print("\n" + "-" * 88) + print("Setup Phase") + print("-" * 88) + + email = q.ask("Enter an email address to receive alarm notifications: ", q.non_empty) + + print("\nCreating SNS topic...") + self.topic = self.sns_resource.create_topic(Name="cloudwatch-alarms-topic") + print(f"SNS topic created: {self.topic.arn}") + + print("Creating email subscription...") + self.subscription = self.topic.subscribe(Protocol="email", Endpoint=email) + print("Email subscription created. Please check your email and confirm the subscription.") + # snippet-end:[python.example_code.sns.CreateTopicForAlarm] + + # snippet-start:[python.example_code.cloudwatch.PutMetricAlarmWithSns] + self.alarm_name = q.ask("\nEnter a name for the CloudWatch alarm: ", q.non_empty) + + namespace_input = input("Enter a name for the custom metric namespace (default: CustomApp/Monitoring): ") + self.namespace = namespace_input.strip() if namespace_input.strip() else "CustomApp/Monitoring" + + print(f"\nCreating CloudWatch alarm '{self.alarm_name}'...") + print(f"Alarm will trigger when {self.metric_name} >= 10 for 1 evaluation period (1 minute).") + + self.cloudwatch_client.put_metric_alarm( + AlarmName=self.alarm_name, + ComparisonOperator="GreaterThanOrEqualToThreshold", + EvaluationPeriods=1, + MetricName=self.metric_name, + Namespace=self.namespace, + Period=60, + Statistic="Average", + Threshold=10.0, + ActionsEnabled=True, + AlarmActions=[self.topic.arn], + AlarmDescription="Alarm when error count exceeds threshold", + ) + print("CloudWatch alarm created successfully.") + # snippet-end:[python.example_code.cloudwatch.PutMetricAlarmWithSns] + + # snippet-start:[python.example_code.cloudwatch.PutDashboard] + self.dashboard_name = "monitoring-dashboard" + print(f"\nCreating CloudWatch dashboard '{self.dashboard_name}'...") + + dashboard_body = { + "widgets": [ + { + "type": "metric", + "properties": { + "metrics": [[self.namespace, self.metric_name]], + "period": 60, + "stat": "Average", + "region": self.cloudwatch_client.meta.region_name, + "title": "Error Count Monitoring" + } + } + ] + } + + import json + self.cloudwatch_client.put_dashboard( + DashboardName=self.dashboard_name, + DashboardBody=json.dumps(dashboard_body) + ) + + region = self.cloudwatch_client.meta.region_name + print(f"Dashboard '{self.dashboard_name}' created successfully.") + print(f"View at: https://console.aws.amazon.com/cloudwatch/home?region={region}#dashboards:name={self.dashboard_name}") + print("-" * 88) + # snippet-end:[python.example_code.cloudwatch.PutDashboard] + + def _publish_metrics_phase(self): + """Publish metric data to demonstrate alarm triggering.""" + # snippet-start:[python.example_code.cloudwatch.PutMetricDataForAlarm] + print("\n" + "-" * 88) + print("Publishing Metric Data") + print("-" * 88) + + print("Publishing normal metric data (ErrorCount = 5)...") + self.cloudwatch_client.put_metric_data( + Namespace=self.namespace, + MetricData=[ + { + "MetricName": self.metric_name, + "Value": 5.0, + "Unit": "Count", + "Timestamp": datetime.now(timezone.utc) + } + ] + ) + print("Metric data published successfully.") + # snippet-end:[python.example_code.cloudwatch.PutMetricDataForAlarm] + + # snippet-start:[python.example_code.cloudwatch.DescribeAlarmsState] + time.sleep(5) + print("\nChecking alarm state...") + alarms = self.cloudwatch_client.describe_alarms(AlarmNames=[self.alarm_name]) + if alarms["MetricAlarms"]: + alarm = alarms["MetricAlarms"][0] + print(f"Current alarm state: {alarm['StateValue']}") + print(f"Alarm reason: {alarm['StateReason']}") + # snippet-end:[python.example_code.cloudwatch.DescribeAlarmsState] + + print("\nPublishing high metric data to trigger alarm (ErrorCount = 15)...") + self.cloudwatch_client.put_metric_data( + Namespace=self.namespace, + MetricData=[ + { + "MetricName": self.metric_name, + "Value": 15.0, + "Unit": "Count", + "Timestamp": datetime.now(timezone.utc) + } + ] + ) + print("Metric data published successfully.") + + print("\nWaiting for alarm state to change (this may take up to 1 minute)...") + for _ in range(12): + time.sleep(5) + alarms = self.cloudwatch_client.describe_alarms(AlarmNames=[self.alarm_name]) + if alarms["MetricAlarms"]: + alarm = alarms["MetricAlarms"][0] + if alarm["StateValue"] == "ALARM": + print(f"Alarm state changed to: {alarm['StateValue']}") + print(f"Alarm reason: {alarm['StateReason']}") + print(f"\nAn email notification has been sent to your email address.") + print("Check your email for the alarm notification.") + break + print("-" * 88) + + def _demonstrate_alarm_phase(self): + """Retrieve and display alarm history.""" + # snippet-start:[python.example_code.cloudwatch.DescribeAlarmHistory] + print("\n" + "-" * 88) + print("Alarm History") + print("-" * 88) + + print("Retrieving alarm history...") + history = self.cloudwatch_client.describe_alarm_history( + AlarmName=self.alarm_name, + HistoryItemType="StateUpdate", + MaxRecords=5 + ) + + if history["AlarmHistoryItems"]: + print("\nRecent alarm state changes:") + for i, item in enumerate(history["AlarmHistoryItems"], 1): + timestamp = item["Timestamp"].strftime("%Y-%m-%dT%H:%M:%SZ") + print(f"{i}. {timestamp}: {item['HistorySummary']}") + else: + print("No alarm history available yet.") + print("-" * 88) + # snippet-end:[python.example_code.cloudwatch.DescribeAlarmHistory] + + def _cleanup_phase(self): + """Cleanup phase: Delete all created resources.""" + # snippet-start:[python.example_code.cloudwatch.DeleteMonitoringResources] + if not self.alarm_name: + return + + print("\n" + "-" * 88) + print("Cleanup") + print("-" * 88) + + delete_resources = q.ask( + "Delete all resources created by this scenario? (y/n) ", q.is_yesno + ) + + if delete_resources: + try: + if self.dashboard_name: + print("\nDeleting CloudWatch dashboard...") + self.cloudwatch_client.delete_dashboards(DashboardNames=[self.dashboard_name]) + print("Dashboard deleted successfully.") + + print("\nDeleting CloudWatch alarm...") + self.cloudwatch_client.delete_alarms(AlarmNames=[self.alarm_name]) + print("Alarm deleted successfully.") + + if self.subscription: + print("\nUnsubscribing from SNS topic...") + self.subscription.delete() + print("Subscription removed.") + + if self.topic: + print("\nDeleting SNS topic...") + self.topic.delete() + print("SNS topic deleted successfully.") + + print("\nAll resources cleaned up successfully.") + except ClientError as e: + print(f"Error during cleanup: {e}") + else: + print("\nResources will remain active.") + print(f"SNS Topic ARN: {self.topic.arn if self.topic else 'N/A'}") + print(f"Alarm Name: {self.alarm_name}") + + print("-" * 88) + print("CloudWatch Monitoring with SNS Alerts scenario completed.") + # snippet-end:[python.example_code.cloudwatch.DeleteMonitoringResources] + + +# snippet-end:[python.example_code.sns.Scenario_CloudWatchSnsMonitoring] + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") + + scenario = CloudWatchSnsMonitoringScenario( + boto3.resource("sns"), + boto3.client("cloudwatch") + ) + scenario.run_scenario() diff --git a/python/example_code/sns/test/test_scenario_cloudwatch_sns_monitoring.py b/python/example_code/sns/test/test_scenario_cloudwatch_sns_monitoring.py new file mode 100644 index 00000000000..445ba87b77e --- /dev/null +++ b/python/example_code/sns/test/test_scenario_cloudwatch_sns_monitoring.py @@ -0,0 +1,174 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Integration tests for scenario_cloudwatch_sns_monitoring.py +""" + +import boto3 +import pytest +from unittest.mock import MagicMock, patch +from botocore.exceptions import ClientError + +from scenario_cloudwatch_sns_monitoring import CloudWatchSnsMonitoringScenario + + +@pytest.fixture +def scenario(): + """Create a scenario instance with mocked AWS resources.""" + sns_resource = MagicMock() + cloudwatch_client = MagicMock() + cloudwatch_client.meta.region_name = "us-east-1" + return CloudWatchSnsMonitoringScenario(sns_resource, cloudwatch_client) + + +@patch("scenario_cloudwatch_sns_monitoring.q") +def test_setup_phase(mock_q, scenario): + """Test the setup phase creates all required resources.""" + mock_q.ask.side_effect = ["test@example.com", "TestAlarm"] + + mock_topic = MagicMock() + mock_topic.arn = "arn:aws:sns:us-east-1:123456789012:cloudwatch-alarms-topic" + scenario.sns_resource.create_topic.return_value = mock_topic + + mock_subscription = MagicMock() + mock_topic.subscribe.return_value = mock_subscription + + with patch("builtins.input", return_value=""): + scenario._setup_phase() + + scenario.sns_resource.create_topic.assert_called_once_with(Name="cloudwatch-alarms-topic") + mock_topic.subscribe.assert_called_once_with(Protocol="email", Endpoint="test@example.com") + scenario.cloudwatch_client.put_metric_alarm.assert_called_once() + scenario.cloudwatch_client.put_dashboard.assert_called_once() + + assert scenario.topic == mock_topic + assert scenario.subscription == mock_subscription + assert scenario.alarm_name == "TestAlarm" + assert scenario.namespace == "CustomApp/Monitoring" + + +def test_publish_metrics_phase(scenario): + """Test publishing metrics and checking alarm state.""" + scenario.namespace = "CustomApp/Monitoring" + scenario.alarm_name = "TestAlarm" + + scenario.cloudwatch_client.describe_alarms.return_value = { + "MetricAlarms": [{ + "StateValue": "ALARM", + "StateReason": "Threshold Crossed" + }] + } + + scenario._publish_metrics_phase() + + assert scenario.cloudwatch_client.put_metric_data.call_count == 2 + scenario.cloudwatch_client.describe_alarms.assert_called() + + +def test_demonstrate_alarm_phase(scenario): + """Test retrieving and displaying alarm history.""" + scenario.alarm_name = "TestAlarm" + + scenario.cloudwatch_client.describe_alarm_history.return_value = { + "AlarmHistoryItems": [ + { + "Timestamp": MagicMock(), + "HistorySummary": "State changed from OK to ALARM" + } + ] + } + + scenario._demonstrate_alarm_phase() + + scenario.cloudwatch_client.describe_alarm_history.assert_called_once_with( + AlarmName="TestAlarm", + HistoryItemType="StateUpdate", + MaxRecords=5 + ) + + +@patch("scenario_cloudwatch_sns_monitoring.q") +def test_cleanup_phase_with_deletion(mock_q, scenario): + """Test cleanup phase when user chooses to delete resources.""" + mock_q.ask.return_value = True + + scenario.alarm_name = "TestAlarm" + scenario.dashboard_name = "monitoring-dashboard" + + mock_topic = MagicMock() + mock_topic.arn = "arn:aws:sns:us-east-1:123456789012:test-topic" + scenario.topic = mock_topic + + mock_subscription = MagicMock() + scenario.subscription = mock_subscription + + scenario._cleanup_phase() + + scenario.cloudwatch_client.delete_dashboards.assert_called_once_with( + DashboardNames=["monitoring-dashboard"] + ) + scenario.cloudwatch_client.delete_alarms.assert_called_once_with( + AlarmNames=["TestAlarm"] + ) + mock_subscription.delete.assert_called_once() + mock_topic.delete.assert_called_once() + + +@patch("scenario_cloudwatch_sns_monitoring.q") +def test_cleanup_phase_without_deletion(mock_q, scenario): + """Test cleanup phase when user chooses not to delete resources.""" + mock_q.ask.return_value = False + + scenario.alarm_name = "TestAlarm" + mock_topic = MagicMock() + mock_topic.arn = "arn:aws:sns:us-east-1:123456789012:test-topic" + scenario.topic = mock_topic + + scenario._cleanup_phase() + + scenario.cloudwatch_client.delete_dashboards.assert_not_called() + scenario.cloudwatch_client.delete_alarms.assert_not_called() + + +@patch("scenario_cloudwatch_sns_monitoring.q") +def test_run_scenario_success(mock_q, scenario): + """Test complete scenario execution.""" + mock_q.ask.side_effect = ["test@example.com", "TestAlarm", False] + + mock_topic = MagicMock() + mock_topic.arn = "arn:aws:sns:us-east-1:123456789012:test-topic" + scenario.sns_resource.create_topic.return_value = mock_topic + + scenario.cloudwatch_client.describe_alarms.return_value = { + "MetricAlarms": [{ + "StateValue": "ALARM", + "StateReason": "Threshold Crossed" + }] + } + + scenario.cloudwatch_client.describe_alarm_history.return_value = { + "AlarmHistoryItems": [] + } + + with patch("builtins.input", return_value=""): + scenario.run_scenario() + + scenario.sns_resource.create_topic.assert_called_once() + scenario.cloudwatch_client.put_metric_alarm.assert_called_once() + assert scenario.cloudwatch_client.put_metric_data.call_count == 2 + + +def test_run_scenario_handles_errors(scenario): + """Test scenario handles errors gracefully.""" + scenario.sns_resource.create_topic.side_effect = ClientError( + {"Error": {"Code": "InvalidParameter", "Message": "Invalid topic name"}}, + "CreateTopic" + ) + + with patch("scenario_cloudwatch_sns_monitoring.q"): + with patch("builtins.input", return_value=""): + scenario.run_scenario() + + # Cleanup should still be attempted + assert True # Scenario completes without crashing diff --git a/scenarios/features/cloudwatch_sns_monitoring/README.md b/scenarios/features/cloudwatch_sns_monitoring/README.md new file mode 100644 index 00000000000..7c9267b904a --- /dev/null +++ b/scenarios/features/cloudwatch_sns_monitoring/README.md @@ -0,0 +1,44 @@ +# CloudWatch Monitoring with SNS Alerts + +## Overview + +This scenario demonstrates how to use Amazon CloudWatch to monitor infrastructure metrics and send alerts via Amazon SNS when thresholds are breached. This is one of the most common real-world use cases for SNS in production environments. + +## What You'll Learn + +- Create SNS topics and email subscriptions for notifications +- Create CloudWatch alarms that monitor custom metrics +- Publish custom metric data to CloudWatch +- Trigger alarms and receive email notifications +- Create CloudWatch dashboards for visualization +- Retrieve alarm history and state changes + +## Scenario Flow + +1. **Setup**: Create SNS topic, subscribe email, create CloudWatch alarm and dashboard +2. **Monitoring**: Publish metric data and trigger alarm state changes +3. **Notifications**: Receive email alerts when thresholds are breached +4. **Cleanup**: Delete all created resources + +## Prerequisites + +- AWS credentials configured +- Valid email address for receiving notifications +- Permissions for CloudWatch, SNS, and IAM operations + +## Services Used + +- Amazon CloudWatch - Monitoring and alarms +- Amazon SNS - Notification delivery + +## Running the Scenario + +Follow the prompts to: +- Provide an email address for notifications +- Name your CloudWatch alarm +- Specify a custom metric namespace +- Publish metric data to trigger alarms +- View alarm state changes and history +- Clean up resources when complete + +**Note**: You must confirm your email subscription to receive alarm notifications. diff --git a/scenarios/features/cloudwatch_sns_monitoring/SPECIFICATION.md b/scenarios/features/cloudwatch_sns_monitoring/SPECIFICATION.md new file mode 100644 index 00000000000..740d1e16ed2 --- /dev/null +++ b/scenarios/features/cloudwatch_sns_monitoring/SPECIFICATION.md @@ -0,0 +1,199 @@ +# CloudWatch Monitoring with SNS Alerts - Technical Specification + +This document contains the technical specifications for _CloudWatch Monitoring with SNS Alerts_, +a feature scenario that showcases AWS services and SDKs. It is primarily intended for the AWS code +examples team to use while developing this example in additional languages. + +This document explains the following: + +- Architecture and features of the example scenario. +- Sample reference output. +- Suggested error handling. +- Metadata information for the scenario. + +For an introduction, see the [README.md](README.md). + +--- + +### Table of contents + +- [Resources and User Input](#resources-and-user-input) +- [Errors](#errors) +- [Metadata](#metadata) + +## Resources and User Input + +This scenario demonstrates a common real-world use case: using CloudWatch to monitor infrastructure metrics and send alerts via SNS when thresholds are breached. + +### Phase 1: Setup Resources + +The scenario creates the following resources: + +- An Amazon SNS topic for receiving alarm notifications +- An email subscription to the SNS topic (user must confirm) +- A CloudWatch alarm that monitors a custom metric +- A CloudWatch dashboard to visualize the metric + +**User Prompts**: + +``` +Enter an email address to receive alarm notifications: +``` + +The user provides an email address for SNS notifications. The scenario creates an SNS topic and subscribes the email. + +``` +Enter a name for the CloudWatch alarm: +``` + +Alarm names must be unique within the AWS account and region. Valid characters: ASCII characters only. + +``` +Enter a name for the custom metric namespace (default: CustomApp/Monitoring): +``` + +Namespace for the custom metric. Defaults to "CustomApp/Monitoring" if not provided. + +**Example Output**: +``` +-------------------------------------------------------------------------------- +Welcome to the CloudWatch Monitoring with SNS Alerts Scenario. +-------------------------------------------------------------------------------- +This scenario demonstrates how to: +1. Create an SNS topic for alarm notifications +2. Create a CloudWatch alarm that monitors a custom metric +3. Publish metric data to trigger the alarm +4. Receive email notifications when the alarm state changes + +Enter an email address to receive alarm notifications: +user@example.com + +Creating SNS topic... +SNS topic created: arn:aws:sns:us-east-1:123456789012:cloudwatch-alarms-topic +Email subscription created. Please check your email and confirm the subscription. + +Enter a name for the CloudWatch alarm: +HighErrorRateAlarm + +Enter a name for the custom metric namespace (default: CustomApp/Monitoring): +[Enter for default] + +Creating CloudWatch alarm 'HighErrorRateAlarm'... +Alarm will trigger when ErrorCount >= 10 for 1 evaluation period (1 minute). +CloudWatch alarm created successfully. + +Creating CloudWatch dashboard... +Dashboard 'monitoring-dashboard' created successfully. +View at: https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#dashboards:name=monitoring-dashboard +-------------------------------------------------------------------------------- +``` + +### Phase 2: Publish Metric Data + +The scenario publishes custom metric data to CloudWatch to demonstrate alarm triggering. + +**Steps**: +1. Publish normal metric values (below threshold) +2. Display current alarm state +3. Publish metric values that exceed the threshold +4. Wait for alarm state to change to ALARM +5. Display alarm state and notification details + +**Example Output**: +``` +-------------------------------------------------------------------------------- +Publishing normal metric data (ErrorCount = 5)... +Metric data published successfully. + +Checking alarm state... +Current alarm state: OK +Alarm reason: Threshold Crossed: 1 datapoint [5.0] was not greater than or equal to the threshold (10.0). + +Publishing high metric data to trigger alarm (ErrorCount = 15)... +Metric data published successfully. + +Waiting for alarm state to change (this may take up to 1 minute)... +Alarm state changed to: ALARM +Alarm reason: Threshold Crossed: 1 datapoint [15.0] was greater than or equal to the threshold (10.0). + +An email notification has been sent to user@example.com +Check your email for the alarm notification. +-------------------------------------------------------------------------------- +``` + +### Phase 3: Demonstrate Alarm Actions + +The scenario retrieves and displays alarm history to show state transitions. + +**Example Output**: +``` +-------------------------------------------------------------------------------- +Retrieving alarm history... + +Recent alarm state changes: +1. 2026-02-25T13:45:00Z: State changed from OK to ALARM + Summary: Threshold Crossed: 1 datapoint [15.0] was greater than or equal to the threshold (10.0). + +2. 2026-02-25T13:40:00Z: State changed from INSUFFICIENT_DATA to OK + Summary: Threshold Crossed: 1 datapoint [5.0] was not greater than or equal to the threshold (10.0). +-------------------------------------------------------------------------------- +``` + +### Phase 4: Cleanup + +The scenario prompts the user to delete all created resources. + +**User Prompts**: +``` +Delete all resources created by this scenario? (y/n) +``` + +**Example Output**: +``` +Deleting CloudWatch dashboard... +Dashboard deleted successfully. + +Deleting CloudWatch alarm... +Alarm deleted successfully. + +Unsubscribing from SNS topic... +Subscription removed. + +Deleting SNS topic... +SNS topic deleted successfully. + +All resources cleaned up successfully. +-------------------------------------------------------------------------------- +CloudWatch Monitoring with SNS Alerts scenario completed. +``` + +--- + +## Errors + +| Action | Error | Handling | +|--------|-------|----------| +| `CreateTopic` | InvalidParameter | Notify user of invalid topic name format | +| `Subscribe` | InvalidParameter | Notify user of invalid email format | +| `PutMetricAlarm` | LimitExceededException | Notify user they've reached alarm limit | +| `PutMetricData` | InvalidParameterValue | Notify user of invalid metric data format | +| `DescribeAlarms` | ResourceNotFoundException | Notify user the alarm doesn't exist | + +--- + +## Metadata + +| action / scenario | metadata file | metadata key | +|-------------------|---------------|--------------| +| `CreateTopic` | sns_metadata.yaml | sns_CreateTopic | +| `Subscribe` | sns_metadata.yaml | sns_Subscribe | +| `Unsubscribe` | sns_metadata.yaml | sns_Unsubscribe | +| `DeleteTopic` | sns_metadata.yaml | sns_DeleteTopic | +| `PutMetricAlarm` | cloudwatch_metadata.yaml | cloudwatch_PutMetricAlarm | +| `PutMetricData` | cloudwatch_metadata.yaml | cloudwatch_PutMetricData | +| `DescribeAlarms` | cloudwatch_metadata.yaml | cloudwatch_DescribeAlarms | +| `DescribeAlarmHistory` | cloudwatch_metadata.yaml | cloudwatch_DescribeAlarmHistory | +| `DeleteAlarms` | cloudwatch_metadata.yaml | cloudwatch_DeleteAlarms | +| `PutDashboard` | cloudwatch_metadata.yaml | cloudwatch_PutDashboard | +| `DeleteDashboards` | cloudwatch_metadata.yaml | cloudwatch_DeleteDashboards | +| `CloudWatch SNS Monitoring Scenario` | cloudwatch_metadata.yaml | cloudwatch_Scenario_SnsMonitoring |