CloudWatch examples using Amazon SDK for .NET - Amazon SDK for .NET
Services or capabilities described in Amazon Web Services documentation might vary by Region. To see the differences applicable to the China Regions, see Getting Started with Amazon Web Services in China (PDF).

CloudWatch examples using Amazon SDK for .NET

The following code examples show you how to perform actions and implement common scenarios by using the Amazon SDK for .NET with CloudWatch.

Actions are code excerpts from larger programs and must be run in context. While actions show you how to call individual service functions, you can see actions in context in their related scenarios and cross-service examples.

Scenarios are code examples that show you how to accomplish a specific task by calling multiple functions within the same service.

Each example includes a link to GitHub, where you can find instructions on how to set up and run the code in context.

Get started

The following code examples show how to get started using CloudWatch.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

using Amazon.CloudWatch; using Amazon.CloudWatch.Model; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace CloudWatchActions; public static class HelloCloudWatch { static async Task Main(string[] args) { // Use the AWS .NET Core Setup package to set up dependency injection for the Amazon CloudWatch service. // Use your AWS profile name, or leave it blank to use the default profile. using var host = Host.CreateDefaultBuilder(args) .ConfigureServices((_, services) => services.AddAWSService<IAmazonCloudWatch>() ).Build(); // Now the client is available for injection. var cloudWatchClient = host.Services.GetRequiredService<IAmazonCloudWatch>(); // You can use await and any of the async methods to get a response. var metricNamespace = "AWS/Billing"; var response = await cloudWatchClient.ListMetricsAsync(new ListMetricsRequest { Namespace = metricNamespace }); Console.WriteLine($"Hello Amazon CloudWatch! Following are some metrics available in the {metricNamespace} namespace:"); Console.WriteLine(); foreach (var metric in response.Metrics.Take(5)) { Console.WriteLine($"\tMetric: {metric.MetricName}"); Console.WriteLine($"\tNamespace: {metric.Namespace}"); Console.WriteLine($"\tDimensions: {string.Join(", ", metric.Dimensions.Select(m => $"{m.Name}:{m.Value}"))}"); Console.WriteLine(); } } }
  • For API details, see ListMetrics in Amazon SDK for .NET API Reference.

Actions

The following code example shows how to use DeleteAlarms.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Delete a list of alarms from CloudWatch. /// </summary> /// <param name="alarmNames">A list of names of alarms to delete.</param> /// <returns>True if successful.</returns> public async Task<bool> DeleteAlarms(List<string> alarmNames) { var deleteAlarmsResult = await _amazonCloudWatch.DeleteAlarmsAsync( new DeleteAlarmsRequest() { AlarmNames = alarmNames }); return deleteAlarmsResult.HttpStatusCode == HttpStatusCode.OK; }
  • For API details, see DeleteAlarms in Amazon SDK for .NET API Reference.

The following code example shows how to use DeleteAnomalyDetector.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Delete a single metric anomaly detector. /// </summary> /// <param name="anomalyDetector">The anomaly detector to delete.</param> /// <returns>True if successful.</returns> public async Task<bool> DeleteAnomalyDetector(SingleMetricAnomalyDetector anomalyDetector) { var deleteAnomalyDetectorResponse = await _amazonCloudWatch.DeleteAnomalyDetectorAsync( new DeleteAnomalyDetectorRequest() { SingleMetricAnomalyDetector = anomalyDetector }); return deleteAnomalyDetectorResponse.HttpStatusCode == HttpStatusCode.OK; }

The following code example shows how to use DeleteDashboards.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Delete a list of CloudWatch dashboards. /// </summary> /// <param name="dashboardNames">List of dashboard names to delete.</param> /// <returns>True if successful.</returns> public async Task<bool> DeleteDashboards(List<string> dashboardNames) { var deleteDashboardsResponse = await _amazonCloudWatch.DeleteDashboardsAsync( new DeleteDashboardsRequest() { DashboardNames = dashboardNames }); return deleteDashboardsResponse.HttpStatusCode == HttpStatusCode.OK; }

The following code example shows how to use DescribeAlarmHistory.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Describe the history of an alarm for a number of days in the past. /// </summary> /// <param name="alarmName">The name of the alarm.</param> /// <param name="historyDays">The number of days in the past.</param> /// <returns>The list of alarm history data.</returns> public async Task<List<AlarmHistoryItem>> DescribeAlarmHistory(string alarmName, int historyDays) { List<AlarmHistoryItem> alarmHistory = new List<AlarmHistoryItem>(); var paginatedAlarmHistory = _amazonCloudWatch.Paginators.DescribeAlarmHistory( new DescribeAlarmHistoryRequest() { AlarmName = alarmName, EndDateUtc = DateTime.UtcNow, HistoryItemType = HistoryItemType.StateUpdate, StartDateUtc = DateTime.UtcNow.AddDays(-historyDays) }); await foreach (var data in paginatedAlarmHistory.AlarmHistoryItems) { alarmHistory.Add(data); } return alarmHistory; }

The following code example shows how to use DescribeAlarms.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Describe the current alarms, optionally filtered by state. /// </summary> /// <param name="stateValue">Optional filter for alarm state.</param> /// <returns>The list of alarm data.</returns> public async Task<List<MetricAlarm>> DescribeAlarms(StateValue? stateValue = null) { List<MetricAlarm> alarms = new List<MetricAlarm>(); var paginatedDescribeAlarms = _amazonCloudWatch.Paginators.DescribeAlarms( new DescribeAlarmsRequest() { StateValue = stateValue }); await foreach (var data in paginatedDescribeAlarms.MetricAlarms) { alarms.Add(data); } return alarms; }
  • For API details, see DescribeAlarms in Amazon SDK for .NET API Reference.

The following code example shows how to use DescribeAlarmsForMetric.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Describe the current alarms for a specific metric. /// </summary> /// <param name="metricNamespace">The namespace of the metric.</param> /// <param name="metricName">The name of the metric.</param> /// <returns>The list of alarm data.</returns> public async Task<List<MetricAlarm>> DescribeAlarmsForMetric(string metricNamespace, string metricName) { var alarmsResult = await _amazonCloudWatch.DescribeAlarmsForMetricAsync( new DescribeAlarmsForMetricRequest() { Namespace = metricNamespace, MetricName = metricName }); return alarmsResult.MetricAlarms; }

The following code example shows how to use DescribeAnomalyDetectors.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Describe anomaly detectors for a metric and namespace. /// </summary> /// <param name="metricNamespace">The namespace of the metric.</param> /// <param name="metricName">The metric of the anomaly detectors.</param> /// <returns>The list of detectors.</returns> public async Task<List<AnomalyDetector>> DescribeAnomalyDetectors(string metricNamespace, string metricName) { List<AnomalyDetector> detectors = new List<AnomalyDetector>(); var paginatedDescribeAnomalyDetectors = _amazonCloudWatch.Paginators.DescribeAnomalyDetectors( new DescribeAnomalyDetectorsRequest() { MetricName = metricName, Namespace = metricNamespace }); await foreach (var data in paginatedDescribeAnomalyDetectors.AnomalyDetectors) { detectors.Add(data); } return detectors; }

The following code example shows how to use DisableAlarmActions.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Disable the actions for a list of alarms from CloudWatch. /// </summary> /// <param name="alarmNames">A list of names of alarms.</param> /// <returns>True if successful.</returns> public async Task<bool> DisableAlarmActions(List<string> alarmNames) { var disableAlarmActionsResult = await _amazonCloudWatch.DisableAlarmActionsAsync( new DisableAlarmActionsRequest() { AlarmNames = alarmNames }); return disableAlarmActionsResult.HttpStatusCode == HttpStatusCode.OK; }

The following code example shows how to use EnableAlarmActions.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Enable the actions for a list of alarms from CloudWatch. /// </summary> /// <param name="alarmNames">A list of names of alarms.</param> /// <returns>True if successful.</returns> public async Task<bool> EnableAlarmActions(List<string> alarmNames) { var enableAlarmActionsResult = await _amazonCloudWatch.EnableAlarmActionsAsync( new EnableAlarmActionsRequest() { AlarmNames = alarmNames }); return enableAlarmActionsResult.HttpStatusCode == HttpStatusCode.OK; }

The following code example shows how to use GetDashboard.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Get information on a dashboard. /// </summary> /// <param name="dashboardName">The name of the dashboard.</param> /// <returns>A JSON object with dashboard information.</returns> public async Task<string> GetDashboard(string dashboardName) { var dashboardResponse = await _amazonCloudWatch.GetDashboardAsync( new GetDashboardRequest() { DashboardName = dashboardName }); return dashboardResponse.DashboardBody; }
  • For API details, see GetDashboard in Amazon SDK for .NET API Reference.

The following code example shows how to use GetMetricData.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Get data for CloudWatch metrics. /// </summary> /// <param name="minutesOfData">The number of minutes of data to include.</param> /// <param name="useDescendingTime">True to return the data descending by time.</param> /// <param name="endDateUtc">The end date for the data, in UTC.</param> /// <param name="maxDataPoints">The maximum data points to include.</param> /// <param name="dataQueries">Optional data queries to include.</param> /// <returns>A list of the requested metric data.</returns> public async Task<List<MetricDataResult>> GetMetricData(int minutesOfData, bool useDescendingTime, DateTime? endDateUtc = null, int maxDataPoints = 0, List<MetricDataQuery>? dataQueries = null) { var metricData = new List<MetricDataResult>(); // If no end time is provided, use the current time for the end time. endDateUtc ??= DateTime.UtcNow; var timeZoneOffset = TimeZoneInfo.Local.GetUtcOffset(endDateUtc.Value.ToLocalTime()); var startTimeUtc = endDateUtc.Value.AddMinutes(-minutesOfData); // The timezone string should be in the format +0000, so use the timezone offset to format it correctly. var timeZoneString = $"{timeZoneOffset.Hours:D2}{timeZoneOffset.Minutes:D2}"; var paginatedMetricData = _amazonCloudWatch.Paginators.GetMetricData( new GetMetricDataRequest() { StartTimeUtc = startTimeUtc, EndTimeUtc = endDateUtc.Value, LabelOptions = new LabelOptions { Timezone = timeZoneString }, ScanBy = useDescendingTime ? ScanBy.TimestampDescending : ScanBy.TimestampAscending, MaxDatapoints = maxDataPoints, MetricDataQueries = dataQueries, }); await foreach (var data in paginatedMetricData.MetricDataResults) { metricData.Add(data); } return metricData; }
  • For API details, see GetMetricData in Amazon SDK for .NET API Reference.

The following code example shows how to use GetMetricStatistics.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Get billing statistics using a call to a wrapper class. /// </summary> /// <returns>A collection of billing statistics.</returns> private static async Task<List<Datapoint>> SetupBillingStatistics() { // Make a request for EstimatedCharges with a period of one day for the past seven days. var billingStatistics = await _cloudWatchWrapper.GetMetricStatistics( "AWS/Billing", "EstimatedCharges", new List<string>() { "Maximum" }, new List<Dimension>() { new Dimension { Name = "Currency", Value = "USD" } }, 7, 86400); billingStatistics = billingStatistics.OrderBy(n => n.Timestamp).ToList(); return billingStatistics; } /// <summary> /// Wrapper to get statistics for a specific CloudWatch metric. /// </summary> /// <param name="metricNamespace">The namespace of the metric.</param> /// <param name="metricName">The name of the metric.</param> /// <param name="statistics">The list of statistics to include.</param> /// <param name="dimensions">The list of dimensions to include.</param> /// <param name="days">The number of days in the past to include.</param> /// <param name="period">The period for the data.</param> /// <returns>A list of DataPoint objects for the statistics.</returns> public async Task<List<Datapoint>> GetMetricStatistics(string metricNamespace, string metricName, List<string> statistics, List<Dimension> dimensions, int days, int period) { var metricStatistics = await _amazonCloudWatch.GetMetricStatisticsAsync( new GetMetricStatisticsRequest() { Namespace = metricNamespace, MetricName = metricName, Dimensions = dimensions, Statistics = statistics, StartTimeUtc = DateTime.UtcNow.AddDays(-days), EndTimeUtc = DateTime.UtcNow, Period = period }); return metricStatistics.Datapoints; }

The following code example shows how to use GetMetricWidgetImage.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Get an image for a metric graphed over time. /// </summary> /// <param name="metricNamespace">The namespace of the metric.</param> /// <param name="metric">The name of the metric.</param> /// <param name="stat">The name of the stat to chart.</param> /// <param name="period">The period to use for the chart.</param> /// <returns>A memory stream for the chart image.</returns> public async Task<MemoryStream> GetTimeSeriesMetricImage(string metricNamespace, string metric, string stat, int period) { var metricImageWidget = new { title = "Example Metric Graph", view = "timeSeries", stacked = false, period = period, width = 1400, height = 600, metrics = new List<List<object>> { new() { metricNamespace, metric, new { stat } } } }; var metricImageWidgetString = JsonSerializer.Serialize(metricImageWidget); var imageResponse = await _amazonCloudWatch.GetMetricWidgetImageAsync( new GetMetricWidgetImageRequest() { MetricWidget = metricImageWidgetString }); return imageResponse.MetricWidgetImage; } /// <summary> /// Save a metric image to a file. /// </summary> /// <param name="memoryStream">The MemoryStream for the metric image.</param> /// <param name="metricName">The name of the metric.</param> /// <returns>The path to the file.</returns> public string SaveMetricImage(MemoryStream memoryStream, string metricName) { var metricFileName = $"{metricName}_{DateTime.Now.Ticks}.png"; using var sr = new StreamReader(memoryStream); // Writes the memory stream to a file. File.WriteAllBytes(metricFileName, memoryStream.ToArray()); var filePath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, metricFileName); return filePath; }

The following code example shows how to use ListDashboards.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Get a list of dashboards. /// </summary> /// <returns>A list of DashboardEntry objects.</returns> public async Task<List<DashboardEntry>> ListDashboards() { var results = new List<DashboardEntry>(); var paginateDashboards = _amazonCloudWatch.Paginators.ListDashboards( new ListDashboardsRequest()); // Get the entire list using the paginator. await foreach (var data in paginateDashboards.DashboardEntries) { results.Add(data); } return results; }
  • For API details, see ListDashboards in Amazon SDK for .NET API Reference.

The following code example shows how to use ListMetrics.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// List metrics available, optionally within a namespace. /// </summary> /// <param name="metricNamespace">Optional CloudWatch namespace to use when listing metrics.</param> /// <param name="filter">Optional dimension filter.</param> /// <param name="metricName">Optional metric name filter.</param> /// <returns>The list of metrics.</returns> public async Task<List<Metric>> ListMetrics(string? metricNamespace = null, DimensionFilter? filter = null, string? metricName = null) { var results = new List<Metric>(); var paginateMetrics = _amazonCloudWatch.Paginators.ListMetrics( new ListMetricsRequest { Namespace = metricNamespace, Dimensions = filter != null ? new List<DimensionFilter> { filter } : null, MetricName = metricName }); // Get the entire list using the paginator. await foreach (var metric in paginateMetrics.Metrics) { results.Add(metric); } return results; }
  • For API details, see ListMetrics in Amazon SDK for .NET API Reference.

The following code example shows how to use PutAnomalyDetector.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Add an anomaly detector for a single metric. /// </summary> /// <param name="anomalyDetector">A single metric anomaly detector.</param> /// <returns>True if successful.</returns> public async Task<bool> PutAnomalyDetector(SingleMetricAnomalyDetector anomalyDetector) { var putAlarmDetectorResult = await _amazonCloudWatch.PutAnomalyDetectorAsync( new PutAnomalyDetectorRequest() { SingleMetricAnomalyDetector = anomalyDetector }); return putAlarmDetectorResult.HttpStatusCode == HttpStatusCode.OK; }

The following code example shows how to use PutDashboard.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Set up a dashboard using a call to the wrapper class. /// </summary> /// <param name="customMetricNamespace">The metric namespace.</param> /// <param name="customMetricName">The metric name.</param> /// <param name="dashboardName">The name of the dashboard.</param> /// <returns>A list of validation messages.</returns> private static async Task<List<DashboardValidationMessage>> SetupDashboard( string customMetricNamespace, string customMetricName, string dashboardName) { // Get the dashboard model from configuration. var newDashboard = new DashboardModel(); _configuration.GetSection("dashboardExampleBody").Bind(newDashboard); // Add a new metric to the dashboard. newDashboard.Widgets.Add(new Widget { Height = 8, Width = 8, Y = 8, X = 0, Type = "metric", Properties = new Properties { Metrics = new List<List<object>> { new() { customMetricNamespace, customMetricName } }, View = "timeSeries", Region = "us-east-1", Stat = "Sum", Period = 86400, YAxis = new YAxis { Left = new Left { Min = 0, Max = 100 } }, Title = "Custom Metric Widget", LiveData = true, Sparkline = true, Trend = true, Stacked = false, SetPeriodToTimeRange = false } }); var newDashboardString = JsonSerializer.Serialize(newDashboard, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }); var validationMessages = await _cloudWatchWrapper.PutDashboard(dashboardName, newDashboardString); return validationMessages; } /// <summary> /// Wrapper to create or add to a dashboard with metrics. /// </summary> /// <param name="dashboardName">The name for the dashboard.</param> /// <param name="dashboardBody">The metric data in JSON for the dashboard.</param> /// <returns>A list of validation messages for the dashboard.</returns> public async Task<List<DashboardValidationMessage>> PutDashboard(string dashboardName, string dashboardBody) { // Updating a dashboard replaces all contents. // Best practice is to include a text widget indicating this dashboard was created programmatically. var dashboardResponse = await _amazonCloudWatch.PutDashboardAsync( new PutDashboardRequest() { DashboardName = dashboardName, DashboardBody = dashboardBody }); return dashboardResponse.DashboardValidationMessages; }
  • For API details, see PutDashboard in Amazon SDK for .NET API Reference.

The following code example shows how to use PutMetricAlarm.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Add a metric alarm to send an email when the metric passes a threshold. /// </summary> /// <param name="alarmDescription">A description of the alarm.</param> /// <param name="alarmName">The name for the alarm.</param> /// <param name="comparison">The type of comparison to use.</param> /// <param name="metricName">The name of the metric for the alarm.</param> /// <param name="metricNamespace">The namespace of the metric.</param> /// <param name="threshold">The threshold value for the alarm.</param> /// <param name="alarmActions">Optional actions to execute when in an alarm state.</param> /// <returns>True if successful.</returns> public async Task<bool> PutMetricEmailAlarm(string alarmDescription, string alarmName, ComparisonOperator comparison, string metricName, string metricNamespace, double threshold, List<string> alarmActions = null!) { try { var putEmailAlarmResponse = await _amazonCloudWatch.PutMetricAlarmAsync( new PutMetricAlarmRequest() { AlarmActions = alarmActions, AlarmDescription = alarmDescription, AlarmName = alarmName, ComparisonOperator = comparison, Threshold = threshold, Namespace = metricNamespace, MetricName = metricName, EvaluationPeriods = 1, Period = 10, Statistic = new Statistic("Maximum"), DatapointsToAlarm = 1, TreatMissingData = "ignore" }); return putEmailAlarmResponse.HttpStatusCode == HttpStatusCode.OK; } catch (LimitExceededException lex) { _logger.LogError(lex, $"Unable to add alarm {alarmName}. Alarm quota has already been reached."); } return false; } /// <summary> /// Add specific email actions to a list of action strings for a CloudWatch alarm. /// </summary> /// <param name="accountId">The AccountId for the alarm.</param> /// <param name="region">The region for the alarm.</param> /// <param name="emailTopicName">An Amazon Simple Notification Service (SNS) topic for the alarm email.</param> /// <param name="alarmActions">Optional list of existing alarm actions to append to.</param> /// <returns>A list of string actions for an alarm.</returns> public List<string> AddEmailAlarmAction(string accountId, string region, string emailTopicName, List<string>? alarmActions = null) { alarmActions ??= new List<string>(); var snsAlarmAction = $"arn:aws:sns:{region}:{accountId}:{emailTopicName}"; alarmActions.Add(snsAlarmAction); return alarmActions; }
  • For API details, see PutMetricAlarm in Amazon SDK for .NET API Reference.

The following code example shows how to use PutMetricData.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

/// <summary> /// Add some metric data using a call to a wrapper class. /// </summary> /// <param name="customMetricName">The metric name.</param> /// <param name="customMetricNamespace">The metric namespace.</param> /// <returns></returns> private static async Task<List<MetricDatum>> PutRandomMetricData(string customMetricName, string customMetricNamespace) { List<MetricDatum> customData = new List<MetricDatum>(); Random rnd = new Random(); // Add 10 random values up to 100, starting with a timestamp 15 minutes in the past. var utcNowMinus15 = DateTime.UtcNow.AddMinutes(-15); for (int i = 0; i < 10; i++) { var metricValue = rnd.Next(0, 100); customData.Add( new MetricDatum { MetricName = customMetricName, Value = metricValue, TimestampUtc = utcNowMinus15.AddMinutes(i) } ); } await _cloudWatchWrapper.PutMetricData(customMetricNamespace, customData); return customData; } /// <summary> /// Wrapper to add metric data to a CloudWatch metric. /// </summary> /// <param name="metricNamespace">The namespace of the metric.</param> /// <param name="metricData">A data object for the metric data.</param> /// <returns>True if successful.</returns> public async Task<bool> PutMetricData(string metricNamespace, List<MetricDatum> metricData) { var putDataResponse = await _amazonCloudWatch.PutMetricDataAsync( new PutMetricDataRequest() { MetricData = metricData, Namespace = metricNamespace, }); return putDataResponse.HttpStatusCode == HttpStatusCode.OK; }
  • For API details, see PutMetricData in Amazon SDK for .NET API Reference.

Scenarios

The following code example shows how to:

  • List CloudWatch namespaces and metrics.

  • Get statistics for a metric and for estimated billing.

  • Create and update a dashboard.

  • Create and add data to a metric.

  • Create and trigger an alarm, then view alarm history.

  • Add an anomaly detector.

  • Get a metric image, then clean up resources.

Amazon SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Amazon Code Examples Repository.

Run an interactive scenario at a command prompt.

public class CloudWatchScenario { /* Before running this .NET code example, set up your development environment, including your credentials. To enable billing metrics and statistics for this example, make sure billing alerts are enabled for your account: https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/monitor_estimated_charges_with_cloudwatch.html#turning_on_billing_metrics This .NET example performs the following tasks: 1. List and select a CloudWatch namespace. 2. List and select a CloudWatch metric. 3. Get statistics for a CloudWatch metric. 4. Get estimated billing statistics for the last week. 5. Create a new CloudWatch dashboard with two metrics. 6. List current CloudWatch dashboards. 7. Create a CloudWatch custom metric and add metric data. 8. Add the custom metric to the dashboard. 9. Create a CloudWatch alarm for the custom metric. 10. Describe current CloudWatch alarms. 11. Get recent data for the custom metric. 12. Add data to the custom metric to trigger the alarm. 13. Wait for an alarm state. 14. Get history for the CloudWatch alarm. 15. Add an anomaly detector. 16. Describe current anomaly detectors. 17. Get and display a metric image. 18. Clean up resources. */ private static ILogger logger = null!; private static CloudWatchWrapper _cloudWatchWrapper = null!; private static IConfiguration _configuration = null!; private static readonly List<string> _statTypes = new List<string> { "SampleCount", "Average", "Sum", "Minimum", "Maximum" }; private static SingleMetricAnomalyDetector? anomalyDetector = null!; static async Task Main(string[] args) { // Set up dependency injection for the Amazon service. using var host = Host.CreateDefaultBuilder(args) .ConfigureLogging(logging => logging.AddFilter("System", LogLevel.Debug) .AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Information) .AddFilter<ConsoleLoggerProvider>("Microsoft", LogLevel.Trace)) .ConfigureServices((_, services) => services.AddAWSService<IAmazonCloudWatch>() .AddTransient<CloudWatchWrapper>() ) .Build(); _configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("settings.json") // Load settings from .json file. .AddJsonFile("settings.local.json", true) // Optionally, load local settings. .Build(); logger = LoggerFactory.Create(builder => { builder.AddConsole(); }) .CreateLogger<CloudWatchScenario>(); _cloudWatchWrapper = host.Services.GetRequiredService<CloudWatchWrapper>(); Console.WriteLine(new string('-', 80)); Console.WriteLine("Welcome to the Amazon CloudWatch example scenario."); Console.WriteLine(new string('-', 80)); try { var selectedNamespace = await SelectNamespace(); var selectedMetric = await SelectMetric(selectedNamespace); await GetAndDisplayMetricStatistics(selectedNamespace, selectedMetric); await GetAndDisplayEstimatedBilling(); await CreateDashboardWithMetrics(); await ListDashboards(); await CreateNewCustomMetric(); await AddMetricToDashboard(); await CreateMetricAlarm(); await DescribeAlarms(); await GetCustomMetricData(); await AddMetricDataForAlarm(); await CheckForMetricAlarm(); await GetAlarmHistory(); anomalyDetector = await AddAnomalyDetector(); await DescribeAnomalyDetectors(); await GetAndOpenMetricImage(); await CleanupResources(); } catch (Exception ex) { logger.LogError(ex, "There was a problem executing the scenario."); await CleanupResources(); } } /// <summary> /// Select a namespace. /// </summary> /// <returns>The selected namespace.</returns> private static async Task<string> SelectNamespace() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"1. Select a CloudWatch Namespace from a list of Namespaces."); var metrics = await _cloudWatchWrapper.ListMetrics(); // Get a distinct list of namespaces. var namespaces = metrics.Select(m => m.Namespace).Distinct().ToList(); for (int i = 0; i < namespaces.Count; i++) { Console.WriteLine($"\t{i + 1}. {namespaces[i]}"); } var namespaceChoiceNumber = 0; while (namespaceChoiceNumber < 1 || namespaceChoiceNumber > namespaces.Count) { Console.WriteLine( "Select a namespace by entering a number from the preceding list:"); var choice = Console.ReadLine(); Int32.TryParse(choice, out namespaceChoiceNumber); } var selectedNamespace = namespaces[namespaceChoiceNumber - 1]; Console.WriteLine(new string('-', 80)); return selectedNamespace; } /// <summary> /// Select a metric from a namespace. /// </summary> /// <param name="metricNamespace">The namespace for metrics.</param> /// <returns>The metric name.</returns> private static async Task<Metric> SelectMetric(string metricNamespace) { Console.WriteLine(new string('-', 80)); Console.WriteLine($"2. Select a CloudWatch metric from a namespace."); var namespaceMetrics = await _cloudWatchWrapper.ListMetrics(metricNamespace); for (int i = 0; i < namespaceMetrics.Count && i < 15; i++) { var dimensionsWithValues = namespaceMetrics[i].Dimensions .Where(d => !string.Equals("None", d.Value)); Console.WriteLine($"\t{i + 1}. {namespaceMetrics[i].MetricName} " + $"{string.Join(", :", dimensionsWithValues.Select(d => d.Value))}"); } var metricChoiceNumber = 0; while (metricChoiceNumber < 1 || metricChoiceNumber > namespaceMetrics.Count) { Console.WriteLine( "Select a metric by entering a number from the preceding list:"); var choice = Console.ReadLine(); Int32.TryParse(choice, out metricChoiceNumber); } var selectedMetric = namespaceMetrics[metricChoiceNumber - 1]; Console.WriteLine(new string('-', 80)); return selectedMetric; } /// <summary> /// Get and display metric statistics for a specific metric. /// </summary> /// <param name="metricNamespace">The namespace for metrics.</param> /// <param name="metric">The CloudWatch metric.</param> /// <returns>Async task.</returns> private static async Task GetAndDisplayMetricStatistics(string metricNamespace, Metric metric) { Console.WriteLine(new string('-', 80)); Console.WriteLine($"3. Get CloudWatch metric statistics for the last day."); for (int i = 0; i < _statTypes.Count; i++) { Console.WriteLine($"\t{i + 1}. {_statTypes[i]}"); } var statisticChoiceNumber = 0; while (statisticChoiceNumber < 1 || statisticChoiceNumber > _statTypes.Count) { Console.WriteLine( "Select a metric statistic by entering a number from the preceding list:"); var choice = Console.ReadLine(); Int32.TryParse(choice, out statisticChoiceNumber); } var selectedStatistic = _statTypes[statisticChoiceNumber - 1]; var statisticsList = new List<string> { selectedStatistic }; var metricStatistics = await _cloudWatchWrapper.GetMetricStatistics(metricNamespace, metric.MetricName, statisticsList, metric.Dimensions, 1, 60); if (!metricStatistics.Any()) { Console.WriteLine($"No {selectedStatistic} statistics found for {metric} in namespace {metricNamespace}."); } metricStatistics = metricStatistics.OrderBy(s => s.Timestamp).ToList(); for (int i = 0; i < metricStatistics.Count && i < 10; i++) { var metricStat = metricStatistics[i]; var statValue = metricStat.GetType().GetProperty(selectedStatistic)!.GetValue(metricStat, null); Console.WriteLine($"\t{i + 1}. Timestamp {metricStatistics[i].Timestamp:G} {selectedStatistic}: {statValue}"); } Console.WriteLine(new string('-', 80)); } /// <summary> /// Get and display estimated billing statistics. /// </summary> /// <param name="metricNamespace">The namespace for metrics.</param> /// <param name="metric">The CloudWatch metric.</param> /// <returns>Async task.</returns> private static async Task GetAndDisplayEstimatedBilling() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"4. Get CloudWatch estimated billing for the last week."); var billingStatistics = await SetupBillingStatistics(); for (int i = 0; i < billingStatistics.Count; i++) { Console.WriteLine($"\t{i + 1}. Timestamp {billingStatistics[i].Timestamp:G} : {billingStatistics[i].Maximum}"); } Console.WriteLine(new string('-', 80)); } /// <summary> /// Get billing statistics using a call to a wrapper class. /// </summary> /// <returns>A collection of billing statistics.</returns> private static async Task<List<Datapoint>> SetupBillingStatistics() { // Make a request for EstimatedCharges with a period of one day for the past seven days. var billingStatistics = await _cloudWatchWrapper.GetMetricStatistics( "AWS/Billing", "EstimatedCharges", new List<string>() { "Maximum" }, new List<Dimension>() { new Dimension { Name = "Currency", Value = "USD" } }, 7, 86400); billingStatistics = billingStatistics.OrderBy(n => n.Timestamp).ToList(); return billingStatistics; } /// <summary> /// Create a dashboard with metrics. /// </summary> /// <param name="metricNamespace">The namespace for metrics.</param> /// <param name="metric">The CloudWatch metric.</param> /// <returns>Async task.</returns> private static async Task CreateDashboardWithMetrics() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"5. Create a new CloudWatch dashboard with metrics."); var dashboardName = _configuration["dashboardName"]; var newDashboard = new DashboardModel(); _configuration.GetSection("dashboardExampleBody").Bind(newDashboard); var newDashboardString = JsonSerializer.Serialize( newDashboard, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }); var validationMessages = await _cloudWatchWrapper.PutDashboard(dashboardName, newDashboardString); Console.WriteLine(validationMessages.Any() ? $"\tValidation messages:" : null); for (int i = 0; i < validationMessages.Count; i++) { Console.WriteLine($"\t{i + 1}. {validationMessages[i].Message}"); } Console.WriteLine($"\tDashboard {dashboardName} was created."); Console.WriteLine(new string('-', 80)); } /// <summary> /// List dashboards. /// </summary> /// <returns>Async task.</returns> private static async Task ListDashboards() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"6. List the CloudWatch dashboards in the current account."); var dashboards = await _cloudWatchWrapper.ListDashboards(); for (int i = 0; i < dashboards.Count; i++) { Console.WriteLine($"\t{i + 1}. {dashboards[i].DashboardName}"); } Console.WriteLine(new string('-', 80)); } /// <summary> /// Create and add data for a new custom metric. /// </summary> /// <returns>Async task.</returns> private static async Task CreateNewCustomMetric() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"7. Create and add data for a new custom metric."); var customMetricNamespace = _configuration["customMetricNamespace"]; var customMetricName = _configuration["customMetricName"]; var customData = await PutRandomMetricData(customMetricName, customMetricNamespace); var valuesString = string.Join(',', customData.Select(d => d.Value)); Console.WriteLine($"\tAdded metric values for for metric {customMetricName}: \n\t{valuesString}"); Console.WriteLine(new string('-', 80)); } /// <summary> /// Add some metric data using a call to a wrapper class. /// </summary> /// <param name="customMetricName">The metric name.</param> /// <param name="customMetricNamespace">The metric namespace.</param> /// <returns></returns> private static async Task<List<MetricDatum>> PutRandomMetricData(string customMetricName, string customMetricNamespace) { List<MetricDatum> customData = new List<MetricDatum>(); Random rnd = new Random(); // Add 10 random values up to 100, starting with a timestamp 15 minutes in the past. var utcNowMinus15 = DateTime.UtcNow.AddMinutes(-15); for (int i = 0; i < 10; i++) { var metricValue = rnd.Next(0, 100); customData.Add( new MetricDatum { MetricName = customMetricName, Value = metricValue, TimestampUtc = utcNowMinus15.AddMinutes(i) } ); } await _cloudWatchWrapper.PutMetricData(customMetricNamespace, customData); return customData; } /// <summary> /// Add the custom metric to the dashboard. /// </summary> /// <returns>Async task.</returns> private static async Task AddMetricToDashboard() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"8. Add the new custom metric to the dashboard."); var dashboardName = _configuration["dashboardName"]; var customMetricNamespace = _configuration["customMetricNamespace"]; var customMetricName = _configuration["customMetricName"]; var validationMessages = await SetupDashboard(customMetricNamespace, customMetricName, dashboardName); Console.WriteLine(validationMessages.Any() ? $"\tValidation messages:" : null); for (int i = 0; i < validationMessages.Count; i++) { Console.WriteLine($"\t{i + 1}. {validationMessages[i].Message}"); } Console.WriteLine($"\tDashboard {dashboardName} updated with metric {customMetricName}."); Console.WriteLine(new string('-', 80)); } /// <summary> /// Set up a dashboard using a call to the wrapper class. /// </summary> /// <param name="customMetricNamespace">The metric namespace.</param> /// <param name="customMetricName">The metric name.</param> /// <param name="dashboardName">The name of the dashboard.</param> /// <returns>A list of validation messages.</returns> private static async Task<List<DashboardValidationMessage>> SetupDashboard( string customMetricNamespace, string customMetricName, string dashboardName) { // Get the dashboard model from configuration. var newDashboard = new DashboardModel(); _configuration.GetSection("dashboardExampleBody").Bind(newDashboard); // Add a new metric to the dashboard. newDashboard.Widgets.Add(new Widget { Height = 8, Width = 8, Y = 8, X = 0, Type = "metric", Properties = new Properties { Metrics = new List<List<object>> { new() { customMetricNamespace, customMetricName } }, View = "timeSeries", Region = "us-east-1", Stat = "Sum", Period = 86400, YAxis = new YAxis { Left = new Left { Min = 0, Max = 100 } }, Title = "Custom Metric Widget", LiveData = true, Sparkline = true, Trend = true, Stacked = false, SetPeriodToTimeRange = false } }); var newDashboardString = JsonSerializer.Serialize(newDashboard, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }); var validationMessages = await _cloudWatchWrapper.PutDashboard(dashboardName, newDashboardString); return validationMessages; } /// <summary> /// Create a CloudWatch alarm for the new metric. /// </summary> /// <returns>Async task.</returns> private static async Task CreateMetricAlarm() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"9. Create a CloudWatch alarm for the new metric."); var customMetricNamespace = _configuration["customMetricNamespace"]; var customMetricName = _configuration["customMetricName"]; var alarmName = _configuration["exampleAlarmName"]; var accountId = _configuration["accountId"]; var region = _configuration["region"]; var emailTopic = _configuration["emailTopic"]; var alarmActions = new List<string>(); if (GetYesNoResponse( $"\tAdd an email action for topic {emailTopic} to alarm {alarmName}? (y/n)")) { _cloudWatchWrapper.AddEmailAlarmAction(accountId, region, emailTopic, alarmActions); } await _cloudWatchWrapper.PutMetricEmailAlarm( "Example metric alarm", alarmName, ComparisonOperator.GreaterThanOrEqualToThreshold, customMetricName, customMetricNamespace, 100, alarmActions); Console.WriteLine($"\tAlarm {alarmName} added for metric {customMetricName}."); Console.WriteLine(new string('-', 80)); } /// <summary> /// Describe Alarms. /// </summary> /// <returns>Async task.</returns> private static async Task DescribeAlarms() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"10. Describe CloudWatch alarms in the current account."); var alarms = await _cloudWatchWrapper.DescribeAlarms(); alarms = alarms.OrderByDescending(a => a.StateUpdatedTimestamp).ToList(); for (int i = 0; i < alarms.Count && i < 10; i++) { var alarm = alarms[i]; Console.WriteLine($"\t{i + 1}. {alarm.AlarmName}"); Console.WriteLine($"\tState: {alarm.StateValue} for {alarm.MetricName} {alarm.ComparisonOperator} {alarm.Threshold}"); } Console.WriteLine(new string('-', 80)); } /// <summary> /// Get the recent data for the metric. /// </summary> /// <returns>Async task.</returns> private static async Task GetCustomMetricData() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"11. Get current data for new custom metric."); var customMetricNamespace = _configuration["customMetricNamespace"]; var customMetricName = _configuration["customMetricName"]; var accountId = _configuration["accountId"]; var query = new List<MetricDataQuery> { new MetricDataQuery { AccountId = accountId, Id = "m1", Label = "Custom Metric Data", MetricStat = new MetricStat { Metric = new Metric { MetricName = customMetricName, Namespace = customMetricNamespace, }, Period = 1, Stat = "Maximum" } } }; var metricData = await _cloudWatchWrapper.GetMetricData( 20, true, DateTime.UtcNow.AddMinutes(1), 20, query); for (int i = 0; i < metricData.Count; i++) { for (int j = 0; j < metricData[i].Values.Count; j++) { Console.WriteLine( $"\tTimestamp {metricData[i].Timestamps[j]:G} Value: {metricData[i].Values[j]}"); } } Console.WriteLine(new string('-', 80)); } /// <summary> /// Add metric data to trigger an alarm. /// </summary> /// <returns>Async task.</returns> private static async Task AddMetricDataForAlarm() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"12. Add metric data to the custom metric to trigger an alarm."); var customMetricNamespace = _configuration["customMetricNamespace"]; var customMetricName = _configuration["customMetricName"]; var nowUtc = DateTime.UtcNow; List<MetricDatum> customData = new List<MetricDatum> { new MetricDatum { MetricName = customMetricName, Value = 101, TimestampUtc = nowUtc.AddMinutes(-2) }, new MetricDatum { MetricName = customMetricName, Value = 101, TimestampUtc = nowUtc.AddMinutes(-1) }, new MetricDatum { MetricName = customMetricName, Value = 101, TimestampUtc = nowUtc } }; var valuesString = string.Join(',', customData.Select(d => d.Value)); Console.WriteLine($"\tAdded metric values for for metric {customMetricName}: \n\t{valuesString}"); await _cloudWatchWrapper.PutMetricData(customMetricNamespace, customData); Console.WriteLine(new string('-', 80)); } /// <summary> /// Check for a metric alarm using the DescribeAlarmsForMetric action. /// </summary> /// <returns>Async task.</returns> private static async Task CheckForMetricAlarm() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"13. Checking for an alarm state."); var customMetricNamespace = _configuration["customMetricNamespace"]; var customMetricName = _configuration["customMetricName"]; var hasAlarm = false; var retries = 10; while (!hasAlarm && retries > 0) { var alarms = await _cloudWatchWrapper.DescribeAlarmsForMetric(customMetricNamespace, customMetricName); hasAlarm = alarms.Any(a => a.StateValue == StateValue.ALARM); retries--; Thread.Sleep(20000); } Console.WriteLine(hasAlarm ? $"\tAlarm state found for {customMetricName}." : $"\tNo Alarm state found for {customMetricName} after 10 retries."); Console.WriteLine(new string('-', 80)); } /// <summary> /// Get history for an alarm. /// </summary> /// <returns>Async task.</returns> private static async Task GetAlarmHistory() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"14. Get alarm history."); var exampleAlarmName = _configuration["exampleAlarmName"]; var alarmHistory = await _cloudWatchWrapper.DescribeAlarmHistory(exampleAlarmName, 2); for (int i = 0; i < alarmHistory.Count; i++) { var history = alarmHistory[i]; Console.WriteLine($"\t{i + 1}. {history.HistorySummary}, time {history.Timestamp:g}"); } if (!alarmHistory.Any()) { Console.WriteLine($"\tNo alarm history data found for {exampleAlarmName}."); } Console.WriteLine(new string('-', 80)); } /// <summary> /// Add an anomaly detector. /// </summary> /// <returns>Async task.</returns> private static async Task<SingleMetricAnomalyDetector> AddAnomalyDetector() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"15. Add an anomaly detector."); var customMetricNamespace = _configuration["customMetricNamespace"]; var customMetricName = _configuration["customMetricName"]; var detector = new SingleMetricAnomalyDetector { MetricName = customMetricName, Namespace = customMetricNamespace, Stat = "Maximum" }; await _cloudWatchWrapper.PutAnomalyDetector(detector); Console.WriteLine($"\tAdded anomaly detector for metric {customMetricName}."); Console.WriteLine(new string('-', 80)); return detector; } /// <summary> /// Describe anomaly detectors. /// </summary> /// <returns>Async task.</returns> private static async Task DescribeAnomalyDetectors() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"16. Describe anomaly detectors in the current account."); var customMetricNamespace = _configuration["customMetricNamespace"]; var customMetricName = _configuration["customMetricName"]; var detectors = await _cloudWatchWrapper.DescribeAnomalyDetectors(customMetricNamespace, customMetricName); for (int i = 0; i < detectors.Count; i++) { var detector = detectors[i]; Console.WriteLine($"\t{i + 1}. {detector.SingleMetricAnomalyDetector.MetricName}, state {detector.StateValue}"); } Console.WriteLine(new string('-', 80)); } /// <summary> /// Fetch and open a metrics image for a CloudWatch metric and namespace. /// </summary> /// <returns>Async task.</returns> private static async Task GetAndOpenMetricImage() { Console.WriteLine(new string('-', 80)); Console.WriteLine("17. Get a metric image from CloudWatch."); Console.WriteLine($"\tGetting Image data for custom metric."); var customMetricNamespace = _configuration["customMetricNamespace"]; var customMetricName = _configuration["customMetricName"]; var memoryStream = await _cloudWatchWrapper.GetTimeSeriesMetricImage(customMetricNamespace, customMetricName, "Maximum", 10); var file = _cloudWatchWrapper.SaveMetricImage(memoryStream, "MetricImages"); ProcessStartInfo info = new ProcessStartInfo(); Console.WriteLine($"\tFile saved as {Path.GetFileName(file)}."); Console.WriteLine($"\tPress enter to open the image."); Console.ReadLine(); info.FileName = Path.Combine("ms-photos://", file); info.UseShellExecute = true; info.CreateNoWindow = true; info.Verb = string.Empty; Process.Start(info); Console.WriteLine(new string('-', 80)); } /// <summary> /// Clean up created resources. /// </summary> /// <param name="metricNamespace">The namespace for metrics.</param> /// <param name="metric">The CloudWatch metric.</param> /// <returns>Async task.</returns> private static async Task CleanupResources() { Console.WriteLine(new string('-', 80)); Console.WriteLine($"18. Clean up resources."); var dashboardName = _configuration["dashboardName"]; if (GetYesNoResponse($"\tDelete dashboard {dashboardName}? (y/n)")) { Console.WriteLine($"\tDeleting dashboard."); var dashboardList = new List<string> { dashboardName }; await _cloudWatchWrapper.DeleteDashboards(dashboardList); } var alarmName = _configuration["exampleAlarmName"]; if (GetYesNoResponse($"\tDelete alarm {alarmName}? (y/n)")) { Console.WriteLine($"\tCleaning up alarms."); var alarms = new List<string> { alarmName }; await _cloudWatchWrapper.DeleteAlarms(alarms); } if (GetYesNoResponse($"\tDelete anomaly detector? (y/n)") && anomalyDetector != null) { Console.WriteLine($"\tCleaning up anomaly detector."); await _cloudWatchWrapper.DeleteAnomalyDetector( anomalyDetector); } Console.WriteLine(new string('-', 80)); } /// <summary> /// Get a yes or no response from the user. /// </summary> /// <param name="question">The question string to print on the console.</param> /// <returns>True if the user responds with a yes.</returns> private static bool GetYesNoResponse(string question) { Console.WriteLine(question); var ynResponse = Console.ReadLine(); var response = ynResponse != null && ynResponse.Equals("y", StringComparison.InvariantCultureIgnoreCase); return response; } }

Wrapper methods used by the scenario for CloudWatch actions.

/// <summary> /// Wrapper class for Amazon CloudWatch methods. /// </summary> public class CloudWatchWrapper { private readonly IAmazonCloudWatch _amazonCloudWatch; private readonly ILogger<CloudWatchWrapper> _logger; /// <summary> /// Constructor for the CloudWatch wrapper. /// </summary> /// <param name="amazonCloudWatch">The injected CloudWatch client.</param> /// <param name="logger">The injected logger for the wrapper.</param> public CloudWatchWrapper(IAmazonCloudWatch amazonCloudWatch, ILogger<CloudWatchWrapper> logger) { _logger = logger; _amazonCloudWatch = amazonCloudWatch; } /// <summary> /// List metrics available, optionally within a namespace. /// </summary> /// <param name="metricNamespace">Optional CloudWatch namespace to use when listing metrics.</param> /// <param name="filter">Optional dimension filter.</param> /// <param name="metricName">Optional metric name filter.</param> /// <returns>The list of metrics.</returns> public async Task<List<Metric>> ListMetrics(string? metricNamespace = null, DimensionFilter? filter = null, string? metricName = null) { var results = new List<Metric>(); var paginateMetrics = _amazonCloudWatch.Paginators.ListMetrics( new ListMetricsRequest { Namespace = metricNamespace, Dimensions = filter != null ? new List<DimensionFilter> { filter } : null, MetricName = metricName }); // Get the entire list using the paginator. await foreach (var metric in paginateMetrics.Metrics) { results.Add(metric); } return results; } /// <summary> /// Wrapper to get statistics for a specific CloudWatch metric. /// </summary> /// <param name="metricNamespace">The namespace of the metric.</param> /// <param name="metricName">The name of the metric.</param> /// <param name="statistics">The list of statistics to include.</param> /// <param name="dimensions">The list of dimensions to include.</param> /// <param name="days">The number of days in the past to include.</param> /// <param name="period">The period for the data.</param> /// <returns>A list of DataPoint objects for the statistics.</returns> public async Task<List<Datapoint>> GetMetricStatistics(string metricNamespace, string metricName, List<string> statistics, List<Dimension> dimensions, int days, int period) { var metricStatistics = await _amazonCloudWatch.GetMetricStatisticsAsync( new GetMetricStatisticsRequest() { Namespace = metricNamespace, MetricName = metricName, Dimensions = dimensions, Statistics = statistics, StartTimeUtc = DateTime.UtcNow.AddDays(-days), EndTimeUtc = DateTime.UtcNow, Period = period }); return metricStatistics.Datapoints; } /// <summary> /// Wrapper to create or add to a dashboard with metrics. /// </summary> /// <param name="dashboardName">The name for the dashboard.</param> /// <param name="dashboardBody">The metric data in JSON for the dashboard.</param> /// <returns>A list of validation messages for the dashboard.</returns> public async Task<List<DashboardValidationMessage>> PutDashboard(string dashboardName, string dashboardBody) { // Updating a dashboard replaces all contents. // Best practice is to include a text widget indicating this dashboard was created programmatically. var dashboardResponse = await _amazonCloudWatch.PutDashboardAsync( new PutDashboardRequest() { DashboardName = dashboardName, DashboardBody = dashboardBody }); return dashboardResponse.DashboardValidationMessages; } /// <summary> /// Get information on a dashboard. /// </summary> /// <param name="dashboardName">The name of the dashboard.</param> /// <returns>A JSON object with dashboard information.</returns> public async Task<string> GetDashboard(string dashboardName) { var dashboardResponse = await _amazonCloudWatch.GetDashboardAsync( new GetDashboardRequest() { DashboardName = dashboardName }); return dashboardResponse.DashboardBody; } /// <summary> /// Get a list of dashboards. /// </summary> /// <returns>A list of DashboardEntry objects.</returns> public async Task<List<DashboardEntry>> ListDashboards() { var results = new List<DashboardEntry>(); var paginateDashboards = _amazonCloudWatch.Paginators.ListDashboards( new ListDashboardsRequest()); // Get the entire list using the paginator. await foreach (var data in paginateDashboards.DashboardEntries) { results.Add(data); } return results; } /// <summary> /// Wrapper to add metric data to a CloudWatch metric. /// </summary> /// <param name="metricNamespace">The namespace of the metric.</param> /// <param name="metricData">A data object for the metric data.</param> /// <returns>True if successful.</returns> public async Task<bool> PutMetricData(string metricNamespace, List<MetricDatum> metricData) { var putDataResponse = await _amazonCloudWatch.PutMetricDataAsync( new PutMetricDataRequest() { MetricData = metricData, Namespace = metricNamespace, }); return putDataResponse.HttpStatusCode == HttpStatusCode.OK; } /// <summary> /// Get an image for a metric graphed over time. /// </summary> /// <param name="metricNamespace">The namespace of the metric.</param> /// <param name="metric">The name of the metric.</param> /// <param name="stat">The name of the stat to chart.</param> /// <param name="period">The period to use for the chart.</param> /// <returns>A memory stream for the chart image.</returns> public async Task<MemoryStream> GetTimeSeriesMetricImage(string metricNamespace, string metric, string stat, int period) { var metricImageWidget = new { title = "Example Metric Graph", view = "timeSeries", stacked = false, period = period, width = 1400, height = 600, metrics = new List<List<object>> { new() { metricNamespace, metric, new { stat } } } }; var metricImageWidgetString = JsonSerializer.Serialize(metricImageWidget); var imageResponse = await _amazonCloudWatch.GetMetricWidgetImageAsync( new GetMetricWidgetImageRequest() { MetricWidget = metricImageWidgetString }); return imageResponse.MetricWidgetImage; } /// <summary> /// Save a metric image to a file. /// </summary> /// <param name="memoryStream">The MemoryStream for the metric image.</param> /// <param name="metricName">The name of the metric.</param> /// <returns>The path to the file.</returns> public string SaveMetricImage(MemoryStream memoryStream, string metricName) { var metricFileName = $"{metricName}_{DateTime.Now.Ticks}.png"; using var sr = new StreamReader(memoryStream); // Writes the memory stream to a file. File.WriteAllBytes(metricFileName, memoryStream.ToArray()); var filePath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, metricFileName); return filePath; } /// <summary> /// Get data for CloudWatch metrics. /// </summary> /// <param name="minutesOfData">The number of minutes of data to include.</param> /// <param name="useDescendingTime">True to return the data descending by time.</param> /// <param name="endDateUtc">The end date for the data, in UTC.</param> /// <param name="maxDataPoints">The maximum data points to include.</param> /// <param name="dataQueries">Optional data queries to include.</param> /// <returns>A list of the requested metric data.</returns> public async Task<List<MetricDataResult>> GetMetricData(int minutesOfData, bool useDescendingTime, DateTime? endDateUtc = null, int maxDataPoints = 0, List<MetricDataQuery>? dataQueries = null) { var metricData = new List<MetricDataResult>(); // If no end time is provided, use the current time for the end time. endDateUtc ??= DateTime.UtcNow; var timeZoneOffset = TimeZoneInfo.Local.GetUtcOffset(endDateUtc.Value.ToLocalTime()); var startTimeUtc = endDateUtc.Value.AddMinutes(-minutesOfData); // The timezone string should be in the format +0000, so use the timezone offset to format it correctly. var timeZoneString = $"{timeZoneOffset.Hours:D2}{timeZoneOffset.Minutes:D2}"; var paginatedMetricData = _amazonCloudWatch.Paginators.GetMetricData( new GetMetricDataRequest() { StartTimeUtc = startTimeUtc, EndTimeUtc = endDateUtc.Value, LabelOptions = new LabelOptions { Timezone = timeZoneString }, ScanBy = useDescendingTime ? ScanBy.TimestampDescending : ScanBy.TimestampAscending, MaxDatapoints = maxDataPoints, MetricDataQueries = dataQueries, }); await foreach (var data in paginatedMetricData.MetricDataResults) { metricData.Add(data); } return metricData; } /// <summary> /// Add a metric alarm to send an email when the metric passes a threshold. /// </summary> /// <param name="alarmDescription">A description of the alarm.</param> /// <param name="alarmName">The name for the alarm.</param> /// <param name="comparison">The type of comparison to use.</param> /// <param name="metricName">The name of the metric for the alarm.</param> /// <param name="metricNamespace">The namespace of the metric.</param> /// <param name="threshold">The threshold value for the alarm.</param> /// <param name="alarmActions">Optional actions to execute when in an alarm state.</param> /// <returns>True if successful.</returns> public async Task<bool> PutMetricEmailAlarm(string alarmDescription, string alarmName, ComparisonOperator comparison, string metricName, string metricNamespace, double threshold, List<string> alarmActions = null!) { try { var putEmailAlarmResponse = await _amazonCloudWatch.PutMetricAlarmAsync( new PutMetricAlarmRequest() { AlarmActions = alarmActions, AlarmDescription = alarmDescription, AlarmName = alarmName, ComparisonOperator = comparison, Threshold = threshold, Namespace = metricNamespace, MetricName = metricName, EvaluationPeriods = 1, Period = 10, Statistic = new Statistic("Maximum"), DatapointsToAlarm = 1, TreatMissingData = "ignore" }); return putEmailAlarmResponse.HttpStatusCode == HttpStatusCode.OK; } catch (LimitExceededException lex) { _logger.LogError(lex, $"Unable to add alarm {alarmName}. Alarm quota has already been reached."); } return false; } /// <summary> /// Add specific email actions to a list of action strings for a CloudWatch alarm. /// </summary> /// <param name="accountId">The AccountId for the alarm.</param> /// <param name="region">The region for the alarm.</param> /// <param name="emailTopicName">An Amazon Simple Notification Service (SNS) topic for the alarm email.</param> /// <param name="alarmActions">Optional list of existing alarm actions to append to.</param> /// <returns>A list of string actions for an alarm.</returns> public List<string> AddEmailAlarmAction(string accountId, string region, string emailTopicName, List<string>? alarmActions = null) { alarmActions ??= new List<string>(); var snsAlarmAction = $"arn:aws:sns:{region}:{accountId}:{emailTopicName}"; alarmActions.Add(snsAlarmAction); return alarmActions; } /// <summary> /// Describe the current alarms, optionally filtered by state. /// </summary> /// <param name="stateValue">Optional filter for alarm state.</param> /// <returns>The list of alarm data.</returns> public async Task<List<MetricAlarm>> DescribeAlarms(StateValue? stateValue = null) { List<MetricAlarm> alarms = new List<MetricAlarm>(); var paginatedDescribeAlarms = _amazonCloudWatch.Paginators.DescribeAlarms( new DescribeAlarmsRequest() { StateValue = stateValue }); await foreach (var data in paginatedDescribeAlarms.MetricAlarms) { alarms.Add(data); } return alarms; } /// <summary> /// Describe the current alarms for a specific metric. /// </summary> /// <param name="metricNamespace">The namespace of the metric.</param> /// <param name="metricName">The name of the metric.</param> /// <returns>The list of alarm data.</returns> public async Task<List<MetricAlarm>> DescribeAlarmsForMetric(string metricNamespace, string metricName) { var alarmsResult = await _amazonCloudWatch.DescribeAlarmsForMetricAsync( new DescribeAlarmsForMetricRequest() { Namespace = metricNamespace, MetricName = metricName }); return alarmsResult.MetricAlarms; } /// <summary> /// Describe the history of an alarm for a number of days in the past. /// </summary> /// <param name="alarmName">The name of the alarm.</param> /// <param name="historyDays">The number of days in the past.</param> /// <returns>The list of alarm history data.</returns> public async Task<List<AlarmHistoryItem>> DescribeAlarmHistory(string alarmName, int historyDays) { List<AlarmHistoryItem> alarmHistory = new List<AlarmHistoryItem>(); var paginatedAlarmHistory = _amazonCloudWatch.Paginators.DescribeAlarmHistory( new DescribeAlarmHistoryRequest() { AlarmName = alarmName, EndDateUtc = DateTime.UtcNow, HistoryItemType = HistoryItemType.StateUpdate, StartDateUtc = DateTime.UtcNow.AddDays(-historyDays) }); await foreach (var data in paginatedAlarmHistory.AlarmHistoryItems) { alarmHistory.Add(data); } return alarmHistory; } /// <summary> /// Delete a list of alarms from CloudWatch. /// </summary> /// <param name="alarmNames">A list of names of alarms to delete.</param> /// <returns>True if successful.</returns> public async Task<bool> DeleteAlarms(List<string> alarmNames) { var deleteAlarmsResult = await _amazonCloudWatch.DeleteAlarmsAsync( new DeleteAlarmsRequest() { AlarmNames = alarmNames }); return deleteAlarmsResult.HttpStatusCode == HttpStatusCode.OK; } /// <summary> /// Disable the actions for a list of alarms from CloudWatch. /// </summary> /// <param name="alarmNames">A list of names of alarms.</param> /// <returns>True if successful.</returns> public async Task<bool> DisableAlarmActions(List<string> alarmNames) { var disableAlarmActionsResult = await _amazonCloudWatch.DisableAlarmActionsAsync( new DisableAlarmActionsRequest() { AlarmNames = alarmNames }); return disableAlarmActionsResult.HttpStatusCode == HttpStatusCode.OK; } /// <summary> /// Enable the actions for a list of alarms from CloudWatch. /// </summary> /// <param name="alarmNames">A list of names of alarms.</param> /// <returns>True if successful.</returns> public async Task<bool> EnableAlarmActions(List<string> alarmNames) { var enableAlarmActionsResult = await _amazonCloudWatch.EnableAlarmActionsAsync( new EnableAlarmActionsRequest() { AlarmNames = alarmNames }); return enableAlarmActionsResult.HttpStatusCode == HttpStatusCode.OK; } /// <summary> /// Add an anomaly detector for a single metric. /// </summary> /// <param name="anomalyDetector">A single metric anomaly detector.</param> /// <returns>True if successful.</returns> public async Task<bool> PutAnomalyDetector(SingleMetricAnomalyDetector anomalyDetector) { var putAlarmDetectorResult = await _amazonCloudWatch.PutAnomalyDetectorAsync( new PutAnomalyDetectorRequest() { SingleMetricAnomalyDetector = anomalyDetector }); return putAlarmDetectorResult.HttpStatusCode == HttpStatusCode.OK; } /// <summary> /// Describe anomaly detectors for a metric and namespace. /// </summary> /// <param name="metricNamespace">The namespace of the metric.</param> /// <param name="metricName">The metric of the anomaly detectors.</param> /// <returns>The list of detectors.</returns> public async Task<List<AnomalyDetector>> DescribeAnomalyDetectors(string metricNamespace, string metricName) { List<AnomalyDetector> detectors = new List<AnomalyDetector>(); var paginatedDescribeAnomalyDetectors = _amazonCloudWatch.Paginators.DescribeAnomalyDetectors( new DescribeAnomalyDetectorsRequest() { MetricName = metricName, Namespace = metricNamespace }); await foreach (var data in paginatedDescribeAnomalyDetectors.AnomalyDetectors) { detectors.Add(data); } return detectors; } /// <summary> /// Delete a single metric anomaly detector. /// </summary> /// <param name="anomalyDetector">The anomaly detector to delete.</param> /// <returns>True if successful.</returns> public async Task<bool> DeleteAnomalyDetector(SingleMetricAnomalyDetector anomalyDetector) { var deleteAnomalyDetectorResponse = await _amazonCloudWatch.DeleteAnomalyDetectorAsync( new DeleteAnomalyDetectorRequest() { SingleMetricAnomalyDetector = anomalyDetector }); return deleteAnomalyDetectorResponse.HttpStatusCode == HttpStatusCode.OK; } /// <summary> /// Delete a list of CloudWatch dashboards. /// </summary> /// <param name="dashboardNames">List of dashboard names to delete.</param> /// <returns>True if successful.</returns> public async Task<bool> DeleteDashboards(List<string> dashboardNames) { var deleteDashboardsResponse = await _amazonCloudWatch.DeleteDashboardsAsync( new DeleteDashboardsRequest() { DashboardNames = dashboardNames }); return deleteDashboardsResponse.HttpStatusCode == HttpStatusCode.OK; } }