Amazon Web Services ブログ
cfn-lint を使った AWS CloudFormation テンプレートの Git pre-commit バリデーション
linter とは、コードを精査して、そのコードを実行したときにエラーを発生させる可能性のある構文エラーやバグがないかを探すプログラムのことです。スタンドアロンのツールとして実行することも可能ですが、ビルド自動化やオーサリングなどのツールに組み込まれていることが多いです。
これまで CloudFormation による構文チェックは、サービス API の ValidateTemplate
アクションに限られていました。このアクションを実行すると、テンプレートが正しい形式の JSON または YAML で書かれているかどうかがわかりますが、自分で定義した実際のリソースが検証されているわけではありません。数か月前、私はより良い選択肢がないか探しました。Stelligent の cfn_nag のようなツールは、テンプレートのリソースに追加の検証を実行できますが、これはセキュリティやベストプラクティスの観点から行われるものです。さらに探しているうちに、CloudFormation のサービスチームが、リソースやプロパティに有効な構文について説明したリソース仕様を公開していることを発見しました。
AWS CloudFormation linter の紹介
同じころ、Amazon の同僚である Kevin Dejong が、Python で自ら記述した CloudFormation linter のオープンソース化を準備していました。これは、私が求めていた必須項目のほとんどをクリアしていました。
- 組み込み関数の解析 (条件文を含む)
- 拡張可能なルールベースのアーキテクチャ
- 公開済みのリソース仕様に照らした検証
- Python で記述されている (ええ、私は偏ってます)
私はこの linter の、とりわけ拡張性に強く引かれました。コミュニティのメンバーは、許容値や二者択一のリソースプロパティなど、リソース仕様の外部に存在する事柄について、新しい構文規則を追加することが可能になるからです。
当社は昨春、この linter を cfn-lint として公開して以来、その向上に取り組んでいます。これまでに、バッチファイル検証、SAM テンプレートサポート、制限チェック、そして、AWS CodePipeline や IAM ポリシーを使って作成されたパイプラインのようなリソース用のより詳細な検証などを追加してきました。linter 規則のフルリストは、GitHub のドキュメントでご覧いただけます。
linter を理解するには、とにかく自分でやってみるのが一番です。
linter のインストール
この linter は Python で記述されているので、pip
を使うのが最も簡単なインストール方法です (みなさんのマシンに Python がすでにインストールされているものと仮定しています)。
pip3 install cfn-lint
linter をインストールし、オペレーティングシステムパスに組み込んだら、コマンドラインから実行できるようになります。基本の構文は以下のようになります。
cfn-lint --template simple-vpc.template.yml --region us-east-1 --ignore-checks W
上記には、linter がシンプルな VPC テンプレートを us-east-1 リージョンのリソース仕様に照らして、すべての警告ルールを無視して検証すると記されています。
ただ、この linter は、コマンドラインのみならず数多くのユースケースをサポートしています。
- Git リポジトリの pre-commit hook として
- Atom、Visual Studio Code、Sublime、IntelliJ、さらには VIM の IDE プラグインとして
- CI/CD パイプラインの構築ステップ、検証ステップとして
このブログでは、cfn-lint を Git の pre-commit hook としてセットアップする方法を解説します。他のユースケースについては今後の記事で取りあげる予定です。
Git リポジトリのセットアップ
まず、テンプレートに Git リポジトリが必要です。リポジトリがない場合は、以下の私の demo repo をコピーしてください。
git clone https://github.com/cmmeyer/cfnlintdemo.git
空のリポジトリを作成することもできます。
mkdir cfnlintdemo
cd cfnlintdemo
git init
cfn-lint を pre-commit フックとして設定
次に、pre-commit
をインストールして Git に関連付けます。pre-commit のフレームワークに慣れていない方は、同社のウェブサイトで詳細がご覧いただけます。pre-commit は、コードレビューのためにコードを提出する前にスクリプトを実行して、コード内の単純な問題を特定する方法を提供しています。ここでは、CloudFormation テンプレートの構文を検証します。
pre-commit
は、Python の pip
インストーラーからインストールすることもできます。
sudo pip install pre-commit
また、pre-commit がウェブサイトで提供している Python ベースのインストーラーも使用できます。
curl https://pre-commit.com/install-local.py | python -
次に、pre-commmit
に cfn-lint
をフックとして使用するよう指示します。これを実行するには、リポジトリのルート内に .pre-commit-config.yaml
ファイルを作成します。以下は、私の cfnlintdemo
リポジトリ内にあるファイルです。
# .pre-commit-config.yaml
repos:
- repo: https://github.com/awslabs/cfn-python-lint
rev: v0.15.0 # The version of cfn-lint to use
hooks:
- id: cfn-python-lint
files: templates/.*\.(json|yml|yaml)$
上記は、pre-commit に対して、cfn-lint のバージョン 0.15.0 (本記事の執筆時点での最新版) をインストールし、git commit
のアクションがあったときは常に templates
ディレクトリ内のすべてのファイルに対してこれを実行するように指示しています。
最後のステップで、以下のコマンドをリポジトリのルートから実行して、リポジトリにフックをインストールします。
pre-commit のインストール
誤ったテンプレートを追加してみる
linter がインストールされたので、誤ったテンプレートをリポジトリにコミットさせて、これをテストしてみます。以下のサンプルのテンプレートには、スタックを更新または削除しようとしたときに障害となる可能性のある、サブネットのルートテーブルの誤った関連付けが含まれています。
# cfnlintdemo/templates/bad-routeable-association.yaml
---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Bad SubnetRouteTableAssociation'
Parameters:
PublicRouteTable:
Type: String
PrivateRouteTable:
Type: String
PublicSubnet01:
Type: String
PrivateSubnet01:
Type: String
Resources:
PublicSubnetRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: {Ref: PublicRouteTable}
SubnetId: {Ref: PublicSubnet01}
PrivateSubnetRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: {Ref: PrivateRouteTable}
SubnetId: {Ref: PublicSubnet01}
ファイルを保存して、これをリポジトリにコミットさせてみます。
git add /templates/bad-reoutetable-association.yaml
git commit -m "Bring the badness"
次のように表示されるはずです。
[INFO] Installing environment for https://github.com/awslabs/cfn-python-lint.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
AWSLabs CloudFormation Linter............................................Failed
hookid: cfn-python-lint
W2001 Parameter PrivateSubnet01 not used.
templates/bad-route-table-association.yaml:15:3
E3022 SubnetId in PublicSubnetRouteTableAssociation1 is also associated with PrivateSubnetRouteTableAssociation1
templates/bad-route-table-association.yaml:24:9
E3022 SubnetId in PrivateSubnetRouteTableAssociation1 is also associated with PublicSubnetRouteTableAssociation1
templates/bad-route-table-association.yaml:30:9<
まとめ
お疲れさまでした。 誤ったテンプレートが Git リポジトリにコミットされるのを防ぐことができました。これで、CloudFormation テンプレートを含むあらゆる Git リポジトリの pre-commit フックとして、cfn-lint を追加できるようになりました。
著者について
Chuck Meyer は、オハイオ州を拠点とする、AWS CloudFormation のシニアデベロッパーアドボケイトです。 社外および社内の開発チームと協力して、CloudFormation ユーザーの開発者体験を継続的に向上させています。 ライブミュージックを熱狂的に愛し、ベースの演奏やライブ鑑賞に多くの時間を費やしています。#cloudformation Slack チャンネルへの参加を希望される方は、Twitter (@chuckm) からご連絡ください。