考えるべきは“我々は何をテストしたいのか”

チェシャ猫氏:さて、では先ほど作ったLambdaのカウントするものをデプロイしてみましょう。先ほどの囲みの部分をデプロイすると、エラーが出る! 実際に実行するとInternal server errorになります。ダメじゃん。

何が足りないかというと、権限がついていません。だから、Dynamoに対して値の書き込みに行こうとして、失敗している。

ここで考えなければいけないのは、我々は何をテストしたいかです。要するに、今CDKでスナップショットができるとか、バリデーションができると言っていたのは、今ここで我々が本当に欲しかったものがテストできていません。

テスト戦略とツール

というところで、テスト戦略とツールについて最後にちょっと考えましょう。さて、YAMLの生成のロジックを考えると、こうなっています。

まず、我々はCDKを実装してYAMLを生成します。そこからリソースが作成されます。我々はYAMLの性質についてテストしていたわけで、期待するYAML、例えばhaveResourceみたいなものが、先ほどテストケースとして出てきました。つまり、CDKのテスト機構が提供している予測可能性はどこかというと、左側半分です。1と2を足すと、期待するYAMLが生成されている。

ただ、真に欲しいのはBです。我々は最終的にできるリソースに対して性質を保証したくて、本当に欲しいのは、最後にBになるところだと思います。

CDKを信用するのであれば、1+2=Aであって、A+3=Bであれば、全部回ったらBになる。要するに、足りていないもの、残りは右側の矢印です。Aしたものをアプライすると、期待する振る舞いをするリソースができる。

要するに、CloudFormationのテンプレートをテストしたいというのが、我々が本当にやりたかったことです。“YAMLの振る舞いをテストしたい”とも言えます。

リソースに対して我々がどういう性質を期待しているかというと、大きく2つあります。1つはポリシー的なもの。いわゆるガードレールというもので、リソースやアプリをまたいで組織的になにか条件をかけたい。もう1つはセマンティックな性質で、要するにリソースがうまく噛み合って動いているかです。先ほどの権限が足りないのは後者の例です。

テストで使えそうな3つのツール

実際にテストしようとすると、いくつか使えそうなツールがあるので見ていきましょう。ここでは3つ挙げてみました。1つ目はcfn-nag、CloudFormation-nagです。どういうものかというと、CloudFormation用の専用ツールで、セキュリティ系のルールセットが提供されていて、セキュリティ系のチェックができます。nagは“説教する”みたいな(意味の)動詞ですが、ガミガミ言ってくれるようなものです。

2つ目はCloudFormation Guardです。これはセキュリティに限らず、ルールを自分で記述できます。最後はConftestです。これは一般のYAML用で、CloudFormationに限らず使えます。

nagはみなさんたぶんよく知っていると思いますが、セキュリティ系の定番のツールです。デフォルトのルール定義がたくさんあるのが魅力ですが、拡張性が低いです。

一方で、CloudFormation Guardはわりと新しくて、2020年の6月にOSSになって、10 月に GAかな。Rustで書かれているツールです。ルールはサンプルしかなく、自分で書かなければいけませんが、ある程度自分でルールセットを書くのが簡単なDSLが提供されています。

ただ、さっき言ったようにLambdaとDynamoみたいなところは複数のリソースに渡って条件を書かなければいけないので、各リソースにタグがついているような個別の条件ではありません。

あるいは、自動生成されたリソース名は、CloudFormation Guardは正規表現が使えますが、=〇〇では書けないので、例えば2つのリソースがあったら、「このLambdaにつけられた〇〇がこのDynamoにつけられた〇〇と一致」のような条件を書かなければならず、正規表現だとキャプチャができないので難しい。

そういうときに使うのがConftestです。Open Policy Agentという、Kubernetes関連でよく使われるPolicy Enforcerの派生ツールみたいなもので、特筆すべきは拡張性の高さです。Prologの一種である、Regoという言語を使います。

Regoがなにかというと、「〇〇であるという事実」を定義するときに、「△△かつXXが成り立つことにする」としてルールを記述していきます。説明が連鎖していくわけです。

イコールが出てきたときは、代入しているわけではなく、これらの条件を満たすようなものが選び方があるかどうかを全探索していきます。これをユニフィケーション(単一化)と呼びます。これがあるので正規表現とかと違い曖昧性をもち、かつ、キャプチャできる。fにbindできる性質があります。

先ほどの権限のテストの話でいうと、例ではありますが、権限の不足を発見するためにはこんな記述になります。

「権限が必要だけど、もっていないようなものはあるか?」と。「needs_permissionだけど、has_permissionじゃないような組を検出しなさい」と。

どういう組みかというと、Policyに対してStatementを、以下を満たすようなものがあるか?

適切に選ぶとPutItemとUpdateItemがうまく取ったときにあるかというので、単一化を使って組み合わせで条件を充足するものがあるかどうかを、Conftestを使うと調べられます。

今見てもらうと分かりますが、普通のプログラミング言語とたぶん意味論がぜんぜん違うので、確認しつつ、練習したい人はPlaygroundみたいなものも用意されているので、最初はこちらを使ってちょっとずつ練習しながら書いていくのがよいでしょう。

各ツールの使いどころです。まず拡張性の件で複雑なのはConftestです。それ以外の部分、Policyの部分をどうするか。デフォルトで済むならnagのほうがよく、追加するならguardのほうがよい。

セクション3のまとめ

まとめとして、まず、何をテストしているのか意識しましょう。CDKが提供しているテストはあくまでもCDKからCloudFormationのテストで、CloudFormationのアプライの結果がどうなるかのテストではありません。

期待する振る舞いをテストするためには、例えば今挙げたようなツールを使ってConftestを使うと、強力にテストできますよというお話を各論的ですが、ツールの一覧として挙げました。

あなたのInfrastructure as Codeも予測可能な状態に

さて、本日のまとめです。まずIaCとは何かを定義して、なぜ静的テストが必要なのかという話をしました。ポイントになるのは、予測可能性を担保したいことです。AWSで考えると、CDKを使うことで、予測可能性の3要素である再現性と純粋性とモジュール性をある程度満たせます。

不足している部分、特に意味論的な性質、リソースの兼ね合いについてはConftestを使うと機能的な要件もチェックでき、安心してアプライできる状態になります。

今回お話したテクニック、あるいは考え方を使って、あなたのInfrastructure as Codeもぜひ予測可能な状態にしてみてください。以上、チェシャ猫がお送りしました。ありがとうございました。