CloudFormationの変更セットを見やすい表にしてGitHubにコメントする

# 概要

タイトルの件をGitHub Actionsとシェルスクリプトで実現します。▼
![0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/8d7eaf8c-a655-cefb-31b0-1b9b27fd5721.png)
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/20772ffd-03b4-333f-5554-9a8e3daf3ff9.png)


# 本文

[AWS CloudFormation](https://aws.amazon.com/jp/cloudformation/)（以下 CFn）のマネジメントコンソールでは、変更セットの内容を表で確認することができます。▼
![console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/774a198b-ee8d-33d9-46b8-828d79982974.png)
AWS CLIでも変更セットの内容は取得できます。ただし返ってくるJSONは人間が読める感じではありません。▼

```bash
$ aws cloudformation describe-change-set --change-set-name $changeset_id

{とても長いJSONが返ってくる}
```

そこでこのJSONを**`jq`コマンドでゴニョゴニョし、Markdownの表にします。**さらにそれをGitHubのプルリクエストにコメントします。

## jqでいい感じにする

シェルスクリプトを作ります。これをCI/CDで使います。▼

```output_changeset_as_markdown.sh
# 前略
changeset_id=$1
changeset_json=$(aws cloudformation describe-change-set --change-set-name $changeset_id)
stack_name=$(echo "$changeset_json" | jq -r .StackName)
changes=$(echo "$changeset_json" | jq -r .Changes)
changes_length=$(echo "$changes" | jq length)
echo "<details><summary><code>$stack_name ($changes_length changes)</code></summary>" # クリックで展開できるやつ
echo
if [ $changes_length -gt 0 ]; then
echo '|Action|論理ID|物理ID|リソースタイプ|置換|' # 少しでも横幅を減らすためにActionだけ英語
echo '|---|---|---|---|---|'
for i in $( seq 0 $(($changes_length - 1)) ); do
  row=$(echo "$changes" | jq -r .[$i].ResourceChange)
  col_1=$(echo "$row" | jq -r .Action)
  col_2=$(echo "$row" | jq -r .LogicalResourceId)
  col_3=$(echo "$row" | jq -r .PhysicalResourceId | sed -e 's/null/-/') # nullの場合'-'を表示
  col_4=$(echo "$row" | jq -r .ResourceType | sed -e 's/AWS:://') # リソースタイプの'AWS::'は省略
  col_5=$(echo "$row" | jq -r .Replacement | sed -e 's/null/-/' | sed -e 's/True/\*\*True\*\*/') # nullの場合'-'を表示。Trueなら太字にする
  echo "|$col_1|$col_2|$col_3|$col_4|$col_5|"
done
fi
echo '<ul><li><details><summary>view json</summary>' # インデントを付ける目的でリストにしている
echo
echo '```json'
echo "$changeset_json"
echo '```'
echo '</details></li></ul></details>'
```

## CI/CDでプルリクエストにコメントする

**以下はGitHub Actionsでの例です。**

今回は[sticky-pull-request-comment](https://github.com/marketplace/actions/sticky-pull-request-comment)を使い、プルリクエストのコメントをUpsert（なければInsert,あればUpdate）します。

【参考】[GitHub ActionsでPRに同一種類のコメントをUpsertで追加する方法](https://qiita.com/yokawasa/items/f7fee20fd4a4220f25f8)

ワークフローは以下のようにします。▼

```workflow.yml
# 前略（以下はjobのsteps）
- name: Make comment body
  id: make-cfn-comment
  run: |
    comment_body_file="/tmp/pr_comment_body.txt" # 出力先ファイル

    # コメント本文の作成（$changeset_id_listは変更セットのID一覧）
    for id in $(echo "$changeset_id_list"); do
      ./scripts/output_changeset_as_markdown.sh $id >> ${comment_body_file}
    done

    # タイトルの追加
    comment_title="### CFn change sets"
    sed -i "1i ${comment_title}" ${comment_body_file}

    # 次のステップで使うためにset-output
    echo ::set-output name=header::changeset-comment # 後述
    echo ::set-output name=result::${comment_body_file} # 出力先ファイル


- name: Comment on pull request
  uses: marocchino/sticky-pull-request-comment@v1
  with:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    header: ${{ steps.make-cfn-comment.outputs.header }} # 同じheaderのコメントがすでにある場合は上書きされる
    path: ${{ steps.make-cfn-comment.outputs.result }} # 出力したファイルのパス
```

## 完成したもの

通常はスタック名の一覧だけにすることでコメントの表示サイズが大きくなることを抑えつつ... ▼
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/4978082c-b738-670c-adae-ff3db953930f.png)

スタック名をクリックして表を見られるようにしました。▼
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/20772ffd-03b4-333f-5554-9a8e3daf3ff9.png)

`▶ view json` をクリックして元のJSONを見ることも可能です。▼
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502255/4b949bdd-150c-7d37-14e1-c923dc61e93c.png)

# まとめ

GitHub上でCFnの変更セットを確認できるようになり、大変便利になりました😊