Systems Manager Automation Runbook 作成の勘所

## はじめに

こんにちは、NTTテクノクロスでAWSの開発エンジニアをやらせていただいています。だはです。
元教員として、初心者目線から色々な記事を書かせていただいています。
AWSを利用した開発エンジニアとして働き始めて1年、そろそろ技術記事に挑戦してみようと思ったこの頃です。
ちなみに今回は[NTTテクノクロスアドベントカレンダーシリーズ2](https://qiita.com/advent-calendar/2024/ntt-tx)の1日目となります。
業務的にはIaC (Infrastructure as Code)に近い技術を触ることが多いので、細かなサービスごとの差異に着目してみたり、便利な使い方をメモに残してみたりしようと思っています。
第一弾は「(CloudFormationを使い慣れてきた人から見た) Systems Manager Automation Runbookテンプレート作成の勘所」を書いてみようと思います。
内容としては、先日[JAWS-UG東京で発表させていただいたもの](https://www.youtube.com/watch?v=IkyhOZjO6xA)と同じになります。
音声の方が情報入るという方は、是非上記リンクもご覧ください。

## 目次

[1.概要](#1概要)
[2.ハマりどころや推せるポイント](#2ハマりどころや推せるポイント)
[　2-1.構文](#2-1構文)
[　2-2.咄嗟に必要になる正規表現](#2-2咄嗟に必要になる正規表現)
[　2-3.実行結果の確認方法](#2-3実行結果の確認方法)
[　2-4.処理の途中で型変換](#2-4処理の途中で型変換)
[　2-5.二種類の書き方](#2-5二種類の書き方)
[　2-6.たまに出る謎のエラー](#2-6たまに出る謎のエラー)
[3.まとめ](#3まとめ)
[4.あとがき](#4あとがき)

## 1.概要

ここでお伝えしたいのは、大きく分けて3点です。

#### どんなサービスなのか
まず今回の主題であるサービスをご紹介します。
AWSには["Systems Manager"](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/what-is-systems-manager.html)というサービスがあります。「（仮想、実在、問わず）EC2を通してなんやかんやする」というもの、だと私は解釈しています。
その中の["Automation"](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-automation.html)というサービスについて、["Runbook"](https://docs.aws.amazon.com/ja_jp/systems-manager-automation-runbooks/latest/userguide/automation-runbook-reference.html)というサービスを通じて、一定の動作をするように定義することができます。
興味のある方は[公式のリファレンス](https://docs.aws.amazon.com/ja_jp/systems-manager-automation-runbooks/latest/userguide/automation-runbook-reference.html)を閲覧することで、Runbookでどんなことができるかは確認できるかと思います。

#### どんなことをしてきたのか
私は上記のサービスを使って、社内AWS利用手続きの簡素化に取り組みました。
例えばEC2インスタンスを作りたい人がいたとしたら、
① Excelシートでパラメータ決めてもらって
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/edbdd2ca-2eab-aaff-cfe0-699c04466f54.png)
② 関数組んでURLを生成して
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/7183d006-91d9-e9c5-98e2-944c352075fd.png)
③ URL先にジャンプしたら"Execute"をクリックするだけ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/074061d5-3916-54ca-d9a5-322977d58f89.png)
...と、いったことに取り組んだ結果、色々学ぶことができました。

#### どんな話をしたいのか
次に本記事の概要です。[「はじめに」](#はじめに)でもご紹介したように、今回は「(CloudFormationを使い慣れてきた人から見た)Systems Manager Automation Runbookテンプレート作成の勘所」を書いていきます。
以下、"CloudFormation"は"Cfn"と、"Systems Manager Automation Runbook"は"Runbook"と表現していきます。ご留意ください。
目次で大まかな内容は想像がつくかと思いますが、実際にRunbookテンプレートを作成していくにあたって「ここは苦戦したから残しておきたいな」「こう使うと便利だな」と感じた部分を書きました。
細かい型定義の差異やコード記述方法の使い分けなど、Runbookを触っている方向けの記事になっていると思っています。

[(目次へ)](#目次)

## 2.ハマりどころや推せるポイント

### 2-1.構文

これ地味に効いてくるのですが、他のStepやParametersで受け取っておいた値を引用しようとすると、書き方が特殊すぎてイライラします。
CloudFormationだと何も考えずに宣言した関数（みたいなもの）を書けばいいと思います。
一方で、Runbookは下記のように書く必要があります。
![ssm③.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/e39b0a53-c8b0-d5f6-e56a-44ad15332b4f.png)
CloudFormationだと

<details open><summary>Cfnコード</summary>

```rb

Values:
  - SubnetAvailabilityZone

```

</details>
でいいところを、Runbookでは
<details open><summary>Runbookコード</summary>

```rb

Values:
  - '{{ SubnetAvailabilityZone }}'

```

</details>
と、書かされます。該当箇所を空白で囲み、二重中括弧で囲み、シングルクォートで囲む必要があるのです。

更に追い打ちをかけるのが、宣言です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/951245bf-4e17-9691-9b38-1af4ec6793ec.png)
上の画像をよく見てほしいのですが、すべて小文字で始まります。
しかし単語の組み合わせが発生しているものに関しては、途中で大文字を挟む必要があります。

まるでボディブローのようにジワジワきます。私の引っかかったエラーは、6割が上記の構文ミスでした。

[(目次へ)](#目次)

### 2-2.咄嗟に必要になる正規表現

次に個人的な備忘録としても残しておきたいのが、正規表現です。
これは私の経験不足でもあるのですが、Runbookを触ることになって、初めて正規表現を使わされました。
![ssm⑤.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/84142846-77af-7c43-cdd2-39b699b69f09.png)
利用タイミングは、各Stepで出力される値を、別のStepで利用したいときが該当します。
Outputを書くことになるのですが、例で"$.Payload.message"とあります。
初心者は「"$"ってなんぞや。Excelの絶対参照か？」となるはずです。私はなりました。
これは正規表現を用いて、各種APIの出力文を抜き出すための場所なようです。
[リファレンス](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html)では以下のように、各種APIで出力される文章のサンプルがあります。
![スクリーンショット 2024-09-22 17.18.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/51659a82-6652-8f4f-0c99-c77a1b3546ca.png)
そのうち必要なものを抜き出すために、正規表現が必要だそうです。大変。
今回はあくせくしながら書きましたが、未だに理解できている実感はありません。恐ろしや。

[(目次へ)](#目次)

### 2-3.実行結果の確認方法

Runbookの実行において便利でもあり、ちょっと不便でもあるのが、実行結果の確認方法です。
結論から述べると、「Runbookを実行する」項目と「Runbookの実行結果を確認する」項目は別サービスの扱いに見える、ということです。
Runbookを作成して、実行する項目は下記のような画面になります。
![スクリーンショット 2024-09-06 21.51.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/703d17f5-abb0-c42c-59a1-3b7d101784ed.png)
ちなみにランブックオートメーションを実行すると、下記のような画面に遷移していきます。
![スクリーンショット 2024-09-06 18.50.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/c7724b82-fa47-6de0-bc7f-c55f2df138c3.png)
当然ですが、Runbookの実行直後は結果を追いかけることができます。
一方で、少し間を置いて（他サービスを閲覧するなどして）実行結果を確認したい場合、上記のサービス項目から探すことはできません。
個人的には、RunbookはAutomationサービスの動きを定義してあげているに過ぎないので、下記のような画面を遷移する必要がある、という解釈です。
![スクリーンショット 2024-09-06 18.54.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/805c847d-bf45-2e34-b329-02701c9da943.png)
最初は戸惑いがありましたが、Cfnとは違って結果だけをシンプルに確認していけるので、これはこれでありかな、という感想です。

[(目次へ)](#目次)

### 2-4.処理の途中で型変換

Runbookテンプレート作成で最も長い間躓いていたのは、ParametersのList化です。
AWSのIaCはCloudFormationテンプレートの作成から入ったので、Runbookの習熟には少し混乱が生じました。
私が混乱した差異を列挙すると

* Cfnでは整数型を"Number"で表現するのに対して、Runbookでは"Integer"と表現すること
* 文字列型のList化は可能だが、他の型のList化は不可能であること
* Runbookテンプレート内にちょっとした条件文やPythonコード実行処理を挟むことで、やりたいことは大体実現できること
* 他サービスとの連携を、API単位で記述すること（リソースの作成削除、とは考え方が違うこと）

...と、いう点でした。
さて、Cfnテンプレート作成の際は、時折「EC2インスタンスを自動的に止める時間」や「CloudWatchの閾値」をParameter化することがあったのではないでしょうか。
またはブール値のパラメータをユーザーに選択させたい、ということは無いでしょうか。
私はそんな時、ユーザビリティを考慮してList化することが多いです(クリックで選択でき、謎のスペルミスでエラーが起きることを回避するため)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/4f33836e-71ef-e433-964c-4757987e141d.png)
これを
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/c7e80099-054c-85f4-2c51-ffb7b53965eb.png)
こうしたいわけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/043b08f5-8492-a70d-2f7d-7119e72b30a8.png)
が、Runbookでは整数型やブール型をList化できない、という欠点があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/b8808e94-eb6d-6a6b-7303-1defecb88589.png)
こう書いても
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/5ed3332d-bd18-4271-8c91-78aa3eb831b7.png)
こうなります。悲しい。
そこで取り組んだのが、
① 一旦文字列型でList化し、ユーザーにParameter入力してもらう
② executeScriptのstepを設けて、Pythonコードで型を変換する
③ outputしてじっくり料理する
と、いう方策です。
該当部分のコードを書いてみると、以下のような内容になります。

:::note warn
今回のコードはだいぶ前に書いたものでして、EOLとされているPython3.8です。
利用する際はPython3.11での動作を確認することを推奨します。
ご留意ください。
:::

<details><summary>今回のコード（クリックで展開）</summary>

```rb

schemaVersion: "0.3"

description: |-
  test
  test2

parameters:
  TimeForAutoStopInstance:
    type: String
    default: true
    allowedValues:
      - 1
      - 2

mainSteps:
    - name: ConvertStringToInteger
    action: aws:executeScript
    nextStep: Hogehoge
    isEnd: false
    inputs:
      Runtime: python3.8
      Handler: script_handler
      Script: |-
        def script_handler(events, context):
          test_str = events["StrParameters"]
          test_int = str(test_str)
          return{"test_int": test_int}
      InputPayload:
        StrParameters: "{{ TimeForAutoStopInstance }}"
    outputs:
      - Type: Integer
        Name: Autostop
        Selector: $.Payload.test_int

```

</details>

細かく解説していくと、

<details open><summary>Parametersの項目では、List化した値をユーザーに選んでもらう

</summary>

```rb

parameters:
  TimeForAutoStopInstance:
    type: String
    default: true
    allowedValues:
      - 1
      - 2

```

</details>

<details open><summary>"executeScript"のStepを作り、Pythonコードにて型変換させてあげる

</summary>

```rb

    - name: ConvertStringToInteger
    action: aws:executeScript
    nextStep: Hogehoge
    isEnd: false
    inputs:
      Runtime: python3.8
      Handler: script_handler
      Script: |-
        def script_handler(events, context):
          test_str = events["StrParameters"]
          test_int = str(test_str)
          return{"test_int": test_int}
      InputPayload:
        StrParameters: "{{ TimeForAutoStopInstance }}"

```

</details>

<details open><summary>型変換したStepで新たにOutputし、後で好きなように料理する

</summary>

```rb

    outputs:
      - Type: Integer
        Name: Autostop
        Selector: $.Payload.test_str

```

</details>

以上の段階を踏むことで、ユーザビリティ向上や、Parameterの設定ミスというリスク回避をすることができました。

[(目次へ)](#目次)

### 2-5.二種類の書き方

Runbook作成の際に最も感動したのは、パズルを組み合わせるだけでコードを書ける(書いてくれる)、ということです。
ここ一番の感動ポイントです。大事なことなのでもう一度言います(書きます)。
「**可視化しながらコードを書ける**」んです。

#### 「コード」モード
例えばEC2インスタンスを立ち上げさせたい時、"EC2:LaunchInstance"というAPIを処理させると思います。
YAMLコードで書いていくと、下記の要領でリファレンスなどを調べながら記述することになるでしょう。
![スクリーンショット 2024-09-06 22.07.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/69ec6b9a-016d-58ad-ff0b-4cf033049c01.png)
これはこれで普通にコード書けます。当然、ローカルにとっておいた過去の成果物をコピペしたり逆も然りで自由です。

エラー検知もしてくれます。たまに変なところを指すのは最早可愛げすらあります。

真ん中の上の方に「レコメンデーション」とありますが、「こうした方がいいよ」というのをこっそり教えてくれたりもします。

#### 「デザイン」モード

上記に加えて、Step単位でドラッグ＆ドロップしていくことで、Runbookを作成していくこともできるのです。
![スクリーンショット 2024-09-06 22.07.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/697d1515-950e-c39a-824a-feafedaf5d6e.png)
この「デザイン」機能ですが、既に多くのテンプレートを書いている方は「コード」機能にコピペで済むので、メリットは少ないかもしれません。
ちなみにちょっと不満なのは、なんだかんだで細かいパラメータの設定は、手書きなところ。例としてはこんな感じです。先ほどと同じ画像を使っています。
![ssm③.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/47c5601a-aacc-ff1a-f990-635cfc377cf4.png)
ただ繰り返しますが、「デザイン」機能は個人的には書き終わったものに対しても、処理の流れや分岐を視覚化できるので、とても助かる機能ではあります。
あと今振り返って思えば、AWSのAPI一覧をアイコンで眺めていられるのも楽しかったです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/7003cf24-ad9d-18ff-0a4d-4c23d961869f.png)
小学生の頃、国語辞典や生物図鑑を眺めていたような気持ちになれます。

[(目次へ)](#目次)

### 2-6.たまに出る謎のエラー

おそらくこれは本件に限った話では無いですが、明らかに正しい箇所に赤い波線が生じていることがあります。
カーソルを合わせてみても、見当違いのことを言われることもしばしば。
普段APIを叩くとやけに細かいエラー文が確認できる分、こういうところで戸惑う人がいるかもしれませんね。ちなみに私は戸惑います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2773852/a26f6d6c-9392-d5fb-3071-110fb4d9f735.png)
細かいユースケース1つ1つに対応するのは難しいですが、こういう時は大体全然関係ないところが書き足りなかったり、スペルミスだったりします。
結論としては、「赤波線は素直に信用しない」というところでしょうか。

[(目次へ)](#目次)

## 3.まとめ

* 作るとき
そもそもこの記事の主題は「CloudFormationを書き慣れてきた人がRunbookを書くとどう感じるか」というものでした。この視点で考えると、細かな構文の差異だったり、エラーの示し方だったりで戸惑うんじゃないかな、と思っています。(エディタ用意してもらってる身で言うのもおこがましいとは思いますが。)
逆に、GUIチックにコードを操作できる場面も存在するので、そこは美味しい点だけ活かして使っていければ良いんじゃないかな、とは思っています。知らないAPIを眺めていられるというのも、初心者には嬉しいポイントです。

<br>

* 使うとき
APIに様々な処理を組み合わせて使うことができます。
ループや条件分岐、スリープは本記事に書いた通りです。
またこれも書いた通りですが、途中で小規模なpythonコードを動作させることもできます。
パラメータ入力には工夫の余地があるかと思います。Excelなんかと組み合わせることで、エンドユーザーがAWSを使いやすくする、なんてのは良い例になるかと考えています。


今後も諦めずに触って、より良い使い方を見つけていきたいところです。

## 4.あとがき

今回は初心者目線でRunbookの使い易さ使い辛さを残していくことで、技術的なノウハウを残す記事を書いてみました。これからRunbookを触る方の一助になれば幸いです。

以上NTTテクノクロスアドベントカレンダーの1日目、だはがお送りしました。
弊社に興味を持っていただけた方は、[こちら](https://www.ntt-tx.co.jp/)をご覧ください。
明日は[@kn-tom](https://qiita.com/kn-tom)さんの記事「pg_repack を使ったデータベース再編成運用の注意点」になります。どうぞお楽しみに！Merry Christmas！