[AWS CDK] スタックデプロイ時に「’null’ values are not allowed in templates」エラーが発生したので対処した話

2021.12.14

こんにちは、CX事業本部 IoT事業部の若槻です。

今回は、AWS CDKでのスタックデプロイ時に'null' values are not allowed in templatesエラーが発生したので対処した話です。

事象

次のようなCDKスタックを作成して、CloudWatch Alarmのアラームをデプロイしようとしました。アラームのプロパティのうちevaluationPeriods(いくつのピリオドを評価対象とするか)にはパラメーターストアから取得した値を使用します。その際にString形式となっている取得データを数値形式に変換するようにしています。

lib/aws-cdk-app-stack.ts

import * as cloudwatch from '@aws-cdk/aws-cloudwatch';
import { StringParameter } from '@aws-cdk/aws-ssm';

export class AwsCdkAppStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const alarmConditionMinutesPeriod = StringParameter.valueForStringParameter(
      this,
      'alarmConditionMinutesPeriod'
    );

    new cloudwatch.Alarm(this, 'testAlarm01', {
      alarmName: 'testAlarm01',
      metric: new cloudwatch.Metric({
        namespace: 'test',
        metricName: 'metric01',
      }),
      evaluationPeriods: Number(alarmConditionMinutesPeriod), //取得したテキスト形式の値を数値に変換
      threshold: 10,
      statistic: cloudwatch.Statistic.MAXIMUM,
      comparisonOperator:
        cloudwatch.ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD,
      period: cdk.Duration.minutes(1),
      treatMissingData: cloudwatch.TreatMissingData.MISSING,
    });
  }
}

パラメーターストアにevaluationPeriodsとしたい値をセットします。今回は30です。

その上でCDKデプロイを行うと、次のようなエラーとなりました。

$ cdk deploy

(中略)

AwsCdkAppStack: deploying...
[0%] start: Publishing 6f5c602aa4dee18ce9a3ff99ea9fa864e2d50ce67c1dd257554b2ef0fce1b4b1:current
[100%] success: Published 6f5c602aa4dee18ce9a3ff99ea9fa864e2d50ce67c1dd257554b2ef0fce1b4b1:current
AwsCdkAppStack: creating CloudFormation changeset...

 ❌  AwsCdkAppStack failed: Error [ValidationError]: [/Resources/testAlarm01525934D0/Type/EvaluationPeriods] 'null' values are not allowed in templates
    at Request.extractError (/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/aws-cdk-app/node_modules/aws-cdk/node_modules/aws-sdk/lib/protocol/query.js:50:29)
    at Request.callListeners (/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/aws-cdk-app/node_modules/aws-cdk/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
    at Request.emit (/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/aws-cdk-app/node_modules/aws-cdk/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
    at Request.emit (/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/aws-cdk-app/node_modules/aws-cdk/node_modules/aws-sdk/lib/request.js:688:14)
    at Request.transition (/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/aws-cdk-app/node_modules/aws-cdk/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/aws-cdk-app/node_modules/aws-cdk/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /Users/wakatsuki.ryuta/projects/cm-rwakatsuki/aws-cdk-app/node_modules/aws-cdk/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/aws-cdk-app/node_modules/aws-cdk/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/aws-cdk-app/node_modules/aws-cdk/node_modules/aws-sdk/lib/request.js:690:12)
    at Request.callListeners (/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/aws-cdk-app/node_modules/aws-cdk/node_modules/aws-sdk/lib/sequential_executor.js:116:18) {
  code: 'ValidationError',
  time: 2021-12-14T14:47:04.930Z,
  requestId: 'd358359f-5cfe-4df9-952e-3496feff6532',
  statusCode: 400,
  retryable: false,
  retryDelay: 524.6367273287826
}
[/Resources/testAlarm01525934D0/Type/EvaluationPeriods] 'null' values are not allowed in templates

対処、解決

エラーメッセージを読むと、EvaluationPeriodsプロパティでnullが指定されてしまっているためエラーになっているように読み取れます。

AwsCdkAppStack failed: Error [ValidationError]: [/Resources/testAlarm01525934D0/Type/EvaluationPeriods] 'null' values are not allowed in templates

パラメーターストアから取得したalarmConditionMinutesPeriodの値が何らかの理由でnullとなってしまっているようです。

そこで色々調べたり試したりしたのですが、結論としては、alarmConditionMinutesPeriodNumber()関数を使わず、as unknown as numberによる型アサーションで数値形式とすることにより解決しました。

lib/aws-cdk-app-stack.ts

import * as cloudwatch from '@aws-cdk/aws-cloudwatch';
import { StringParameter } from '@aws-cdk/aws-ssm';

export class AwsCdkAppStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const alarmConditionMinutesPeriod = StringParameter.valueForStringParameter(
      this,
      'alarmConditionMinutesPeriod'
    );

    new cloudwatch.Alarm(this, 'testAlarm01', {
      alarmName: 'testAlarm01',
      metric: new cloudwatch.Metric({
        namespace: 'test',
        metricName: 'metric01',
      }),
      evaluationPeriods: alarmConditionMinutesPeriod as unknown as number, //型アサーションをするように修正
      threshold: 10,
      statistic: cloudwatch.Statistic.MAXIMUM,
      comparisonOperator:
        cloudwatch.ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD,
      period: cdk.Duration.minutes(1),
      treatMissingData: cloudwatch.TreatMissingData.MISSING,
    });
  }
}

上記修正をした上でスタックをデプロイすると正常に行うことができました。

$ cdk deploy

(中略)

AwsCdkAppStack: deploying...
[0%] start: Publishing 6f5c602aa4dee18ce9a3ff99ea9fa864e2d50ce67c1dd257554b2ef0fce1b4b1:current
[100%] success: Published 6f5c602aa4dee18ce9a3ff99ea9fa864e2d50ce67c1dd257554b2ef0fce1b4b1:current
AwsCdkAppStack: creating CloudFormation changeset...






 ✅  AwsCdkAppStack

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/AwsCdkAppStack/8aee57f0-4ad8-11ec-a443-0e5be759ac01

作成されたアラームを確認すると、ちゃんと30分となっています。

コンソールからだと少し分かりづらいのでCLIでも見てみると、こちらからもEvaluationPeriods30となっていることが確認できますね。

$ aws cloudwatch describe-alarms --alarm-names "testAlarm01"
{
    "MetricAlarms": [
        {
            "AlarmName": "testAlarm01",
            "AlarmArn": "arn:aws:cloudwatch:ap-northeast-1:XXXXXXXXXXXX:alarm:testAlarm01",
            "AlarmConfigurationUpdatedTimestamp": "2021-12-14T14:59:06.403000+00:00",
            "ActionsEnabled": true,
            "OKActions": [],
            "AlarmActions": [],
            "InsufficientDataActions": [],
            "StateValue": "INSUFFICIENT_DATA",
            "StateReason": "Unchecked: Initial alarm creation",
            "StateUpdatedTimestamp": "2021-12-14T14:59:06.403000+00:00",
            "MetricName": "metric01",
            "Namespace": "test_",
            "Statistic": "Maximum",
            "Dimensions": [],
            "Period": 60,
            "EvaluationPeriods": 30,
            "Threshold": 10.0,
            "ComparisonOperator": "LessThanOrEqualToThreshold",
            "TreatMissingData": "missing"
        }
    ],
    "CompositeAlarms": []
}

以上