-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Closed
Labels
@aws-cdk/aws-cloudwatchRelated to Amazon CloudWatchRelated to Amazon CloudWatchblockedWork is blocked on this issue for this codebase. Other labels or comments may indicate why.Work is blocked on this issue for this codebase. Other labels or comments may indicate why.bugThis issue is a bug.This issue is a bug.effort/mediumMedium work item – several days of effortMedium work item – several days of effortp1
Description
Describe the bug
While trying to create a Custom Metric with multiple dimension, and adding EC2 action, the CDK synth fails with the error below.
/Users/Desktop/tscdk/node_modules/aws-cdk-lib/aws-cloudwatch/lib/alarm.js:1
"use strict";var _a;Object.defineProperty(exports,"__esModule",{value:!0}),exports.Alarm=exports.TreatMissingData=exports.ComparisonOperator=void 0;var jsiiDeprecationWarnings=()=>{var tmp=require("../../.warnings.jsii.js");return jsiiDeprecationWarnings=()=>tmp,tmp};const JSII_RTTI_SYMBOL_1=Symbol.for("jsii.rtti");var alarm_base_1=()=>{var tmp=require("./alarm-base");return alarm_base_1=()=>tmp,tmp},cloudwatch_generated_1=()=>{var tmp=require("./cloudwatch.generated");return cloudwatch_generated_1=()=>tmp,tmp},metric_util_1=()=>{var tmp=require("./private/metric-util");return metric_util_1=()=>tmp,tmp},object_1=()=>{var tmp=require("./private/object");return object_1=()=>tmp,tmp},rendering_1=()=>{var tmp=require("./private/rendering");return rendering_1=()=>tmp,tmp},statistic_1=()=>{var tmp=require("./private/statistic");return statistic_1=()=>tmp,tmp},core_1=()=>{var tmp=require("../../core");return core_1=()=>tmp,tmp},ComparisonOperator;(function(ComparisonOperator2){ComparisonOperator2.GREATER_THAN_OR_EQUAL_TO_THRESHOLD="GreaterThanOrEqualToThreshold",ComparisonOperator2.GREATER_THAN_THRESHOLD="GreaterThanThreshold",ComparisonOperator2.LESS_THAN_THRESHOLD="LessThanThreshold",ComparisonOperator2.LESS_THAN_OR_EQUAL_TO_THRESHOLD="LessThanOrEqualToThreshold",ComparisonOperator2.LESS_THAN_LOWER_OR_GREATER_THAN_UPPER_THRESHOLD="LessThanLowerOrGreaterThanUpperThreshold",ComparisonOperator2.GREATER_THAN_UPPER_THRESHOLD="GreaterThanUpperThreshold",ComparisonOperator2.LESS_THAN_LOWER_THRESHOLD="LessThanLowerThreshold"})(ComparisonOperator||(exports.ComparisonOperator=ComparisonOperator={}));const OPERATOR_SYMBOLS={GreaterThanOrEqualToThreshold:">=",GreaterThanThreshold:">",LessThanThreshold:"<",LessThanOrEqualToThreshold:"<="};var TreatMissingData;(function(TreatMissingData2){TreatMissingData2.BREACHING="breaching",TreatMissingData2.NOT_BREACHING="notBreaching",TreatMissingData2.IGNORE="ignore",TreatMissingData2.MISSING="missing"})(TreatMissingData||(exports.TreatMissingData=TreatMissingData={}));class Alarm extends alarm_base_1().AlarmBase{static fromAlarmName(scope,id,alarmName){const stack=core_1().Stack.of(scope);return this.fromAlarmArn(scope,id,stack.formatArn({service:"cloudwatch",resource:"alarm",resourceName:alarmName,arnFormat:core_1().ArnFormat.COLON_RESOURCE_NAME}))}static fromAlarmArn(scope,id,alarmArn){class Import extends alarm_base_1().AlarmBase{constructor(){super(...arguments),this.alarmArn=alarmArn,this.alarmName=core_1().Stack.of(scope).splitArn(alarmArn,core_1().ArnFormat.COLON_RESOURCE_NAME).resourceName}}return new Import(scope,id)}constructor(scope,id,props){super(scope,id,{physicalName:props.alarmName});try{jsiiDeprecationWarnings().aws_cdk_lib_aws_cloudwatch_AlarmProps(props)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,Alarm),error}const comparisonOperator=props.comparisonOperator||ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,metricProps=this.renderMetric(props.metric);props.period&&(metricProps.period=props.period.toSeconds()),props.statistic&&Object.assign(metricProps,{statistic:renderIfSimpleStatistic(props.statistic),extendedStatistic:renderIfExtendedStatistic(props.statistic)});const alarm=new(cloudwatch_generated_1()).CfnAlarm(this,"Resource",{alarmDescription:props.alarmDescription,alarmName:this.physicalName,comparisonOperator,threshold:props.threshold,datapointsToAlarm:props.datapointsToAlarm,evaluateLowSampleCountPercentile:props.evaluateLowSampleCountPercentile,evaluationPeriods:props.evaluationPeriods,treatMissingData:props.treatMissingData,actionsEnabled:props.actionsEnabled,alarmActions:core_1().Lazy.list({produce:()=>this.alarmActionArns}),insufficientDataActions:core_1().Lazy.list({produce:()=>this.insufficientDataActionArns}),okActions:core_1().Lazy.list({produce:()=>this.okActionArns}),...metricProps});this.alarmArn=this.getResourceArnAttribute(alarm.attrArn,{service:"cloudwatch",resource:"alarm",resourceName:this.physicalName,arnFormat:core_1().ArnFormat.COLON_RESOURCE_NAME}),this.alarmName=this.getResourceNameAttribute(alarm.ref),this.metric=props.metric;const datapoints=props.datapointsToAlarm||props.evaluationPeriods;this.annotation={label:`${this.metric} ${OPERATOR_SYMBOLS[comparisonOperator]} ${props.threshold} for ${datapoints} datapoints within ${describePeriod(props.evaluationPeriods*(0,metric_util_1().metricPeriod)(props.metric).toSeconds())}`,value:props.threshold};for(const[i,message]of Object.entries(this.metric.warningsV2??{}))core_1().Annotations.of(this).addWarningV2(i,message)}toAnnotation(){return this.annotation}addAlarmAction(...actions){try{jsiiDeprecationWarnings().aws_cdk_lib_aws_cloudwatch_IAlarmAction(actions)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,this.addAlarmAction),error}this.alarmActionArns===void 0&&(this.alarmActionArns=[]),this.alarmActionArns.push(...actions.map(a=>this.validateActionArn(a.bind(this,this).alarmActionArn)))}validateActionArn(actionArn){if(/arn:aws[a-z0-9-]*:automate:[a-z|\d|-]+:ec2:[a-z]+/.test(actionArn)){const metricConfig=this.metric.toMetricConfig();if(metricConfig.metricStat?.dimensions?.length!=1||metricConfig.metricStat?.dimensions[0].name!="InstanceId")throw new Error(`EC2 alarm actions requires an EC2 Per-Instance Metric. (${JSON.stringify(metricConfig)} does not have an 'InstanceId' dimension)`)}return actionArn}renderMetric(metric){const self=this;return(0,metric_util_1().dispatchMetric)(metric,{withStat(stat,conf){return self.validateMetricStat(stat,metric),conf.renderingProperties?.label==null&&!self.requiresAccountId(stat)?(0,object_1().dropUndefined)({dimensions:stat.dimensions,namespace:stat.namespace,metricName:stat.metricName,period:stat.period?.toSeconds(),statistic:renderIfSimpleStatistic(stat.statistic),extendedStatistic:renderIfExtendedStatistic(stat.statistic),unit:stat.unitFilter}):{metrics:[{metricStat:{metric:{metricName:stat.metricName,namespace:stat.namespace,dimensions:stat.dimensions},period:stat.period.toSeconds(),stat:stat.statistic,unit:stat.unitFilter},id:"m1",accountId:self.requiresAccountId(stat)?stat.account:void 0,label:conf.renderingProperties?.label,returnData:!0}]}},withExpression(){const mset=new(rendering_1()).MetricSet;mset.addTopLevel(!0,metric);let eid=0;function uniqueMetricId(){return`expr_${++eid}`}return{metrics:mset.entries.map(entry=>(0,metric_util_1().dispatchMetric)(entry.metric,{withStat(stat,conf){return self.validateMetricStat(stat,entry.metric),{metricStat:{metric:{metricName:stat.metricName,namespace:stat.namespace,dimensions:stat.dimensions},period:stat.period.toSeconds(),stat:stat.statistic,unit:stat.unitFilter},id:entry.id||uniqueMetricId(),accountId:self.requiresAccountId(stat)?stat.account:void 0,label:conf.renderingProperties?.label,returnData:entry.tag?void 0:!1}},withExpression(expr,conf){const hasSubmetrics=mathExprHasSubmetrics(expr);return hasSubmetrics&&assertSubmetricsCount(expr),self.validateMetricExpression(expr),{expression:expr.expression,id:entry.id||uniqueMetricId(),label:conf.renderingProperties?.label,period:hasSubmetrics?void 0:expr.period,returnData:entry.tag?void 0:!1}}}))}}})}validateMetricStat(stat,metric){const stack=core_1().Stack.of(this);if(definitelyDifferent(stat.region,stack.region))throw new Error(`Cannot create an Alarm in region '${stack.region}' based on metric '${metric}' in '${stat.region}'`)}validateMetricExpression(expr){if(expr.searchAccount!==void 0||expr.searchRegion!==void 0)throw new Error("Cannot create an Alarm based on a MathExpression which specifies a searchAccount or searchRegion")}requiresAccountId(stat){const stackAccount=core_1().Stack.of(this).account;return stat.account===void 0?!1:stackAccount!==stat.account}}exports.Alarm=Alarm,_a=JSII_RTTI_SYMBOL_1,Alarm[_a]={fqn:"aws-cdk-lib.aws_cloudwatch.Alarm",version:"2.128.0"};function definitelyDifferent(x,y){return x&&!core_1().Token.isUnresolved(y)&&x!==y}function describePeriod(seconds){return seconds===60?"1 minute":seconds===1?"1 second":seconds>60?seconds/60+" minutes":seconds+" seconds"}function renderIfSimpleStatistic(statistic){if(statistic===void 0)return;const parsed=(0,statistic_1().parseStatistic)(statistic);if(parsed.type==="simple")return(0,statistic_1().normalizeStatistic)(parsed)}function renderIfExtendedStatistic(statistic){if(statistic===void 0)return;const parsed=(0,statistic_1().parseStatistic)(statistic);if(parsed.type!=="simple")return parsed.type==="single"||parsed.type==="pair"?(0,statistic_1().normalizeStatistic)(parsed):parsed.statistic}function mathExprHasSubmetrics(expr){return Object.keys(expr.usingMetrics).length>0}function assertSubmetricsCount(expr){if(Object.keys(expr.usingMetrics).length>10)throw new Error("Alarms on math expressions cannot contain more than 10 individual metrics")}
^
Error: EC2 alarm actions requires an EC2 Per-Instance Metric. ({"metricStat":{"dimensions":[{"name":"ImageId","value":"ami-0e670eb768a5fc3d4"},{"name":"InstanceId","value":"i-080e122e0fe45205e"},{"name":"InstanceType","value":"t2.micro"},{"name":"device","value":"xvda1"},{"name":"fstype","value":"xfs"},{"name":"path","value":"/"}],"namespace":"CWAgent","metricName":"disk_used_percent","period":{"amount":5,"unit":{"label":"minutes","isoLabel":"M","inMillis":60000}},"statistic":"Average"},"renderingProperties":{}} does not have an 'InstanceId' dimension)
at Alarm.validateActionArn (/Users//Desktop/tscdk/node_modules/aws-cdk-lib/aws-cloudwatch/lib/alarm.js:1:5300)
at /Users//Desktop/tscdk/node_modules/aws-cdk-lib/aws-cloudwatch/lib/alarm.js:1:4982
at Array.map (<anonymous>)
at Alarm.addAlarmAction (/Users//Desktop/tscdk/node_modules/aws-cdk-lib/aws-cloudwatch/lib/alarm.js:1:4970)
at new TscdkStack (/Users//Desktop/tscdk/lib/tscdk-stack.ts:66:15)
at Object.<anonymous> (/Users//Desktop/tscdk/bin/tscdk.ts:7:1)
at Module._compile (node:internal/modules/cjs/loader:1256:14)
at Module.m._compile (/Users//Desktop/tscdk/node_modules/ts-node/src/index.ts:1618:23)
at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
at Object.require.extensions.<computed> [as .ts] (/Users//Desktop/tscdk/node_modules/ts-node/src/index.ts:1621:12)
Subprocess exited with error 1
The code for generating the error is as below
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
// import * as sqs from 'aws-cdk-lib/aws-sqs';
import * as ec2 from 'aws-cdk-lib/aws-ec2'
import * as cw from 'aws-cdk-lib/aws-cloudwatch'
import { Ec2Action, Ec2InstanceAction } from 'aws-cdk-lib/aws-cloudwatch-actions';
export class TscdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const metric = new cw.Metric({
namespace: 'CWAgent',
metricName: 'disk_used_percent',
dimensionsMap: {
InstanceId: "<instance-id>",
ImageId: "ami-id",
InstanceType: "t2.micro",
path: "/",
device: "xvda1",
fstype: "xfs"
},
period: cdk.Duration.minutes(5),
statistic: "Average"
})
const sev3Alarm = new cw.Alarm(this, `DISK_USED_PERCENT_SEV3`, {
alarmName: `DISK_USED_PERCENT_SEV3`,
actionsEnabled: true,
metric: metric,
evaluationPeriods: 1,
datapointsToAlarm: 1,
threshold: 75,
comparisonOperator: cw.ComparisonOperator.GREATER_THAN_THRESHOLD,
treatMissingData: cw.TreatMissingData.BREACHING,
});
sev3Alarm.addAlarmAction(new Ec2Action(Ec2InstanceAction.REBOOT));
}
}To resolve the issue, there are 2 ways of doing it, the first one is we comment out the other dimensions apart from instance id in the dimensions map as below.
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
// import * as sqs from 'aws-cdk-lib/aws-sqs';
import * as ec2 from 'aws-cdk-lib/aws-ec2'
import * as cw from 'aws-cdk-lib/aws-cloudwatch'
import { Ec2Action, Ec2InstanceAction } from 'aws-cdk-lib/aws-cloudwatch-actions';
export class TscdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const metric = new cw.Metric({
namespace: 'CWAgent',
metricName: 'disk_used_percent',
dimensionsMap: {
InstanceId: "instance-id"
},
period: cdk.Duration.minutes(5),
statistic: "Average"
})
const sev3Alarm = new cw.Alarm(this, `DISK_USED_PERCENT_SEV3`, {
alarmName: `DISK_USED_PERCENT_SEV3`,
actionsEnabled: true,
metric: metric,
evaluationPeriods: 1,
datapointsToAlarm: 1,
threshold: 75,
comparisonOperator: cw.ComparisonOperator.GREATER_THAN_THRESHOLD,
treatMissingData: cw.TreatMissingData.BREACHING,
});
sev3Alarm.addAlarmAction(new Ec2Action(Ec2InstanceAction.REBOOT));
}
}Or we use escape hatch as below.
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
// import * as sqs from 'aws-cdk-lib/aws-sqs';
import * as ec2 from 'aws-cdk-lib/aws-ec2'
import * as cw from 'aws-cdk-lib/aws-cloudwatch'
import { Ec2Action, Ec2InstanceAction } from 'aws-cdk-lib/aws-cloudwatch-actions';
export class TscdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const metric = new cw.Metric({
namespace: 'CWAgent',
metricName: 'disk_used_percent',
dimensionsMap: {
InstanceId: "instance-id"
},
period: cdk.Duration.minutes(5),
statistic: "Average"
})
const sev3Alarm = new cw.Alarm(this, `DISK_USED_PERCENT_SEV3`, {
alarmName: `DISK_USED_PERCENT_SEV3`,
actionsEnabled: true,
metric: metric,
evaluationPeriods: 1,
datapointsToAlarm: 1,
threshold: 75,
comparisonOperator: cw.ComparisonOperator.GREATER_THAN_THRESHOLD,
treatMissingData: cw.TreatMissingData.BREACHING,
});
var sev3AlarmCfn = sev3Alarm.node.defaultChild as cw.CfnAlarm
sev3AlarmCfn.dimensions = [{
name: "InstanceId",
value: "instance-id"
},
{
name: "ImageId",
value: "imageId"
},
{
name: "device",
value: "xvda1"
},
{
name: "InstanceType",
value: "t2.micro"
},
{
name: "path",
value: "/"
},
{
name: "device",
value: "xvda1"
},
{
name: "fstype",
value: "xfs"
}
]
sev3Alarm.addAlarmAction(new Ec2Action(Ec2InstanceAction.REBOOT));
}
}Expected Behavior
The expected behaviour is that it should allow to add EC2 action for the custom metrics too, as one can do using CloudWatch Console, and CloudFormation.
Current Behavior
The current behaviour is that it results into the error stated below.
/Users//Desktop/tscdk/node_modules/aws-cdk-lib/aws-cloudwatch/lib/alarm.js:1
"use strict";var _a;Object.defineProperty(exports,"__esModule",{value:!0}),exports.Alarm=exports.TreatMissingData=exports.ComparisonOperator=void 0;var jsiiDeprecationWarnings=()=>{var tmp=require("../../.warnings.jsii.js");return jsiiDeprecationWarnings=()=>tmp,tmp};const JSII_RTTI_SYMBOL_1=Symbol.for("jsii.rtti");var alarm_base_1=()=>{var tmp=require("./alarm-base");return alarm_base_1=()=>tmp,tmp},cloudwatch_generated_1=()=>{var tmp=require("./cloudwatch.generated");return cloudwatch_generated_1=()=>tmp,tmp},metric_util_1=()=>{var tmp=require("./private/metric-util");return metric_util_1=()=>tmp,tmp},object_1=()=>{var tmp=require("./private/object");return object_1=()=>tmp,tmp},rendering_1=()=>{var tmp=require("./private/rendering");return rendering_1=()=>tmp,tmp},statistic_1=()=>{var tmp=require("./private/statistic");return statistic_1=()=>tmp,tmp},core_1=()=>{var tmp=require("../../core");return core_1=()=>tmp,tmp},ComparisonOperator;(function(ComparisonOperator2){ComparisonOperator2.GREATER_THAN_OR_EQUAL_TO_THRESHOLD="GreaterThanOrEqualToThreshold",ComparisonOperator2.GREATER_THAN_THRESHOLD="GreaterThanThreshold",ComparisonOperator2.LESS_THAN_THRESHOLD="LessThanThreshold",ComparisonOperator2.LESS_THAN_OR_EQUAL_TO_THRESHOLD="LessThanOrEqualToThreshold",ComparisonOperator2.LESS_THAN_LOWER_OR_GREATER_THAN_UPPER_THRESHOLD="LessThanLowerOrGreaterThanUpperThreshold",ComparisonOperator2.GREATER_THAN_UPPER_THRESHOLD="GreaterThanUpperThreshold",ComparisonOperator2.LESS_THAN_LOWER_THRESHOLD="LessThanLowerThreshold"})(ComparisonOperator||(exports.ComparisonOperator=ComparisonOperator={}));const OPERATOR_SYMBOLS={GreaterThanOrEqualToThreshold:">=",GreaterThanThreshold:">",LessThanThreshold:"<",LessThanOrEqualToThreshold:"<="};var TreatMissingData;(function(TreatMissingData2){TreatMissingData2.BREACHING="breaching",TreatMissingData2.NOT_BREACHING="notBreaching",TreatMissingData2.IGNORE="ignore",TreatMissingData2.MISSING="missing"})(TreatMissingData||(exports.TreatMissingData=TreatMissingData={}));class Alarm extends alarm_base_1().AlarmBase{static fromAlarmName(scope,id,alarmName){const stack=core_1().Stack.of(scope);return this.fromAlarmArn(scope,id,stack.formatArn({service:"cloudwatch",resource:"alarm",resourceName:alarmName,arnFormat:core_1().ArnFormat.COLON_RESOURCE_NAME}))}static fromAlarmArn(scope,id,alarmArn){class Import extends alarm_base_1().AlarmBase{constructor(){super(...arguments),this.alarmArn=alarmArn,this.alarmName=core_1().Stack.of(scope).splitArn(alarmArn,core_1().ArnFormat.COLON_RESOURCE_NAME).resourceName}}return new Import(scope,id)}constructor(scope,id,props){super(scope,id,{physicalName:props.alarmName});try{jsiiDeprecationWarnings().aws_cdk_lib_aws_cloudwatch_AlarmProps(props)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,Alarm),error}const comparisonOperator=props.comparisonOperator||ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,metricProps=this.renderMetric(props.metric);props.period&&(metricProps.period=props.period.toSeconds()),props.statistic&&Object.assign(metricProps,{statistic:renderIfSimpleStatistic(props.statistic),extendedStatistic:renderIfExtendedStatistic(props.statistic)});const alarm=new(cloudwatch_generated_1()).CfnAlarm(this,"Resource",{alarmDescription:props.alarmDescription,alarmName:this.physicalName,comparisonOperator,threshold:props.threshold,datapointsToAlarm:props.datapointsToAlarm,evaluateLowSampleCountPercentile:props.evaluateLowSampleCountPercentile,evaluationPeriods:props.evaluationPeriods,treatMissingData:props.treatMissingData,actionsEnabled:props.actionsEnabled,alarmActions:core_1().Lazy.list({produce:()=>this.alarmActionArns}),insufficientDataActions:core_1().Lazy.list({produce:()=>this.insufficientDataActionArns}),okActions:core_1().Lazy.list({produce:()=>this.okActionArns}),...metricProps});this.alarmArn=this.getResourceArnAttribute(alarm.attrArn,{service:"cloudwatch",resource:"alarm",resourceName:this.physicalName,arnFormat:core_1().ArnFormat.COLON_RESOURCE_NAME}),this.alarmName=this.getResourceNameAttribute(alarm.ref),this.metric=props.metric;const datapoints=props.datapointsToAlarm||props.evaluationPeriods;this.annotation={label:`${this.metric} ${OPERATOR_SYMBOLS[comparisonOperator]} ${props.threshold} for ${datapoints} datapoints within ${describePeriod(props.evaluationPeriods*(0,metric_util_1().metricPeriod)(props.metric).toSeconds())}`,value:props.threshold};for(const[i,message]of Object.entries(this.metric.warningsV2??{}))core_1().Annotations.of(this).addWarningV2(i,message)}toAnnotation(){return this.annotation}addAlarmAction(...actions){try{jsiiDeprecationWarnings().aws_cdk_lib_aws_cloudwatch_IAlarmAction(actions)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,this.addAlarmAction),error}this.alarmActionArns===void 0&&(this.alarmActionArns=[]),this.alarmActionArns.push(...actions.map(a=>this.validateActionArn(a.bind(this,this).alarmActionArn)))}validateActionArn(actionArn){if(/arn:aws[a-z0-9-]*:automate:[a-z|\d|-]+:ec2:[a-z]+/.test(actionArn)){const metricConfig=this.metric.toMetricConfig();if(metricConfig.metricStat?.dimensions?.length!=1||metricConfig.metricStat?.dimensions[0].name!="InstanceId")throw new Error(`EC2 alarm actions requires an EC2 Per-Instance Metric. (${JSON.stringify(metricConfig)} does not have an 'InstanceId' dimension)`)}return actionArn}renderMetric(metric){const self=this;return(0,metric_util_1().dispatchMetric)(metric,{withStat(stat,conf){return self.validateMetricStat(stat,metric),conf.renderingProperties?.label==null&&!self.requiresAccountId(stat)?(0,object_1().dropUndefined)({dimensions:stat.dimensions,namespace:stat.namespace,metricName:stat.metricName,period:stat.period?.toSeconds(),statistic:renderIfSimpleStatistic(stat.statistic),extendedStatistic:renderIfExtendedStatistic(stat.statistic),unit:stat.unitFilter}):{metrics:[{metricStat:{metric:{metricName:stat.metricName,namespace:stat.namespace,dimensions:stat.dimensions},period:stat.period.toSeconds(),stat:stat.statistic,unit:stat.unitFilter},id:"m1",accountId:self.requiresAccountId(stat)?stat.account:void 0,label:conf.renderingProperties?.label,returnData:!0}]}},withExpression(){const mset=new(rendering_1()).MetricSet;mset.addTopLevel(!0,metric);let eid=0;function uniqueMetricId(){return`expr_${++eid}`}return{metrics:mset.entries.map(entry=>(0,metric_util_1().dispatchMetric)(entry.metric,{withStat(stat,conf){return self.validateMetricStat(stat,entry.metric),{metricStat:{metric:{metricName:stat.metricName,namespace:stat.namespace,dimensions:stat.dimensions},period:stat.period.toSeconds(),stat:stat.statistic,unit:stat.unitFilter},id:entry.id||uniqueMetricId(),accountId:self.requiresAccountId(stat)?stat.account:void 0,label:conf.renderingProperties?.label,returnData:entry.tag?void 0:!1}},withExpression(expr,conf){const hasSubmetrics=mathExprHasSubmetrics(expr);return hasSubmetrics&&assertSubmetricsCount(expr),self.validateMetricExpression(expr),{expression:expr.expression,id:entry.id||uniqueMetricId(),label:conf.renderingProperties?.label,period:hasSubmetrics?void 0:expr.period,returnData:entry.tag?void 0:!1}}}))}}})}validateMetricStat(stat,metric){const stack=core_1().Stack.of(this);if(definitelyDifferent(stat.region,stack.region))throw new Error(`Cannot create an Alarm in region '${stack.region}' based on metric '${metric}' in '${stat.region}'`)}validateMetricExpression(expr){if(expr.searchAccount!==void 0||expr.searchRegion!==void 0)throw new Error("Cannot create an Alarm based on a MathExpression which specifies a searchAccount or searchRegion")}requiresAccountId(stat){const stackAccount=core_1().Stack.of(this).account;return stat.account===void 0?!1:stackAccount!==stat.account}}exports.Alarm=Alarm,_a=JSII_RTTI_SYMBOL_1,Alarm[_a]={fqn:"aws-cdk-lib.aws_cloudwatch.Alarm",version:"2.128.0"};function definitelyDifferent(x,y){return x&&!core_1().Token.isUnresolved(y)&&x!==y}function describePeriod(seconds){return seconds===60?"1 minute":seconds===1?"1 second":seconds>60?seconds/60+" minutes":seconds+" seconds"}function renderIfSimpleStatistic(statistic){if(statistic===void 0)return;const parsed=(0,statistic_1().parseStatistic)(statistic);if(parsed.type==="simple")return(0,statistic_1().normalizeStatistic)(parsed)}function renderIfExtendedStatistic(statistic){if(statistic===void 0)return;const parsed=(0,statistic_1().parseStatistic)(statistic);if(parsed.type!=="simple")return parsed.type==="single"||parsed.type==="pair"?(0,statistic_1().normalizeStatistic)(parsed):parsed.statistic}function mathExprHasSubmetrics(expr){return Object.keys(expr.usingMetrics).length>0}function assertSubmetricsCount(expr){if(Object.keys(expr.usingMetrics).length>10)throw new Error("Alarms on math expressions cannot contain more than 10 individual metrics")}
^
Error: EC2 alarm actions requires an EC2 Per-Instance Metric. ({"metricStat":{"dimensions":[{"name":"ImageId","value":"ami-0e670eb768a5fc3d4"},{"name":"InstanceId","value":"i-080e122e0fe45205e"},{"name":"InstanceType","value":"t2.micro"},{"name":"device","value":"xvda1"},{"name":"fstype","value":"xfs"},{"name":"path","value":"/"}],"namespace":"CWAgent","metricName":"disk_used_percent","period":{"amount":5,"unit":{"label":"minutes","isoLabel":"M","inMillis":60000}},"statistic":"Average"},"renderingProperties":{}} does not have an 'InstanceId' dimension)
at Alarm.validateActionArn (/Users//Desktop/tscdk/node_modules/aws-cdk-lib/aws-cloudwatch/lib/alarm.js:1:5300)
at /Users//Desktop/tscdk/node_modules/aws-cdk-lib/aws-cloudwatch/lib/alarm.js:1:4982
at Array.map (<anonymous>)
at Alarm.addAlarmAction (/Users//Desktop/tscdk/node_modules/aws-cdk-lib/aws-cloudwatch/lib/alarm.js:1:4970)
at new TscdkStack (/Users//Desktop/tscdk/lib/tscdk-stack.ts:66:15)
at Object.<anonymous> (/Users//Desktop/tscdk/bin/tscdk.ts:7:1)
at Module._compile (node:internal/modules/cjs/loader:1256:14)
at Module.m._compile (/Users//Desktop/tscdk/node_modules/ts-node/src/index.ts:1618:23)
at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
at Object.require.extensions.<computed> [as .ts] (/Users//Desktop/tscdk/node_modules/ts-node/src/index.ts:1621:12)
Subprocess exited with error 1
Reproduction Steps
Use the below code, and run cdk synth
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
// import * as sqs from 'aws-cdk-lib/aws-sqs';
import * as ec2 from 'aws-cdk-lib/aws-ec2'
import * as cw from 'aws-cdk-lib/aws-cloudwatch'
import { Ec2Action, Ec2InstanceAction } from 'aws-cdk-lib/aws-cloudwatch-actions';
export class TscdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const metric = new cw.Metric({
namespace: 'CWAgent',
metricName: 'disk_used_percent',
dimensionsMap: {
InstanceId: "instance-id",
ImageId: "image-id",
InstanceType: "t2.micro",
path: "/",
device: "xvda1",
fstype: "xfs"
},
period: cdk.Duration.minutes(5),
statistic: "Average"
})
const sev3Alarm = new cw.Alarm(this, `DISK_USED_PERCENT_SEV3`, {
alarmName: `DISK_USED_PERCENT_SEV3`,
actionsEnabled: true,
metric: metric,
evaluationPeriods: 1,
datapointsToAlarm: 1,
threshold: 75,
comparisonOperator: cw.ComparisonOperator.GREATER_THAN_THRESHOLD,
treatMissingData: cw.TreatMissingData.BREACHING,
});
sev3Alarm.addAlarmAction(new Ec2Action(Ec2InstanceAction.REBOOT));
}
}
### Possible Solution
Possible bug, it is causing the issue here at this piece of code.| throw new Error(`EC2 alarm actions requires an EC2 Per-Instance Metric. (${JSON.stringify(metricConfig)} does not have an 'InstanceId' dimension)`); |
### Additional Information/Context
_No response_
### CDK CLI Version
"aws-cdk-lib": "2.128.0",
### Framework Version
"constructs": "^10.0.0",
### Node.js Version
v18.18.0
### OS
MacOs
### Language
TypeScript
### Language Version
"typescript": "~5.3.3"
### Other information
Please note, I see a workflow issue over here, as if adding the dimensions directly to the metric is not working, then it should also not work while adding it through escape hatch.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
@aws-cdk/aws-cloudwatchRelated to Amazon CloudWatchRelated to Amazon CloudWatchblockedWork is blocked on this issue for this codebase. Other labels or comments may indicate why.Work is blocked on this issue for this codebase. Other labels or comments may indicate why.bugThis issue is a bug.This issue is a bug.effort/mediumMedium work item – several days of effortMedium work item – several days of effortp1