A Pulumi package for creating and managing Amazon Web Services (AWS) cloud resources with infrastructure-as-code.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Comprehensive guide for monitoring AWS resources with Amazon CloudWatch.
CloudWatch provides monitoring and observability for AWS cloud resources and applications through metrics, logs, alarms, and dashboards.
import * as aws from "@pulumi/aws";
import * as cloudwatch from "@pulumi/aws/cloudwatch";const cpuAlarm = new aws.cloudwatch.MetricAlarm("high-cpu", {
name: "high-cpu-utilization",
comparisonOperator: "GreaterThanThreshold",
evaluationPeriods: 2,
metricName: "CPUUtilization",
namespace: "AWS/EC2",
period: 300,
statistic: "Average",
threshold: 80,
alarmDescription: "Alert when CPU exceeds 80%",
dimensions: {
InstanceId: instance.id,
},
alarmActions: [snsTopic.arn],
okActions: [snsTopic.arn],
});const compositeAlarm = new aws.cloudwatch.CompositeAlarm("system-health", {
alarmName: "system-health-composite",
alarmRule: pulumi.interpolate`
ALARM(${cpuAlarm.arn}) OR
ALARM(${memoryAlarm.arn}) OR
ALARM(${diskAlarm.arn})
`,
alarmActions: [criticalTopic.arn],
});const logGroup = new aws.cloudwatch.LogGroup("application-logs", {
name: "/aws/application/myapp",
retentionInDays: 30,
kmsKeyId: kmsKey.id,
tags: {
Application: "myapp",
Environment: "production",
},
});
const logStream = new aws.cloudwatch.LogStream("app-stream", {
name: "application-stream",
logGroupName: logGroup.name,
});const errorMetric = new aws.cloudwatch.LogMetricFilter("error-count", {
name: "application-errors",
logGroupName: logGroup.name,
pattern: "[timestamp, request_id, level = ERROR*, ...]",
metricTransformation: {
name: "ErrorCount",
namespace: "MyApp/Errors",
value: "1",
defaultValue: "0",
unit: "Count",
},
});
// Create alarm on the metric
const errorAlarm = new aws.cloudwatch.MetricAlarm("error-rate", {
name: "high-error-rate",
comparisonOperator: "GreaterThanThreshold",
evaluationPeriods: 1,
metricName: errorMetric.metricTransformation.name,
namespace: errorMetric.metricTransformation.namespace,
period: 60,
statistic: "Sum",
threshold: 10,
alarmActions: [snsTopic.arn],
});const query = new aws.cloudwatch.QueryDefinition("slow-queries", {
name: "Slow Database Queries",
logGroupNames: [logGroup.name],
queryString: `
fields @timestamp, @message, duration
| filter duration > 1000
| sort duration desc
| limit 20
`,
});const dashboard = new aws.cloudwatch.Dashboard("app-dashboard", {
dashboardName: "application-metrics",
dashboardBody: pulumi.all([
instance.id,
loadBalancer.arn,
]).apply(([instanceId, lbArn]) => JSON.stringify({
widgets: [
{
type: "metric",
properties: {
metrics: [
["AWS/EC2", "CPUUtilization", { stat: "Average" }],
[".", "NetworkIn"],
[".", "NetworkOut"],
],
period: 300,
stat: "Average",
region: "us-west-2",
title: "EC2 Metrics",
yAxis: {
left: { min: 0 },
},
},
},
{
type: "metric",
properties: {
metrics: [
["AWS/ApplicationELB", "TargetResponseTime", { stat: "Average" }],
[".", "RequestCount", { stat: "Sum" }],
],
period: 60,
region: "us-west-2",
title: "Load Balancer Metrics",
},
},
{
type: "log",
properties: {
query: `SOURCE '${logGroup.name}' | fields @timestamp, @message | sort @timestamp desc | limit 100`,
region: "us-west-2",
title: "Recent Logs",
},
},
],
})),
});// Lambda function to publish custom metrics
const metricPublisher = new aws.lambda.Function("metric-publisher", {
runtime: "nodejs18.x",
handler: "index.handler",
role: lambdaRole.arn,
code: new pulumi.asset.AssetArchive({
"index.js": new pulumi.asset.StringAsset(`
const { CloudWatchClient, PutMetricDataCommand } = require("@aws-sdk/client-cloudwatch");
exports.handler = async (event) => {
const client = new CloudWatchClient({});
await client.send(new PutMetricDataCommand({
Namespace: "MyApp/Custom",
MetricData: [{
MetricName: "ProcessingTime",
Value: event.duration,
Unit: "Milliseconds",
Timestamp: new Date(),
Dimensions: [{
Name: "Environment",
Value: "production"
}]
}]
}));
};
`),
}),
});const customMetricAlarm = new aws.cloudwatch.MetricAlarm("custom-metric", {
name: "high-processing-time",
comparisonOperator: "GreaterThanThreshold",
evaluationPeriods: 2,
metricName: "ProcessingTime",
namespace: "MyApp/Custom",
period: 300,
statistic: "Average",
threshold: 5000,
dimensions: {
Environment: "production",
},
alarmActions: [snsTopic.arn],
});const cluster = new aws.ecs.Cluster("app-cluster", {
name: "application-cluster",
settings: [{
name: "containerInsights",
value: "enabled",
}],
});const lambdaFunction = new aws.lambda.Function("monitored-function", {
runtime: "nodejs18.x",
handler: "index.handler",
role: lambdaRole.arn,
code: new pulumi.asset.AssetArchive({
".": new pulumi.asset.FileArchive("./function"),
}),
layers: [
"arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension:21",
],
});// Create alarms in multiple regions
const regions = ["us-west-2", "us-east-1", "eu-west-1"];
const regionalAlarms = regions.map(region => {
const provider = new aws.Provider(`provider-${region}`, { region });
return new aws.cloudwatch.MetricAlarm(`alarm-${region}`, {
name: `regional-alarm-${region}`,
comparisonOperator: "GreaterThanThreshold",
evaluationPeriods: 1,
metricName: "Errors",
namespace: "AWS/Lambda",
period: 60,
statistic: "Sum",
threshold: 5,
alarmActions: [snsTopic.arn],
}, { provider });
});const anomalyAlarm = new aws.cloudwatch.MetricAlarm("anomaly-detection", {
name: "traffic-anomaly",
comparisonOperator: "LessThanLowerOrGreaterThanUpperThreshold",
evaluationPeriods: 2,
thresholdMetricId: "e1",
metricQueries: [
{
id: "e1",
expression: "ANOMALY_DETECTION_BAND(m1, 2)",
label: "RequestCount (Expected)",
},
{
id: "m1",
metric: {
metricName: "RequestCount",
namespace: "AWS/ApplicationELB",
period: 300,
stat: "Sum",
},
},
],
});Install with Tessl CLI
npx tessl i tessl/npm-pulumi--aws@7.16.0