WindowsでCloudFormationのLinter cfn-python-lint を気軽に扱う方法

2021.03.05

しばたです。

CloudFormation用のLinterであるcfn-python-lintはPython製でpipからインストールするのが基本でありWindowsユーザーにとってはちょっと導入の敷居が高くあります。

普段からPythonを使っている方であれば特に問題は無いでしょうが、残念ながら私はPythonを常用していません。
本記事では非Pythonユーザーがcfn-python-lintを気軽に使う方法を模索していきます。

気軽さ、とは?

一言で「気軽」といってもその捉え方は人それぞれだと思います。
私にとっての(すなわち本記事にとっての)気軽さは

  • ツールの導入が容易であること
    • ファイルをダウンロードして終わり、インストーラーの指示に従うだけ、位だとベスト
  • 導入に際し依存関係が少ないこと(独立性が高い)
    • 依存関係が無いことががベスト
  • ツールの廃棄が容易であること
    • 前二つとも関連しますがツールを使わなくなった際にゴミが残らない様にしたい

となります。
「(できるだけ)手順が容易で環境の独立性が高い」方法だと嬉しい感じです。

Dockerでcfn-python-lintを使う

cfn-python-lintではDokcer環境に対する導入手順も紹介されています。

Dockerfile

FROM python:3.8-alpine

RUN pip install cfn-lint
RUN pip install pydot

ENTRYPOINT ["cfn-lint"]
CMD ["--help"]

私は以前こんな記事を書いたとおりWSL2、Docker Desktopを通常利用しています。

最初の環境構築は人によっては面倒かもしれませんが、コンテナ環境であれば環境の独立性は高く導入や破棄も容易です。
この方法は非常に良さそうです。

上記Dockerfileを任意のフォルダに置き、以下の様にdocker buildすれば自前のイメージを作成できます。

cd <Dockerfileのあるフォルダ>
docker build --tag cfn-python-lint:latest .

このイメージを使い以下の様にdocker runしてやればカレントディレクトリにあるテンプレートファイルに対して検査をしてくれます。

# PowerShellコンソールから実行する場合
cd <CFnテンプレートのあるフォルダ>
docker run --rm -v "$($pwd.Path):/data" cfn-python-lint:latest /data/<CFnテンプレートファイル>

上図は私が以前に作った検証環境向けのテンプレートに対して実行した結果なのですが、

W1020 Fn::Sub isn't needed because there are no variables at Resources/SecurityGroupSSH/Properties/GroupDescription/Fn::Sub
/data/sample.yaml:226:9

と「文字列リテラルに対してFn::Sub関数を実施する必要は無い」と教えてくれました。
実際当該箇所はこんな感じで確かに無駄な処理になってました。(上のGroupNameの処理をコピペしてそのまま放置したんだと思います...)

良い感じですね。
Linuxコンテナを使う都合テンプレートファイルの指定方法に制約が付きますが、気軽に使うという点ではメリットの方が勝ると思います。

ちなみに環境を破棄したいときはdocker rmiするだけなので非常にお手軽です。

# PowerShellコンソールからだとこんな感じでコンテナイメージを破棄できる
docker rmi $(docker images cfn-python-lint -q)

VS Codeでcfn-python-lintを使う場合はEmbeddable Pythonを使う

cfn-python-lintには各種エディタと連動するための拡張機能も存在し、私が常用するVisual Studio Code(VS Code)にもそれ用の拡張が存在します。

この拡張を導入し、設定のCfn Lint: Path欄にcfn-python-lintの実行ファイル(cfn-lint)パスを指定してやるとテンプレート編集中にLinterが使える様になります。

そこで最初に前節で導入したdocker runコマンドを以下の様にしてみたのですが、残念ながらテンプレートファイルのパスの解決に失敗してしまいました。

docker run --rm -v "%CD%:/data" cfn-python-lint:latest

この拡張(というかVS Code拡張全般かも?)ではファイルは絶対パスでしか扱えない様で、コンテナ環境とのマウントの都合相対パスしか使えないDocker環境では実現不可能という結論に至りました。

Embeddable Pythonの導入

代替案を考えたものの良い方法は思いつかず結局Pythonをインストールするしか無いのですが、できるだけ依存関係を減らしたかったのでポータブル版である「Embeddable Python Package」を使うことにしました。

こちらであればツールに閉じたPython環境を用意できますし廃棄も容易です。
(pyenvという選択肢も無くはないですが、Windowsだとつらい記憶しかなかったのとより高い独立性が欲しかったのです...)

まずはPythonのダウンロードページからEmbeddable Python Packageをダウンロードし、任意のフォルダに展開します。

cfn-python-lintはPython 3.4以降で動作するとのことだったので、今回は本日時点で最新のPytnon 3.9.2をダウンロードし、C:\aws\python39というフォルダに展開しました。

ここから以下の記事の手順に従いpipを使える様にしcfn-python-lintをインストールしていきます。

# PowerShell 7.1.2コンソールから実行
# カレントディレクトリを移動
cd C:\aws\pytnon39

# python39._pth 内のコメント解除
(Get-Content .\python39._pth -Raw) -replace '#import site','import site' | Set-Content .\python39._pth

# sitecustomize.py ファイルを追加
# ※これをしないと特定モジュールで pip install に失敗する 
@'
import sys
sys.path.insert(0, '')
'@ > .\sitecustomize.py

# pip のインストール
# ※インストーラーをダウンロードしてインストール
Invoke-WebRequest https://bootstrap.pypa.io/get-pip.py -OutFile get-pip.py
.\python.exe .\get-pip.py
Remove-Item .\get-pip.py

ここまでの手順がエラー無く完了すれば.\Scripts\pip.exeが作成されているはずです。

ここからこのpipを使いcfn-python-lintをインストールします。

# あとは pip install するだけ
.\Scripts\pip.exe install cfn-lint

# ※ VS Code拡張でグラフ描画もしたい場合はpydotも追加インストールしておく
.\Scripts\pip.exe install pydot

エラー無く完了すれば.\Scripts\cfn-lint.exeが作成され、前節のDocker環境と同じ様に扱えます。

コマンドを常用するならC:\aws\python39\ScriptsにPATHを通しておけば良いでしょう。
今回はVS Codeから使いたいだけなのでPATHは通さないでおきます。

VS Code拡張で試す

VS Code拡張設定のCfn Lint: Path欄にcfn-lint.exeの絶対パスを指定してやります。
VS Codeの設定ファイルはJSONですのでパスのバックスラッシュはエスケープする必要があります。

C:\\aws\\python39\\Scripts\\cfn-lint.exe

これで期待した動作となりテンプレートファイル編集中にLinterがいい感じに動作してくれます。

環境を破棄したい場合はC:\aws\python39を削除するだけでOKです。
インストーラー版Pythonより導入はちょっと面倒ですが、独立性は非常に高く気軽に試せると思います。

最後に

以上となります。
いろいろ試した結果を書いたためちょっととりとめのない感じになっていますが、

  • Dockerを使えるならdocker runで実行する
  • Visual Studio Codeなどエディタ拡張を使いたいならEmbeddable Python環境を用意する

というのが気軽に試せる方法だと思います。
Linterは非常に便利ですのでみなさんの環境に合わせて導入すると良いと思います。