💭

CloudFormation LinterをGitHub Actionsから実行してテンプレートのミスを検知する

2021/04/08に公開

CloudFormation テンプレートを書いているとスペルミスやパラメータミスなどの間違いをしばしば引き起こします。Cloud Formation Linter である cfn-python-lint を活用することで未然にミスを検知できます。

cfn-python-lint とは

AWS が提供している CloudFormation Linter です。テンプレートの typo やパラメータ不足など CloudFormation テンプレートに誤りがないかを検知できます。
https://github.com/aws-cloudformation/cfn-python-lint

VSCode や JetBrains などの IDE の拡張機能も提供されており、テンプレートを書きながらミスに気付くことができます。また、IDE の場合だと上書きを有効にすることで整形もしてくれるのでかなり便利です。
参考: Linterを使ってCloudFormationの間違いに爆速で気づく | DevelopersIO

GitHub Actions から Linter を実行する意義

CloudFormation テンプレートを作成・更新する人全員に cfn-python-lint を使って事前にチェック・整形してくれるのが理想です。しかし、オープンソースとして公開している場合、全員が同じ環境を整えることは難しいです。そのため、GitHub で開発している場合、Pull Request(PR)作成時に Linter を実行することで検知できるしくみが必要となります。
GitHub Actions であれば、簡単に cfn-python-lint を実行できるうえ、下図のように間違えた箇所を GitHub 上のソースコードにアノテーションしてくれるので修正が容易になります。

github-actions-cfn-lint

GitHub Actions のワークフロー作成

ここからは実際に GitHub Actions の設定に入ります。サンプルコードと Linter が動いている PR はこちらになります。
https://github.com/ohsawa0515/ecs-gpu-optimized-ami-auto-update/pull/1

# .github/workflows/cfn-lint.yml
name: "Cloudformation Linter"

on:
  pull_request:
    types: [synchronize, opened]
    paths:
      - "**.yml"

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 5

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: 3.8

      - name: Install cfn-lint
        run: |
          python -m pip install --upgrade pip
          pip install cfn-lint

      - name: Add problem matcher
        run: echo "::add-matcher::.github/cfn-lint.json"

      - name: Lint by cfn-lint
        run: cfn-lint *.yml -f parseable

やっていることはシンプルで pip で cfn-python-lint をインストールして、cfn-lint *.yml -f parseable で Linter を実行しています。-f parseable オプションを渡すことで、指摘項目を1行にしてくれるため、後述の Problem Matcher においてパースしやすくなります。

# -f parseable なし
# 1つの指摘項目に対して2行に分かれるためパースが若干面倒
W2001 Parameter Foo not used.
cloudformation_template.yml:29:3

E3012 Property Resources/CodeBuild/Properties/TimeoutInMinutes should be of type Integer
cloudformation_template.yml:105:7
# -f parseable あり
# 1つの指摘項目に対して1行かつ「:」でセパレートされるのでパースしやすい
cloudformation_template.yml:29:3:29:6:W2001:Parameter Foo not used.
cloudformation_template.yml:105:7:105:23:E3012:Property Resources/CodeBuild/Properties/TimeoutInMinutes should be of type Integer

Problem Matcher の設定

以下のファイルを追加します。regexpcfn-lint *.yml -f parseable の出力結果をパースするための正規表現です。

# .github/cfn-lint.json
{
    "problemMatcher": [
        {
            "owner": "cfn-lint",
            "pattern": [
                {
                    "regexp": "^(\\S+):(\\d+):(\\d+):(\\d+):(\\d+):([EWI]\\d+:.+)$",
                    "file": 1,
                    "line": 2,
                    "column": 3,
                    "message": 6
                }
            ]
        }
    ]
}

ワークフローファイル(.github/workflows/cfn-lint.yml)で以下の記載を入れれば完了です。

- name: Add problem matcher
  run: echo "::add-matcher::.github/cfn-lint.json"

参考

Discussion