こんにちは、CX 事業本部/製造ビジネステクノロジー部/新規サービスチームの若槻です。
Amazon EventBridge Scheduler は、Lambda 関数などのターゲットをスケジュール実行できる Amazon EventBridge の機能です。
以前までは EventBridge スケジュールルールでも同様のことができましたが、EventBridge Scheduler はスケジュール実行のための機能として特化しており、スケジュールルールに比べて高度なカスタマイズ性やスケーラビリティを備えています。両者の比較は次の記事が参考になります。
そして EventBridge Scheduler を AWS CDK で構成する場合は現状では alpha module を利用します。
この AWS CDK の alpha module を利用して Amazon EventBridge Scheduler を構成する際に、 Resolution error: All principals in a PolicyStatement must have the same Conditions
というエラーが発生したので対処した内容を共有します。
はじめに結論
EventBridge Scheduler では次のように aws_scheduler_targets_alpha.LambdaInvoke
を使用して Lambda 関数を実行ターゲットとして指定するのですが、その際に 同一の CDK スタック内で複数の Scheduler を作成する場合 は、ターゲットで明示的に role
プロパティを指定する必要がありました。
lib/cdk-sample-stack.ts
import {
aws_lambda_nodejs,
aws_lambda,
aws_iam,
Stack,
TimeZone,
} from 'aws-cdk-lib';
import * as aws_scheduler_alpha from '@aws-cdk/aws-scheduler-alpha';
import * as aws_scheduler_targets_alpha from '@aws-cdk/aws-scheduler-targets-alpha';
import { Construct } from 'constructs';
export class CdkSampleStack extends Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
const accounId = this.account;
const region = this.region;
// Lambda 関数
const helloFunc = new aws_lambda_nodejs.NodejsFunction(this, 'HelloFunc', {
runtime: aws_lambda.Runtime.NODEJS_20_X,
architecture: aws_lambda.Architecture.ARM_64,
});
// Lambda 実行ターゲット
const target = new aws_scheduler_targets_alpha.LambdaInvoke(helloFunc, {
role: new aws_iam.Role(this, 'Role', {
assumedBy: new aws_iam.ServicePrincipal(
'scheduler.amazonaws.com'
).withConditions({
StringEquals: {
'aws:SourceAccount': accounId,
},
StringLike: {
'aws:SourceArn': `arn:aws:scheduler:${region}:${accounId}:schedule/default/*`,
},
}),
}),
});
// Scheduler その1
new aws_scheduler_alpha.Schedule(this, 'Shedule1', {
schedule: aws_scheduler_alpha.ScheduleExpression.cron({
minute: '0',
hour: '1',
month: '3',
day: '1-31',
year: '2024',
timeZone: TimeZone.ASIA_TOKYO,
}),
target,
});
// Scheduler その2
new aws_scheduler_alpha.Schedule(this, 'Shedule2', {
schedule: aws_scheduler_alpha.ScheduleExpression.cron({
minute: '0',
hour: '1',
month: '4',
day: '1-10',
year: '2024',
timeZone: TimeZone.ASIA_TOKYO,
}),
target,
});
}
}
CDK デプロイにより、上記で定義した 2 つの Scheduler が作成されました。
事象
当初、同じ Lambda 関数をターゲットとした複数の Scheduler を次のように作成しようとしました。
lib/cdk-sample-stack.ts
import { aws_lambda_nodejs, aws_lambda, Stack, TimeZone } from 'aws-cdk-lib';
import * as aws_scheduler_alpha from '@aws-cdk/aws-scheduler-alpha';
import * as aws_scheduler_targets_alpha from '@aws-cdk/aws-scheduler-targets-alpha';
import { Construct } from 'constructs';
export class CdkSampleStack extends Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
// Lambda 関数
const helloFunc = new aws_lambda_nodejs.NodejsFunction(this, 'HelloFunc', {
runtime: aws_lambda.Runtime.NODEJS_20_X,
architecture: aws_lambda.Architecture.ARM_64,
});
// Lambda 実行ターゲット
const target = new aws_scheduler_targets_alpha.LambdaInvoke(helloFunc, {});
// Scheduler その1
new aws_scheduler_alpha.Schedule(this, 'Shedule1', {
schedule: aws_scheduler_alpha.ScheduleExpression.cron({
minute: '0',
hour: '1',
month: '3',
day: '1-31',
year: '2024',
timeZone: TimeZone.ASIA_TOKYO,
}),
target,
});
// Scheduler その2
new aws_scheduler_alpha.Schedule(this, 'Shedule2', {
schedule: aws_scheduler_alpha.ScheduleExpression.cron({
minute: '0',
hour: '1',
month: '4',
day: '1-10',
year: '2024',
timeZone: TimeZone.ASIA_TOKYO,
}),
target,
});
}
}
しかし CDK Synth 時に次のようなエラーが発生しました。
$ cdk synth
Error: Resolution error: Resolution error: Resolution error: All principals in a PolicyStatement must have the same Conditions (got '{}' and '{"StringEquals":{"aws:SourceAccount":"${Token[AWS.AccountId.9]}"}}'). Use multiple statements instead..
切り分け
その1
実行対象の Lambda 関数およびターゲットを Scheduler ごとに分けてみました。
lib/cdk-sample-stack.ts
import { aws_lambda_nodejs, aws_lambda, Stack, TimeZone } from 'aws-cdk-lib';
import * as aws_scheduler_alpha from '@aws-cdk/aws-scheduler-alpha';
import * as aws_scheduler_targets_alpha from '@aws-cdk/aws-scheduler-targets-alpha';
import { Construct } from 'constructs';
export class CdkSampleStack extends Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
// Lambda 関数1
const helloFunc = new aws_lambda_nodejs.NodejsFunction(this, 'HelloFunc', {
runtime: aws_lambda.Runtime.NODEJS_20_X,
architecture: aws_lambda.Architecture.ARM_64,
});
// Lambda 実行ターゲット1
const target = new aws_scheduler_targets_alpha.LambdaInvoke(helloFunc, {});
// Scheduler その1
new aws_scheduler_alpha.Schedule(this, 'Shedule1', {
schedule: aws_scheduler_alpha.ScheduleExpression.cron({
minute: '0',
hour: '1',
month: '3',
day: '1-31',
year: '2024',
timeZone: TimeZone.ASIA_TOKYO,
}),
target,
});
// Lambda 関数2
const helloFunc2 = new aws_lambda_nodejs.NodejsFunction(
this,
'HelloFunc2',
{
runtime: aws_lambda.Runtime.NODEJS_20_X,
architecture: aws_lambda.Architecture.ARM_64,
}
);
// Lambda 実行ターゲット2
const target2 = new aws_scheduler_targets_alpha.LambdaInvoke(
helloFunc2,
{}
);
// Scheduler その2
new aws_scheduler_alpha.Schedule(this, 'Shedule2', {
schedule: aws_scheduler_alpha.ScheduleExpression.cron({
minute: '0',
hour: '1',
month: '4',
day: '1-10',
year: '2024',
timeZone: TimeZone.ASIA_TOKYO,
}),
target: target2,
});
}
}
すると同じエラーが再度発生しました。
$ cdk synth
Error: Resolution error: Resolution error: Resolution error: All principals in a PolicyStatement must have the same Conditions (got '{}' and '{"StringEquals":{"aws:SourceAccount":"${Token[AWS.AccountId.9]}"}}'). Use multiple statements instead..
これ、同一スタック内で同じロールを複数の Scheduler 間で共用しようとしてエラーになっているのでしょうか?
その2
次のように 1 つの Scheduler を構成しようとした場合は、もちろんデプロイ含め成功します。
lib/cdk-sample-stack.ts
import { aws_lambda_nodejs, aws_lambda, Stack, TimeZone } from 'aws-cdk-lib';
import * as aws_scheduler_alpha from '@aws-cdk/aws-scheduler-alpha';
import * as aws_scheduler_targets_alpha from '@aws-cdk/aws-scheduler-targets-alpha';
import { Construct } from 'constructs';
export class CdkSampleStack extends Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
// Lambda 関数
const helloFunc = new aws_lambda_nodejs.NodejsFunction(this, 'HelloFunc', {
runtime: aws_lambda.Runtime.NODEJS_20_X,
architecture: aws_lambda.Architecture.ARM_64,
});
// Lambda 実行ターゲット
const target = new aws_scheduler_targets_alpha.LambdaInvoke(helloFunc, {});
// Scheduler その1
new aws_scheduler_alpha.Schedule(this, 'Shedule1', {
schedule: aws_scheduler_alpha.ScheduleExpression.cron({
minute: '0',
hour: '1',
month: '3',
day: '1-31',
year: '2024',
timeZone: TimeZone.ASIA_TOKYO,
}),
target,
});
}
}
そして作成された SchedulerRoleForTarget の Trust Relationship は次のようになっていました。
$ aws iam get-role --role-name $SchedulerRoleForTargetName --query 'Role.AssumeRolePolicyDocument'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "scheduler.amazonaws.com"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "XXXXXXXXXXXX"
}
}
}
]
}
上記のステートメントの Condition
の記述は、
(got '{}' and '{"StringEquals":{"aws:SourceAccount":"${Token[AWS.AccountId.9]}"}}')
とあるうちの後者 {"StringEquals":{"aws:SourceAccount":"${Token[AWS.AccountId.9]}"}}
に該当します。そして事象が発生したパターンでは上記に加えて 2 つ目の {}
が追加されようとして、両者が異なっているためエラーとなったようです。
そこで role プロパティを明示的に指定することにより、冒頭に示した通りエラーを回避できました。
おわりに
AWS CDK の alpha module を利用して Amazon EventBridge Scheduler を構成する際に、 Resolution error: All principals in a PolicyStatement must have the same Conditions
というエラーが発生したので対処した内容を共有しました。
バグのような挙動なので、今後修正される可能性もありますが、現時点では上記のような対処が必要です。注意しつつ活用していきましょう。
参考
以上