![[AWS Step Functions] AWS Systems Manager Change Calendarと連携して定期実行処理のイレギュラーケースに対応してみた](https://devio2023-media.developers.io/wp-content/uploads/2019/04/aws-step-functions.png)
[AWS Step Functions] AWS Systems Manager Change Calendarと連携して定期実行処理のイレギュラーケースに対応してみた
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWS Step Functionsでもイレギュラーケースに対応したい
こんにちは、のんピ(@non____97)です。
皆さんはジョブ管理システムから抜け出したいと思ったことはありますか? 私は常に思っています。
普段は定期実行しているジョブネットでも、「休日の時は実行しないで欲しい」というケースがあると思います。また、「普段は実行しないけど、締め日だけ実行して欲しい」といったケースもあるでしょう。このようなイレギュラーなケースをEventBridgeのCrons式で表現することは難しいです。
しかし、そのような場合でもAWS Step FunctionsとAWS Systems Manager Change Calendar(以降SSM Change Calendar)を連携すれば対応できるんです。
今回はジョブ管理システムをAWS Step Functionsに置き換えることを想定して、定期実行処理のイレギュラーケースに対応してみます。
いきなりまとめ
- AWS Step FunctionsとAWS Systems Manager Change Calendarを連携すれば、Cron式では表現しづらいイレギュラーケースにも対応できる
ANDやORで複数のSSM Change Calendarを組み合わせて条件を決めたい場合は、以下のように設定するAND:GetCalendarStateAPIのCalendarNamesパラメーターに複数のSSM Change CalendarのARNを指定するOR:PararellでGetCalendarStateAPIを並列で呼び出し、後続のChoiceでGetCalendarStateAPIの結果を評価する
検証の概要
SSM Change Calendarは柔軟な定期処理実行を実現するための機能です。
今回は以下2つのパターンのイレギュラーな定期実行処理を、AWS Step FunctionsとSSM Change Calendarを用いて表現してみます。
- 締め日であれば処理を実行する
- 祝日であれば処理を実行しない
AWS Step FunctionsとSSM Change Calendarの連携方法は、ステートマシンからSSM Change Calendarの GetCalendarState APIを呼び出し、その結果がOPENかCLOSEDかで判断します。
OPENであれば後続の処理を実行し、CLOSEDであれば後続の処理をスキップします。
やってみた
SSM Change Calendarの作成
まずは、SSM Change Calendarを作成します。カレンダーは締め日用と祝日用の2つ用意します。
締め日用のカレンダーは「締め日であれば処理を実行する」ためのカレンダーです。祝日用のカレンダーは「祝日であれば処理を実行しない」ためのカレンダーです。
締め日用のカレンダーから作成します。
Systems Managerのコンソールからカレンダーの変更 - カレンダーを作成をクリックします。

次に、カレンダーの名前と説明、カレンダータイプを選択し、カレンダーを作成をクリックします。
カレンダータイプはDEFAULT_OPENとDEFAULT_CLOSEDとがあり、以下のように使い分けます。
- 指定した日に実行させたくないのであれば
DEFAULT_OPENを選択 - 指定した日に実行させたいのであれば
DEFAULT_CLOSEDを選択
締め日用のカレンダーは「締め日であれば処理を実行する」ためのカレンダーなので、カレンダータイプはDEFAULT_CLOSEDを選択します。

カレンダーを作成をクリックすると、何もイベントの入っていない、まっさらなカレンダーが作成されます。
それでは締め日をイベントとして追加します。イベントの追加はイベントを作成をクリックします。

イベント名やイベントスケジュール、タイムゾーンを設定して、予定されたイベントを作成をクリックします。
今回は12/23を締め日として指定しました。

予定されたイベントを作成をクリックすると、カレンダーの12/23にSettlement Closing Dateという締め日を表すイベントが追加されていることを確認できます。

祝日用のカレンダーも同様に作成します。「祝日であれば処理を実行しない」ためのカレンダーであるため、カレンダータイプはDEFAULT_OPENを選択しました。

祝日は12/23として指定しました。

なお、今回はイベントを手動で登録しましたが、以下記事で紹介している通り、GoogleカレンダーやOutlookカレンダーなどで祝日や締め日のカレンダーを用意して、SSM Change Calendarにカレンダーのicsファイルをインポートすることでイベントを追加することもできます。
ステートマシンの作成
締め日用カレンダーの動作確認で使うステートマシン
次にステートマシンを作成します。
まずは締め日用カレンダーの動作確認で使うステートマシンから作成します。
先頭のタスクで GetCalendarState APIを呼び出し、締め日用カレンダーを参照します。

GetCalendarStateAPI実行後は、OPENかCLOSEDかを判定するためにChoiceを使用します。OPEN(= 締め日)であればWait→Successに遷移するようにします。それ以外の場合(= 締め日でない)は、Passに遷移するようにします。

作成したステートマシンのAmazon States Language(以降ASL)は以下の通りです。
{
"Comment": "A description of my state machine",
"StartAt": "GetCalendarState",
"States": {
"GetCalendarState": {
"Type": "Task",
"Parameters": {
"CalendarNames": [
"arn:aws:ssm:us-east-1:<AWSアカウントID>:document/SettlementClosingDate"
]
},
"Resource": "arn:aws:states:::aws-sdk:ssm:getCalendarState",
"Next": "Choice"
},
"Choice": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.State",
"StringEquals": "OPEN",
"Next": "Wait"
}
],
"Default": "Pass"
},
"Wait": {
"Type": "Wait",
"Seconds": 5,
"Next": "Success"
},
"Success": {
"Type": "Succeed"
},
"Pass": {
"Type": "Pass",
"End": true
}
}
}
ステートマシンと一緒に作成されるIAMロールだと権限不足で、以下のように「GetCalendarStateAPIの実行を許可する必要がある」と表示されます。

そのため、以下のようにGetCalendarStateAPIの実行を許可するIAMポリシーを対象のIAMロールに追加します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ssm:GetCalendarState",
"Resource": "arn:aws:ssm:*:<AWSアカウントID>:document/*"
}
]
}
以上で締め日用カレンダーの動作確認で使うステートマシンの作成は完了です。
なお、今回は設定しませんが、締め日が毎月23日から25日の間のいずれかの日の10:00(JST)であれば、EventBridgeのCron式でcron(0 1 23-25 * ? *)を設定し、作成したステートマシンを呼び出すように指定します。
ステートマシン内では締め日用カレンダーに登録されているイベントを参照し条件分岐するため、このように設定することで、23日から25日の中で締め日が変動しても、実際の締め日のみ処理を実行させることができます。
祝日用カレンダーの動作確認で使うステートマシン
続いて、祝日用カレンダーの動作確認で使うステートマシンを作成します。
締め日用カレンダーの動作確認で使うステートマシンと同様に、先頭のタスクでGetCalendarStateAPIを呼び出し、休日用カレンダーを参照します。

Choiceの条件及び、条件分岐先は締め日用カレンダーの動作確認で使うステートマシンのものと同じです。

作成したステートマシンのASLは以下の通りです。
{
"Comment": "A description of my state machine",
"StartAt": "GetCalendarState",
"States": {
"GetCalendarState": {
"Type": "Task",
"Parameters": {
"CalendarNames": [
"arn:aws:ssm:us-east-1:<AWSアカウントID>:document/NationalHoliday"
]
},
"Resource": "arn:aws:states:::aws-sdk:ssm:getCalendarState",
"Next": "Choice"
},
"Choice": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.State",
"StringEquals": "OPEN",
"Next": "Wait"
}
],
"Default": "Pass"
},
"Wait": {
"Type": "Wait",
"Seconds": 5,
"Next": "Success"
},
"Success": {
"Type": "Succeed"
},
"Pass": {
"Type": "Pass",
"End": true
}
}
}
締め日用カレンダーのテスト
締め日用カレンダーのテストを行います。
作成した締め日用カレンダーの動作確認で使うステートマシンを実行すると、実行日が23日であり、カレンダーに登録した締め日の23日と一致しているため、OPENと判定されています。その結果、Choice以降はWait→Successに遷移しています。

祝日用カレンダーのテスト
続いて、祝日用カレンダーのテストを行います。
作成した祝日用カレンダーの動作確認で使うステートマシンを実行すると、実行日が23日であり、カレンダーに登録した祝日の23日と一致しているため、CLOSEDと判定されています。その結果、Choice以降はPassに遷移しています。

複数のカレンダーがある場合も試してみた
ANDの場合
複数のカレンダーがある場合も試してみます。
まず、「締め日は基本実行するけど、休日だったら実行しない」のように2つのカレンダーをANDで評価してみます。
ANDで評価する場合は、GetCalendarStateAPIのCalendarNamesパラメーターに複数のSSM Change CalendarのARNを指定します。

その他の設定は変更ありません。
作成したステートマシンのASLは以下の通りです。
{
"Comment": "A description of my state machine",
"StartAt": "GetCalendarState",
"States": {
"GetCalendarState": {
"Type": "Task",
"Parameters": {
"CalendarNames": [
"arn:aws:ssm:us-east-1:<AWSアカウントID>:document/SettlementClosingDate",
"arn:aws:ssm:us-east-1:<AWSアカウントID>:document/NationalHoliday"
]
},
"Resource": "arn:aws:states:::aws-sdk:ssm:getCalendarState",
"Next": "Choice"
},
"Choice": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.State",
"StringEquals": "OPEN",
"Next": "Wait"
}
],
"Default": "Pass"
},
"Wait": {
"Type": "Wait",
"Seconds": 5,
"Next": "Success"
},
"Success": {
"Type": "Succeed"
},
"Pass": {
"Type": "Pass",
"End": true
}
}
}
カレンダー上では締め日かつ休日でもある23日に、作成したステートマシンを実行すると、意図した通りCLOSEDと判定されています。その結果、Choice以降はPassに遷移しています。

ORの場合
続いて、「締め日であれば休日であっても実行する」のように2つのカレンダーをORで評価してみます。
ORで評価する場合は、PararellでGetCalendarStateAPIを並列で呼び出します。Pararellの実行結果はJSONではなく配列になっているので、ResultPathで整形してあげます。

後続のChoiceではResultPathで整形された各GetCalendarStateAPIの結果をORで評価します。

作成したステートマシンのASLは以下の通りです。
{
"Comment": "A description of my state machine",
"StartAt": "Parallel",
"States": {
"Parallel": {
"Type": "Parallel",
"Next": "Choice",
"Branches": [
{
"StartAt": "GetCalendarState_SettlementClosingDate",
"States": {
"GetCalendarState_SettlementClosingDate": {
"Type": "Task",
"End": true,
"Parameters": {
"CalendarNames": [
"arn:aws:ssm:us-east-1:<AWSアカウントID>:document/SettlementClosingDate"
]
},
"Resource": "arn:aws:states:::aws-sdk:ssm:getCalendarState"
}
}
},
{
"StartAt": "GetCalendarState_NationalHoliday",
"States": {
"GetCalendarState_NationalHoliday": {
"Type": "Task",
"End": true,
"Parameters": {
"CalendarNames": [
"arn:aws:ssm:us-east-1:<AWSアカウントID>:document/NationalHoliday"
]
},
"Resource": "arn:aws:states:::aws-sdk:ssm:getCalendarState"
}
}
}
],
"ResultPath": "$.calendarState"
},
"Choice": {
"Type": "Choice",
"Choices": [
{
"Or": [
{
"Variable": "$.calendarState[0].State",
"StringEquals": "OPEN"
},
{
"Variable": "$.calendarState[1].State",
"StringEquals": "OPEN"
}
],
"Next": "Wait"
}
],
"Default": "Pass"
},
"Wait": {
"Type": "Wait",
"Seconds": 5,
"Next": "Success"
},
"Success": {
"Type": "Succeed"
},
"Pass": {
"Type": "Pass",
"End": true
}
}
}
カレンダー上では締め日かつ休日でもある23日に、作成したステートマシンを実行すると、意図した通りOPENと判定されています。その結果、Choice以降はWait→Successに遷移しています。

またまたジョブ管理システムから抜け出すためのTipsを作りました
AWS Step FunctionsとAWS Systems Manager Change Calendarを連携してCron式では表現しづらいイレギュラーケースに対応してみました。
オンプレ環境でもCron式で表現し切れない定期実行は、実行するシェルスクリプト内でif文などを用いて対応していたと思いますが、イメージとしてはそれに近しいですね。
なお、作成したSSM Change Calendarはアカウント間でも共有できます。そのため、複数アカウントがある場合でもSSM Change Calendar管理用アカウント上のSSM Change Calendarを参照していれば、締め日が変わった場合なども対応の手間が少ないのではと考えます。
また、改めてAWS Step Functionsから直接APIを実行できるようになったのは神アップデートだと実感しました。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!






