AWS Systems Manager Change Calendarに祝日や営業時間を設定してアプリケーションから利用する

アプリケーションが利用する「カレンダー」の実現パターンの一つとして、選択肢に加えてもらえるでしょうか?
2022.04.30

みなさん、こんにちは!
福岡オフィスの青柳です。

今回は「AWS Systems Manager」の機能の一つである「Change Calendar」の便利な使い方を紹介したいと思います。

Systems Manager Change Calendarとは

Systems Manager Change Calendarとは、Systems Manager上で定義・管理できる「カレンダー」です。(そのまんまですね)

Change Calendarには、次のような利用の場面があります。

Systems Managerでカレンダーを利用する

Change Calendarを使うことで、Systems Managerの「Automation」や「Run Command」をスケジュールに従って実行することができます。

また、Systems Managerの「Change Manager」で変更を適用する際に、Change Calendarを使って「重要なバッチ処理が行われる時間帯」や「負荷が集中する時間帯」などを設定することにより、これらの時間帯を避けて適用を実施するという使い方もあります。

EventBridgeでカレンダーを利用する

Change Calendarは、Systems Manager以外のAWSサービスでも利用することができます。

例えば、Amazon EventBridgeのイベントソースとしてChange Calendarを指定することができます。
これにより、Change Calendarのカレンダーに定義されたスケジュールに従って、EventBridgeを介して様々な処理へ連係することができます。

例として、スケジュールに従って「Amazon WorkSpaces」の設定を切り替える仕組みを作ってみました。

Change Calendarからのイベントを検知するようにEventBridgeを設定する方法や、EventBridgeからLambda関数を呼び出して処理を行う方法について解説していますので、参考にしてください。

AWS APIを使って様々なAWSサービスからカレンダーを利用する

Change Calendarには、定義したカレンダーにおいて「特定の日時」が「稼働日 (稼働時間帯)」なのか「非稼働日 (非稼働時間帯)」なのかを判定する「GetCalendarState」というAPIが用意されています。
このAPIを使うことで、Systems ManagerやEventBridgeの他にも、様々なAWSサービスからChange Calendarを利用することができます。

例として、下記のブログ記事では、AWS Step FunctionsからChange CalendarのAPIを使ってスケジュール処理を行う手法を紹介しています。

今回のブログ記事では、この「AWS APIを使ってChange Calendarを利用する」の応用として、AWS APIがベースとなっている「AWS CLI」や「AWS SDK」を使ってChange Calendarを利用する方法をご紹介したいと思います。

その前に、まずは準備から行っていきましょう。

Change Calendarの「カレンダー」を作成する

Change Calendarの利用を開始する際には、まず「カレンダー」を作成します。

カレンダーを作成する際、以下の2種類の属性のいずれかを指定します。

  • デフォルトが「OPEN」のカレンダー
  • デフォルトが「CLOSED」のカレンダー

「OPEN」「CLOSED」って何だ?と思われるかもしれませんが、とりあえず、

  • OPEN: 「営業日・営業時間帯」「稼働日・稼働時間帯」
  • CLOSED: 「休業日・営業時間外」「非稼働日・停止時間帯」

と読み替えておいてください。

もう少しイメージし易いように、具体的な例を挙げて説明します。

例1) デフォルトが「OPEN」のカレンダーを使って「国民の祝日」を定義する

まず、新しいカレンダーを作成して「デフォルトで開く」(DEFAULT_OPEN) を選択します。

作成したカレンダー上に、「祝日」を「イベント」として設定していきます。

※ 「祝日」は終日のものですので、上図のように「5月3日」を設定する際には「05/03 00:00:00 ~ 05/04 00:00:00」と指定する必要があります。

これを繰り返すことで、「祝日」の情報が定義されたカレンダーを作成します。

このカレンダーは、イベントが定義されていない日は「営業日」つまり「OPEN」であり、イベントが定義されている日は「休業日」つまり「CLOSED」となります。(とりあえず「土日」の存在は忘れてください)

このように、原則は「営業日 (営業時間帯)」であるが、特定の日付・時間帯を「休業日 (営業時間外)」にしたい場合に、「DEFAULT_OPEN」のカレンダーを使います。

~~~

なお、ここでは説明のために祝日を一つずつ手動で登録していますが、これだと大変ですので、「Google Calendar」など既存のカレンダーからインポートする方法をお勧めします。

例2) デフォルトが「CLOSED」のカレンダーを使って「営業時間帯」を定義する

新しいカレンダーを作成して、今度は「デフォルトで閉じる」(DEFAULT_CLOSED) を選択します。

作成したカレンダー上に、「営業時間帯」を「イベント」として設定していきます。

上図のように、毎日・毎週・毎月・・・と繰り返されるイベントを定義することもできます。(ここでは「毎週」「月・火・水・木・金曜日」の「9:00~18:00」という設定を行いました)

※ 繰り返しイベントの開始日である5月2日にイベントが重複して登録されていますが、そういう仕様であるようです。動作には影響ないので無視してください。

このカレンダーは、イベントが定義されていない時間帯は「営業時間外」つまり「CLOSED」であり、イベントが定義されている時間帯は「営業時間帯」つまり「OPEN」となります。

このように、原則は「休業日 (営業時間外)」であるが、特定の日付・時間帯を「営業日 (営業時間帯)」にしたい場合に、「DEFAULT_CLOSED」のカレンダーを使います。

AWS CLIを使ってカレンダーの動作を確認する

作成したカレンダーが「営業日」「休業日」や「営業時間帯」「営業時間外」を意図した通りに制御できるのか、確認したいと思います。

実際にSystems ManagerのAutomationでChange Calendarを使ったりせずとも、AWS CLIを使って確認することができます。

カレンダーを使った「営業日」「休業日」の判定を行う

「国民の祝日」を定義したカレンダーを使って、指定した日付が「営業日」なのか「休業日」なのかを判定してみます。

$ aws ssm get-calendar-state \
    --calendar-names "arn:aws:ssm:ap-northeast-1:123456789012:document/japan-national-holidays-calendar" \
    --at-time "2022-05-01T00:00:00+09:00"

このコマンドによって、カレンダー「japan-national-holidays-calendar」を使って、指定した日時「2022-05-01 00:00:00」(日本時間) が「OPEN」であるのか「CLOSED」であるのかを判定させています。
(注意: 日時の指定は、厳密な「ISO 8601」の書式に準拠している必要があります)

以下のように結果が出力されたと思います。

{
    "State": "OPEN",
    "AtTime": "2022-04-30T15:00:00Z",
    "NextTransitionTime": "2022-05-02T15:00:00Z"
}

2022年5月1日は祝日ではありませんので「OPEN」が返ってきました。(結果に表示されている"AtTime"はUTC時間なので気を付けてください)

では、コマンドに与える「日時」を変えてみると、どうでしょう?

$ aws ssm get-calendar-state \
    --calendar-names "arn:aws:ssm:ap-northeast-1:123456789012:document/japan-national-holidays-calendar" \
    --at-time "2022-05-03T00:00:00+09:00"

今度は、以下のように結果が出力されたと思います。

{
    "State": "CLOSED",
    "AtTime": "2022-05-02T15:00:00Z",
    "NextTransitionTime": "2022-05-05T15:00:00Z"
}

2022年5月3日は祝日ですので「CLOSED」が返ってきました。

複数のカレンダーを組み合わせて利用する

複数のカレンダーを組み合わせて「OPEN」「CLOSED」の判定を行うことができます。

例えば、上の例で作成した「祝日」カレンダーと「営業時間帯」カレンダーを組み合わせてみます。

$ aws ssm get-calendar-state \
    --calendar-names "arn:aws:ssm:ap-northeast-1:123456789012:document/japan-national-holidays-calendar" \
                     "arn:aws:ssm:ap-northeast-1:123456789012:document/business-hours-calendar" \
    --at-time "2022-05-02T06:00:00+09:00"
{
    "State": "CLOSED",
    "AtTime": "2022-05-01T21:00:00Z",
    "NextTransitionTime": "2022-05-02T00:00:00Z"
}

2022年5月2日は平日ですが、「06:00」は営業時間外ですので「CLOSED」が返ってきます。

続いて、同じ日付で「13:00」を指定してみます。

$ aws ssm get-calendar-state \
    --calendar-names "arn:aws:ssm:ap-northeast-1:123456789012:document/japan-national-holidays-calendar" \
                     "arn:aws:ssm:ap-northeast-1:123456789012:document/business-hours-calendar" \
    --at-time "2022-05-02T13:00:00+09:00"
{
    "State": "OPEN",
    "AtTime": "2022-05-02T04:00:00Z",
    "NextTransitionTime": "2022-05-02T09:00:00Z"
}

「13:00」は営業時間帯ですので、「OPEN」が返ってきます。

では、これではどうでしょう?

$ aws ssm get-calendar-state \
    --calendar-names "arn:aws:ssm:ap-northeast-1:123456789012:document/japan-national-holidays-calendar" \
                     "arn:aws:ssm:ap-northeast-1:123456789012:document/business-hours-calendar" \
    --at-time "2022-05-03T13:00:00+09:00"
{
    "State": "CLOSED",
    "AtTime": "2022-05-03T04:00:00Z",
    "NextTransitionTime": "2022-05-06T00:00:00Z"
}

「2022年5月3日」は祝日ですので、13:00を指定していても「CLOSED」が返ってきます。

このように、複数のカレンダーを指定して「OPEN」「CLOSED」の判定を行うことができます。

なお、複数のカレンダーを指定した場合の判定基準は、以下のようになっています。

  • 全てのカレンダーにおいて「OPEN」と判定できる場合 → 結果は「OPEN」となる
  • どれか一つのカレンダーでも「CLOSED」と判定された場合 → 結果は「CLOSED」となる

複数のカレンダーを使うことで、例えば「国民の祝日」カレンダーと「特別休業日 (年末年始など)」カレンダーを組み合わせて営業日の判定を行ったりすることができます。

なお、判定のメカニズムの仕様上、例えば「国民の祝日は原則として休業日であるが、ゴールデンウィークは『かき入れ時』なので臨時営業日とする」といった判定は、上記の手法では実現することができません。
その場合には、会社特有の「営業日カレンダー」(あるいは「休業日カレンダー」) を用意して、祝日や臨時営業日を反映することになります。

AWS SDKを使ってアプリケーションからカレンダーを利用する

AWS CLIに続いて、「AWS SDK」を使ったChange Calendarの利用を試してみます。

今回は、Lambda関数でAWS SDK for Pythonを使ったサンプルを作成することにします。

まず、Lambda関数がChange Calendarへアクセスできるように、IAMロール (Lambda実行ロール) を用意します。
以下のIAMポリシーを作成して、Lambda実行ロールへアタッチします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:GetCalendarState"
            ],
            "Resource": "*"
        }
    ]
}

Lambda関数を作成して、コードを記述します。

AWS SDK for Python (Boto3) の「SSM.Client」クラスの「get_calendar_state」メソッドを使います。
SSM — Boto3 Docs 1.21.46 documentation

client = boto3.client('ssm')

response = client.get_calendar_state(
    CalendarNames=[
        '<カレンダーの名前またはARN>',
        '<カレンダーの名前またはARN>',
        ・・・
    ],
    AtTime='<判定対象の日付時刻 (省略時は現在時刻を使う)>'
)

パラメーターの指定方法はAWS CLIの時と似ています。

パラメーター 説明
CalendarNames 参照するカレンダーを名前またはARNで指定します。
複数の指定が可能ですが、省略はできません。
AtTime 判定対象の日付時刻をISO 8601形式で指定します。
省略時は現在時刻を指定したことになります。

なお、AWS CLIではカレンダーの指定は「ARN」で行う必要がありましたが、AWS SDK for Pythonでは「名前」「ARN」のいずれでも指定可能です。

サンプルコードは以下のようになりました。

import boto3

def lambda_handler(event, context):
    client = boto3.client('ssm')

    # イベントデータからパラメーターを取得
    param_calendars = event.get('calendars')
    param_datetime = event.get('datetime', '')
    print('Parameter "calendars":', param_calendars)
    print('Parameter "datetime":', param_datetime)

    # パラメーター"datetime"の指定有無によってパラメーターの数を変える
    if param_datetime != '':
        # SSM Change Calendarを使って、指定したカレンダーにおける指定した日時の「状態」を取得する
        response = client.get_calendar_state(
            CalendarNames=param_calendars,
            AtTime=param_datetime
        )
        print('Response from SSM Change Calendar:', response)
    else:
        # SSM Change Calendarを使って、指定したカレンダーにおける現在時刻の「状態」を取得する
        response = client.get_calendar_state(
            CalendarNames=param_calendars
        )
        print('Response from SSM Change Calendar:', response)

    return response

Lambda関数をテストしてみます。

次のようなイベントデータを設定して、Lambda関数を実行します。

{
  "calendars": [
    "japan-national-holidays-calendar"
  ],
  "datetime": "2022-05-01T00:00:00+09:00"
}

結果は以下のようになりました。

{
  "State": "OPEN",
  "AtTime": "2022-04-30T15:00:00Z",
  "NextTransitionTime": "2022-05-02T15:00:00Z",
  "ResponseMetadata": {
    "RequestId": "41f5f93a-d7de-4e24-bcc8-9674b94a9c65",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "server": "Server",
      "date": "Sun, 24 Apr 2022 08:17:55 GMT",
      "content-type": "application/x-amz-json-1.1",
      "content-length": "92",
      "connection": "keep-alive",
      "x-amzn-requestid": "41f5f93a-d7de-4e24-bcc8-9674b94a9c65"
    },
    "RetryAttempts": 0
  }
}

他にもいくつかテストデータを用意してみます。

テストデータ2: 日時を指定しないパターン

{
  "calendars": [
    "japan-national-holidays-calendar"
  ]
}

テストデータ3: 複数のカレンダーを指定したパターン

{
  "calendars": [
    "japan-national-holidays-calendar",
    "business-hours-calendar"
  ],
  "datetime": "2022-05-01T00:00:00+09:00"
}

実行結果は示しませんので、実際に試してみてください。

おわりに

今回は、Systems Manager Change Calendarのカレンダーを作成して、AWS CLIやAWS SDKから利用する方法について紹介しました。

AWS SDKのサンプルは「Python」「Lambda」を使いましたが、他のプログラム言語やプラットフォームでも同様にして利用できると思います。

Change Calendarをアプリケーションから利用する利点として、以下のようなことが考えられます。

  • Systems Managerの「Automation」「Run Command」などで使用するカレンダーと同じものをアプリケーションも利用することで、管理が一元的できる。
  • 独自にデータベースなどを使ってカレンダーを構築しなくても、AWS標準の機能によって実現することができる。
  • 手動ではあるがGoogle Calendarなどからカレンダーのインポートが行えるため、「祝日カレンダー」などが手間をかけずに利用できる。

もちろん、アプリケーションが利用するカレンダーとしてどの方法を用いるのかについては、他にもいろいろな観点があると思います。
今回の手法を選択肢の一つとして考えて頂ければと思います。