Skip to content

cloudwatch: Use of EC2 action with Multiple dimension set in metric results into error #29331

@rtejwani1309

Description

@rtejwani1309

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.

Metadata

Metadata

Assignees

Labels

@aws-cdk/aws-cloudwatchRelated to Amazon CloudWatchblockedWork is blocked on this issue for this codebase. Other labels or comments may indicate why.bugThis issue is a bug.effort/mediumMedium work item – several days of effortp1

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions