Infrastructure as Code の静的テスト戦略 #CODT2020 / Cloud Operator Days Tokyo 2020

Infrastructure as Code の静的テスト戦略 #CODT2020 / Cloud Operator Days Tokyo 2020

Cloud Operator Days Tokyo 2020 で使用したスライドです。

インフラをコードとして管理することで環境構築の再現性を担保する IaC の手法は、ここ数年で急激に普及しました。しかし、そのコードに対するテストは簡単なチェックに留まっていることも多く、実際に環境を構築してみたら上手く動かない、という状況がしばしば起こります。そこで本セッションでは、実際の環境を構築する「前」に、IaC のコード自体に対してテストを行う手法について解説します。

実際にインフラをコードで管理していて、早い段階で設定ミスを検出して無駄なコストを削減したい方におすすめのセッションです。

イベント概要:https://cloudopsdays.com/
ブログ記事:https://ccvanishing.hateblo.jp/entry/2020/07/30/173935

332f89cc697355902a817506b6995f2b?s=128

y_taka_23

July 29, 2020
Tweet

Transcript

  1. #CODT2020 #CODT2020 Infrastructure as Code の 静的テスト戦略 チェシャ猫 (@y_taka_23) Cloud

    Operator Days Tokyo 2020 (29th July, 2020)
  2. #CODT2020 #CODT2020 「Infrastructure as Code 辛い」

  3. #CODT2020 #CODT2020 「デプロイしてみたら上手く動かない」

  4. #CODT2020 本日のアジェンダ • なぜ IaC に静的テストが必要なのか • AWS 上で IaC

    を実現する上で考えるべきこと • IaC をテストする上での戦略とツール
  5. #CODT2020 #CODT2020 IaC における「静的」テスト Why Predictability Matters in IaC?

  6. #CODT2020 #CODT2020 Infrastructure as Code って何だっけ?

  7. #CODT2020 Infrastructure as Code って何だっけ? • ソフトウェア開発のプラクティスをインフラの オートメーションに活かすアプローチ ◦ Git

    によるバージョン管理 ◦ Pull Request によるレビュー ◦ 継続的なテスト ◦ etc... 『Infrastructure as Codeクラウドにおける サーバ管理の原則とプラクティス』 https://www.oreilly.co.jp/books/9784873117966/
  8. #CODT2020 Infrastructure as Code って何だっけ? • ダイナミックインフラプラットフォーム (AWS) ◦ サーバやストレージの提供

    • インフラ定義ツール (CFn, Terraform) ◦ サーバやストレージの構成・設定管理 • サーバ構成ツール (Ansible, Chef) ◦ サーバ自身の細部の設定 • インフラサービス (CloudWatch など) ◦ インフラやアプリの管理支援
  9. #CODT2020 Mutable Immutable Local Global

  10. #CODT2020 Local Global 影響範囲が個々のリソースで完結する 影響範囲がリソースをまたいで全体に及ぶ

  11. #CODT2020 Mutable Immutable 既存のリソースに 重ねがけして更新 一度更地にして ゼロから再構築

  12. #CODT2020 Mutable Immutable Local Global CloudFormation Terraform Ansible Chef Kubernetes

    Docker
  13. #CODT2020 Mutable Immutable Local Global CloudFormation Terraform Ansible Chef Kubernetes

    Docker 今回注目したいのは Global + Mutable
  14. #CODT2020 #CODT2020 なぜ Global + Mutable は辛いのか?

  15. #CODT2020 インフラの現状がカオス • Global: 局所的改善の困難さ • Mutable: 歪みの蓄積

  16. #CODT2020 インフラの現状がカオス 何かが壊れそうで心配 • 失敗経験 • IaC への不信 • 組織の力関係

    • Global: 局所的改善の困難さ • Mutable: 歪みの蓄積
  17. #CODT2020 インフラの現状がカオス 何かが壊れそうで心配 例外的な作業 • 失敗経験 • IaC への不信 •

    組織の力関係 • Global: 局所的改善の困難さ • Mutable: 歪みの蓄積
  18. #CODT2020 インフラの現状がカオス 何かが壊れそうで心配 例外的な作業 • 失敗経験 • IaC への不信 •

    組織の力関係 • 不均一な構成 • システム疲労 • ノウハウ散逸 • Global: 局所的改善の困難さ • Mutable: 歪みの蓄積
  19. #CODT2020 インフラの現状がカオス 何かが壊れそうで心配 例外的な作業 塩漬けインフラ負のサイクル (オートメーション恐怖症) • 失敗経験 • IaC

    への不信 • 組織の力関係 • 不均一な構成 • システム疲労 • ノウハウ散逸 • Global: 局所的改善の困難さ • Mutable: 歪みの蓄積
  20. #CODT2020 インフラの現状がカオス 何かが壊れそうで心配 例外的な作業 塩漬けインフラ負のサイクル (オートメーション恐怖症) • 失敗経験 • IaC

    への不信 • 組織の力関係 • 不均一な構成 • システム疲労 • ノウハウ散逸 • Global: 局所的改善の困難さ • Mutable: 歪みの蓄積
  21. #CODT2020 #CODT2020 「何が起こるかわからない」を減らせばよい

  22. #CODT2020 #CODT2020 予測可能性 Predictability

  23. #CODT2020 #CODT2020 アプリ開発と IaC を比較すると

  24. #CODT2020 開発の V 字モデル 要件定義 コーディング 受け入れテスト 結合テスト 単体テスト 外部設計

    内部設計
  25. #CODT2020 開発の V 字モデル 要件定義 コーディング 受け入れテスト 結合テスト 単体テスト 外部設計

    内部設計
  26. #CODT2020 インフラの V 字モデル? アプリ仕様 IaC 実装 E2E テスト Serverspec

    など 環境一揃い 個別リソース
  27. #CODT2020 インフラの V 字モデル? アプリ仕様 IaC 実装 E2E テスト Serverspec

    など 単体テストの不在 環境一揃い 個別リソース
  28. #CODT2020 インフラの V 字モデル? アプリ仕様 IaC 実装 E2E テスト Serverspec

    など 単体テストの不在 環境一揃い 個別リソース デプロイの壁
  29. #CODT2020 #CODT2020 「デプロイの壁」の手前でテストできれば IaC もアプリ開発により近づける!

  30. #CODT2020 Section 1 のまとめ • IaC = アプリ開発プラクティスのインフラへの応用 ◦ 今回は

    Global + Mutable の領域にフォーカス • 予測可能性をいかに担保するか? ◦ 実行時に「何が起こるかわからない」という恐怖の克服 • アプリに寄せたテスト戦略 ◦ 静的(= デプロイ前)テストで「何が起こるか」を見切る
  31. #CODT2020 #CODT2020 AWS における予測可能性 How to Manage IaC Predictability on

    AWS?
  32. #CODT2020 予測可能性の 3 要素 • 再現性 (Reproducibility) ◦ 同じ操作を誰でも、いつでも繰り返すことができる •

    純粋性 (Purity) ◦ 実行前の状態によらず、結果が常に同じになる • モジュール性 (Modularity) ◦ 再利用可能な部品が記述しやすい仕組みを備える
  33. #CODT2020 #CODT2020 純粋性? 冪等性じゃなくて?

  34. #CODT2020 冪等性 vs 純粋性 • デプロイはパラメータ x と事前状態 e の関数

    ◦ 返り値は変更後の状態:e’ = f (x, e) • 冪等性:複数回実行しても結果が一定 ◦ 任意のパラメータ x と事前状態 e に対して f (x, f (x, e)) = f (x, e) • 純粋性:実行前の状態によらず結果が一定 ◦ 任意のパラメータ x と事前状態 e1, e2 に対して f (x, e1) = f (x, e2)
  35. #CODT2020 冪等性 vs 純粋性 • デプロイはパラメータ x と事前状態 e の関数

    ◦ 返り値は変更後の状態:e’ = f (x, e) • 冪等性:複数回実行しても結果が一定(純粋なら冪等) ◦ 任意のパラメータ x と事前状態 e に対して f (x, f (x, e)) = f (x, e) • 純粋性:実行前の状態によらず結果が一定 ◦ 任意のパラメータ x と事前状態 e1, e2 に対して f (x, e1) = f (x, e2)
  36. #CODT2020 #CODT2020 より具体的に、AWS で考えると

  37. #CODT2020 IaC on AWS の 4 ステップ マネジメント コンソール AWS

    CLI CloudFormation CDK (Cloud Dev. Kit)
  38. #CODT2020 IaC on AWS の 4 ステップ マネジメント コンソール AWS

    CLI CloudFormation CDK (Cloud Dev. Kit) 予測可能性
  39. #CODT2020 マネジメントコンソール • 人間が手作業でリソースを作成 ◦ ナイーブだが直感的で融通も利く • 予測可能性は最も低い ◦ 再現性:なし

    ◦ 純粋性:なし ◦ モジュール性:なし
  40. #CODT2020 AWS CLI • シェルスクリプトなどと組み合わせて自動化 ◦ 機能面では扱える API が最も多い •

    予測可能性はあまり高くない ◦ 再現性:あり(繰り返し実行可) ◦ 純粋性:なし ◦ モジュール性:ほとんどなし
  41. #CODT2020 CloudFormation • YAML による宣言的な定義 ◦ 必要な操作ではなく望まれる状態を記述 • 予測可能性はだいぶ改善した ◦

    再現性:あり ◦ 純粋性:一応あり(宣言的記述、衝突しない名前の生成) ◦ モジュール性:かなり乏しい
  42. #CODT2020 Cloud Development Kit (CDK) • プログラムで CloudFormation 用 YAML

    を生成 ◦ TypeScript / Python / Java / .NET ライブラリ ◦ IDE が使える、型があるので YAML より書くのが楽 • 現状で予測可能性は最も良好 ◦ 再現性:あり ◦ 純粋性:一応あり(実質 CloudFormation と同等) ◦ モジュール性:あり (再利用・配布可能な Construct)
  43. #CODT2020 SQS: MyQueue Subscribe SNS: MyTopic MyStack https://cdkworkshop.com/20-typescript/20-create-project/300-structure.html

  44. #CODT2020 export class MyStack extend cdk.Stack { constructor(...) { super(...);

    const queue = new sqs.Queue(this, 'MyQueue', { visibilityTimeout = cdk.Duration.Seconds(300) }); const topic = new sns.Topic(this, 'MyTopic'); topic.addSubscription(new subs.SqsSubscription(queue)); } } SQS: MyQueue Subscribe SNS: MyTopic MyStack
  45. #CODT2020 export class MyStack extend cdk.Stack { constructor(...) { super(...);

    const queue = new sqs.Queue(this, 'MyQueue', { visibilityTimeout = cdk.Duration.Seconds(300) }); const topic = new sns.Topic(this, 'MyTopic'); topic.addSubscription(new subs.SqsSubscription(queue)); } } SQS: MyQueue Subscribe SNS: MyTopic MyStack scope(親要素)
  46. #CODT2020 cdk synth aws cloudformation deploy export class MyStack extend

    cdk.Stack { constructor(...) { super(...); const queue = new sqs.Queue(this, 'MyQueue', { visibilityTimeout = cdk.Duration.Seconds(300) }); const topic = new sns.Topic(this, 'MyTopic'); topic.addSubscription(new subs.SqsSubscription(queue)); } }
  47. #CODT2020 cdk synth aws cloudformation deploy Resources: MyQueueXXXXXX: Type: AWS::SQS::Queue

    Properties: ... MyQueuePolicyXXXXXX: Type: AWS::SQS::QueuePolicy ... MyTopicXXXXXX: Type: AWS::SNS::Topic ... MyQueueMyStackMyTopicXXXXXX: Type: AWS::SNS::Subscription ...
  48. #CODT2020 cdk synth aws cloudformation deploy Resources: MyQueueXXXXXX: Type: AWS::SQS::Queue

    Properties: ... MyQueuePolicyXXXXXX: Type: AWS::SQS::QueuePolicy ... MyTopicXXXXXX: Type: AWS::SNS::Topic ... MyQueueMyStackMyTopicXXXXXX: Type: AWS::SNS::Subscription ... CDK では明示していない = Construct が内包
  49. #CODT2020 cdk diff cdk deploy

  50. #CODT2020 #CODT2020 再利用可能な Construct の自作も可能

  51. #CODT2020 API Count Hits

  52. #CODT2020 Custom Construct API Count Hits https://cdkworkshop.com/20-typescript/40-hit-counter.html

  53. #CODT2020 export class Counter extends cdk.Construct { public readonly handler:

    lambda.Functions; constructor() { const table = new dynamodb.Table(this, 'Hits', { partitionKey: { ... } }); this.handler = new lambda.Handler(this, 'Handler', { ... environment: { ... HITS_TABLE_NAME: table.tableName } }); } }
  54. #CODT2020 export class Counter extends cdk.Construct { public readonly handler:

    lambda.Functions; constructor() { const table = new dynamodb.Table(this, 'Hits', { partitionKey: { ... } }); this.handler = new lambda.Handler(this, 'Handler', { ... environment: { ... HITS_TABLE_NAME: table.tableName } }); } } Construct を継承したクラスを作成
  55. #CODT2020 export class Counter extends cdk.Construct { public readonly handler:

    lambda.Functions; constructor() { const table = new dynamodb.Table(this, 'Hits', { partitionKey: { ... } }); this.handler = new lambda.Handler(this, 'Handler', { ... environment: { ... HITS_TABLE_NAME: table.tableName } }); } } DynamoDB の Construct
  56. #CODT2020 export class Counter extends cdk.Construct { public readonly handler:

    lambda.Functions; constructor() { const table = new dynamodb.Table(this, 'Hits', { partitionKey: { ... } }); this.handler = new lambda.Handler(this, 'Handler', { ... environment: { ... HITS_TABLE_NAME: table.tableName } }); } } Lambda の Construct
  57. #CODT2020 export class Counter extends cdk.Construct { public readonly handler:

    lambda.Functions; constructor() { const table = new dynamodb.Table(this, 'Hits', { partitionKey: { ... } }); this.handler = new lambda.Handler(this, 'Handler', { ... environment: { ... HITS_TABLE_NAME: table.tableName } }); } } 環境変数経由で参照
  58. #CODT2020 各管理手法の予測可能性 再現性 純粋性 モジュール性 マネコン × × × AWS

    CLI ◯ × × CloudFormation ◯ △ △ CDK ◯ △ ◯
  59. #CODT2020 各管理手法の予測可能性 再現性 純粋性 モジュール性 マネコン × × × AWS

    CLI ◯ × × CloudFormation ◯ △ △ CDK ◯ △ ◯
  60. #CODT2020 #CODT2020 結局、CDK でも「そこそこ」なの?

  61. #CODT2020 CDK とテスト • Snapshot Test ◦ 生成される YAML が前回と同じか(CDK

    のアップデートなど) • Fine-grained Assertion ◦ 生成された YAML に目的のリソースが存在しているか • Validation Test(実体は単なる例外送出のテスト) ◦ 不正なパラメータを渡したときにエラーが発生するか
  62. #CODT2020 #CODT2020 Snapshot Test

  63. #CODT2020 export class DeadLetterQueue extends cdk.Queue { public readonly alarm

    cloudwatch.IAlarm; constructor(scope, id, props = {}) { super(scope, id); this.alarm = new cloudwatch.Alarm(this, 'Alarm', { alarmDescription: 'messages in the DLQ', evaluationPeriods: 1, threshold: 1, metric: this.metricApproximateNumberOfMessagesVisible(), }); } }
  64. #CODT2020 import { SynthUtils } from '@aws-cdk/assert'; test('DLQ preserves the

    snapshot', () => { const stack = new Stack(); new dlq.DeadLetterQueue(stack, 'DLQ’); expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); }); cdk synth に相当
  65. #CODT2020

  66. #CODT2020 その時点での YAML が スナップショットとして 保存される

  67. #CODT2020 export class DeadLetterQueue extends cdk.Queue { public readonly alarm

    cloudwatch.IAlarm; constructor(scope, id, props = {}) { super(scope, id); this.alarm = new cloudwatch.Alarm(this, 'Alarm', { alarmDescription: 'messages in the DLQ', evaluationPeriods: 1, threshold: 1, metric: this.metricApproximateNumberOfMessagesVisible(), period: cdk.Duration.minutes(1), }); } }
  68. #CODT2020 export class DeadLetterQueue extends cdk.Queue { public readonly alarm

    cloudwatch.IAlarm; constructor(scope, id, props = {}) { super(scope, id); this.alarm = new cloudwatch.Alarm(this, 'Alarm', { alarmDescription: 'messages in the DLQ', evaluationPeriods: 1, threshold: 1, metric: this.metricApproximateNumberOfMessagesVisible(), period: cdk.Duration.minutes(1), }); } } 生成される YAML に影響を与える変更
  69. #CODT2020

  70. #CODT2020 - "Period": 300, + "Period": 60, 差分を検知してテスト失敗

  71. #CODT2020 #CODT2020 Fine-Grained Assertion

  72. #CODT2020 import { expect as expectCDK, SynthUtils, haveResource } from

    '@aws-cdk/assert'; test('DLQ has the message alarm', () => { const stack = new Stack(); new dlq.DeadLetterQueue(stack, 'DLQ’); expectCDK(stack).to(haveResource('AWS::CloudWatch::Alarm', { Namespace: 'AWS/Lambda' })); });
  73. #CODT2020 import { expect as expectCDK, SynthUtils, haveResource } from

    '@aws-cdk/assert'; test('DLQ has the message alarm', () => { const stack = new Stack(); new dlq.DeadLetterQueue(stack, 'DLQ’); expectCDK(stack).to(haveResource('AWS::CloudWatch::Alarm', { Namespace: 'AWS/Lambda' })); }); CDK が提供する YAML 生成結果に関する アサーション
  74. #CODT2020 import { expect as expectCDK, SynthUtils, haveResource } from

    '@aws-cdk/assert'; test('DLQ has the message alarm', () => { const stack = new Stack(); new dlq.DeadLetterQueue(stack, 'DLQ’); expectCDK(stack).to(haveResource('AWS::CloudWatch::Alarm', { Namespace: 'AWS/Lambda' })); }); わざと間違えてみた (AWS/SQS)
  75. #CODT2020

  76. #CODT2020 他に多数の属性があっても 実際に記述した Namespace のみを見る = Fine-Grained

  77. #CODT2020 #CODT2020 Validation Test

  78. #CODT2020 export class DeadLetterQueue extends cdk.Queue { public readonly alarm

    cloudwatch.IAlarm; constructor(scope, id, props = {}) { if (props.retention != undefined && props.retention > 14) { throw new Error('retention should be <= 14'); } super(scope, id, { retentionPeriod: cdk.Duration.days(props.retention || 14) }); ... }
  79. #CODT2020 export class DeadLetterQueue extends cdk.Queue { public readonly alarm

    cloudwatch.IAlarm; constructor(scope, id, props = {}) { if (props.retention != undefined && props.retention > 14) { throw new Error('retention should be <= 14'); } super(scope, id, { retentionPeriod: cdk.Duration.days(props.retention || 14) }); ... } 引数 (props) を確認して範囲外なら例外
  80. #CODT2020 test('DLQ retention should be < 14', () => {

    const stack = new Stack(); expect(() => { new dlq.DeadLetterQueue(stack, 'DLQ', { retention: 14 }); }).toThrowError(); }); 範囲内(例外は飛ばない)
  81. #CODT2020

  82. #CODT2020 通常の例外のテストと同様

  83. #CODT2020 Section 2 のまとめ • デプロイ時の予測可能性のために必要な要素 ◦ 再現性 / 純粋性

    / モジュール性 • 段階的に予測可能性を獲得 ◦ コンソール < CLI < CloudFormation < CDK • CDK には静的テスト機構が備わっている ◦ Snapshot / Fine-grained Assertion / Validation
  84. #CODT2020 #CODT2020 よし、自作の Construct デプロイしよう

  85. #CODT2020 Custom Construct API Count Hits

  86. #CODT2020 export class Counter extends cdk.Construct { public readonly handler:

    lambda.Functions; constructor(...) { const table = new dynamodb.Table(this, 'Hits', { ... }); this.handler = new lambda.Handler(this, 'Handler', { ... environment: { ... HITS_TABLE_NAME: table.tableName } }); } }
  87. #CODT2020 \エラー!/

  88. #CODT2020 export class Counter extends cdk.Construct { public readonly handler:

    lambda.Functions; constructor(...) { const table = new dynamodb.Table(this, 'Hits', { ... }); this.handler = new lambda.Handler(this, 'Handler', { ... environment: { ... HITS_TABLE_NAME: table.tableName } }); table.grantReadWriteData(this.handler); } } 権限つけ忘れた!
  89. #CODT2020 #CODT2020 「何」を「どう」テストすべきなのか?

  90. #CODT2020 #CODT2020 テスト戦略とツール Strategies & Tactics for IaC Predictability

  91. #CODT2020 CloudFormation CDK 作成されるリソース 1. 実装 2. YAML の生成 3.

    リソースの作成
  92. #CODT2020 CloudFormation CDK 作成されるリソース 1. 実装 A. 期待する YAML 2.

    YAML の生成 3. リソースの作成
  93. #CODT2020 CloudFormation CDK 作成されるリソース 1. 実装 A. 期待する YAML 2.

    YAML の生成 • CDK が提供する予測可能性:1 + 2 = A 3. リソースの作成
  94. #CODT2020 CloudFormation CDK 作成されるリソース 1. 実装 B. 期待する振る舞い A. 期待する

    YAML 2. YAML の生成 3. リソースの作成 • CDK が提供する予測可能性:1 + 2 = A
  95. #CODT2020 CloudFormation CDK 作成されるリソース 1. 実装 B. 期待する振る舞い A. 期待する

    YAML 2. YAML の生成 3. リソースの作成 • CDK が提供する予測可能性:1 + 2 = A • 本当に欲しい予測可能性:1 + 2 + 3 = B
  96. #CODT2020 CloudFormation CDK 作成されるリソース 1. 実装 B. 期待する振る舞い A. 期待する

    YAML 2. YAML の生成 3. リソースの作成 • 1 + 2 = A かつ A + 3 = B なら 1 + 2 + 3 = B
  97. #CODT2020 CloudFormation CDK 作成されるリソース 1. 実装 B. 期待する振る舞い A. 期待する

    YAML 2. YAML の生成 3. リソースの作成 • 1 + 2 = A かつ A + 3 = B なら 1 + 2 + 3 = B • つまり残りは右側の予測可能性が問題
  98. #CODT2020 #CODT2020 要するに YAML の「振る舞い」をテストしたい

  99. #CODT2020 リソースに期待する振る舞い • ポリシー的な性質 ◦ リソース・アプリをまたいで制約が掛かっているか ◦ 例:0.0.0.0/0 禁止、コスト集約用のタグ必須 •

    意味論的な性質 ◦ 複数のリソースがうまく「噛み合った」状態で動作するか ◦ 例:ネットワーク疎通が可能か、権限が足りているか
  100. #CODT2020 #CODT2020 どんなツールが使えそう?

  101. #CODT2020 CloudFormation のテストツール • cfn-nag ◦ CloudFormation 用、分野はセキュリティ系 • CloudFormation

    Guard (cfn-guard) ◦ CloudFormation 用、分野は限定せず汎用 • Conftest ◦ 一般の YAML 用、分野は限定せず汎用
  102. #CODT2020 cfn-nag • セキュリティ系の定番ツール ◦ ベストプラクティスがあらかじめ定義されている • メリット・デメリット ◦ 定義済みルール:デフォルトで豊富(必要なら抑制も可能)

    ◦ 配布・再利用性:低い(一応 S3 Bucket 経由で共有可能) ◦ 拡張性:低い(Ruby でロジックを陽に記述する必要あり) https://github.com/stelligent/cfn_nag
  103. #CODT2020 CloudFormation Guard (cfn-guard) • 新登場の汎用チェックツール (2020/6/17 -) ◦ 開発者プレビューなので

    Rust のソースからビルド • メリット・デメリット ◦ 定義済みルール:なし(既存の YAML からルール生成が可能) ◦ 配布・再利用性:低い(ルールファイル直接指定のみ) ◦ 拡張性:あまり高くない(個別の属性をチェックする DSL) https://github.com/aws-cloudformation/cloudformation-guard
  104. #CODT2020 #CODT2020 複数のリソース間の関係はどうテストする? 自動生成されたリソース名の参照はどう扱う?

  105. #CODT2020 Conftest • Open Policy Agent (OPA) の派生 ◦ Kubernetes

    との連携 (Gatekeeper) が人気 • メリット・デメリット ◦ 定義済みルール:なし ◦ 配布・再利用性:高い(OCI = Docker レジストリで配布可能) ◦ 拡張性:高い(Prolog の一種 Rego を使用) https://github.com/open-policy-agent/conftest
  106. #CODT2020 #CODT2020 Ruby や Rust はともかく Rego って何?

  107. #CODT2020 Rego 言語ことはじめ is_xxxxx { condition1 condition2 } Rego の記述は

    「xxxxxx とは yyyyyy であることである」 という定義のあつまり
  108. #CODT2020 Rego 言語ことはじめ is_xxxxx { condition1 condition2 } 定義:「xxxxx であるとは」

    Rego の記述は 「xxxxxx とは yyyyyy であることである」 という定義のあつまり
  109. #CODT2020 Rego 言語ことはじめ is_xxxxx { condition1 condition2 } 内容「Conidition 1

    かつ Condition-2 かつ…が成り立つことである」 Rego の記述は 「xxxxxx とは yyyyyy であることである」 という定義のあつまり
  110. #CODT2020 Rego 言語ことはじめ is_xxxxx { condition1 } is_xxxxx { condition2

    } 内容「Conidition 1 または Condition-2 が成り立つことである」 Rego の記述は 「xxxxxx とは yyyyyy であることである」 という定義のあつまり
  111. #CODT2020 Rego 言語ことはじめ is_xxxxx { is_yyyyy } is_yyyyy { is_zzzzz

    } 定義の参照「xxxxx であるとは yyyyy であることで、その yyyyy であるとは…」 Rego の記述は 「xxxxxx とは yyyyyy であることである」 という定義のあつまり
  112. #CODT2020 Rego 言語ことはじめ is_xxxxx { f = functions[_] is_good_func(f) }

    代入ではない(単一化) 配列 functions の中から全体を成り立たせるような添字 _ が存在すれば それを任意に取って f とする。 Rego の記述は 「xxxxxx とは yyyyyy であることである」 という定義のあつまり
  113. #CODT2020 #CODT2020 実際にどうすれば権限がテストできるか?

  114. #CODT2020 export class Counter extends cdk.Construct { public readonly handler:

    lambda.Functions; constructor(...) { const table = new dynamodb.Table(this, 'Hits', { ... }); this.handler = new lambda.Handler(this, 'Handler', { ... environment: { ... HITS_TABLE_NAME: table.tableName } }); table.grantReadWriteData(this.handler); } } 権限の不足を発見したい
  115. #CODT2020 deny[msg] { ... violations = [ [f, t] |

    f := functions[_]; t := tables[_]; needs_permission(f, t); not has_permission(f, t); ] count(violations) > 0 ... } warn[msg] { ... }
  116. #CODT2020 deny[msg] { ... violations = [ [f, t] |

    f := functions[_]; t := tables[_]; needs_permission(f, t); not has_permission(f, t); ] count(violations) > 0 ... } warn[msg] { ... } deny でエラー、warn は警告
  117. #CODT2020 deny[msg] { ... violations = [ [f, t] |

    f := functions[_]; t := tables[_]; needs_permission(f, t); not has_permission(f, t); ] count(violations) > 0 ... } warn[msg] { ... } 権限が必要だが持っていない Function と Table の組 > 0 ならばエラー
  118. #CODT2020 allows([_, policy], [table_name, _]) { policy.Type = "AWS::IAM::Policy" statements

    := policy.Properties.PolicyDocument.Statement[_] statements.Effect = "Allow" statements.Resource[_]["Fn::GetAtt"][0] = table_name statements.Action[_] = "dynamodb.PutItem" statements.Action[_] = "dynamodb.UpdateItem" }
  119. #CODT2020 allows([_, policy], [table_name, _]) { policy.Type = "AWS::IAM::Policy" statements

    := policy.Properties.PolicyDocument.Statement[_] statements.Effect = "Allow" statements.Resource[_]["Fn::GetAtt"][0] = table_name statements.Action[_] = "dynamodb.PutItem" statements.Action[_] = "dynamodb.UpdateItem" } Statement を適切に選んで 以下を満たせるか?
  120. #CODT2020 allows([_, policy], [table_name, _]) { policy.Type = "AWS::IAM::Policy" statements

    := policy.Properties.PolicyDocument.Statement[_] statements.Effect = "Allow" statements.Resource[_]["Fn::GetAtt"][0] = table_name statements.Action[_] = "dynamodb.PutItem" statements.Action[_] = "dynamodb.UpdateItem" } Action を適切に選んで 条件全体を満たせるか?
  121. #CODT2020 #CODT2020 慣れるまで結果を確認しつつ練習したい

  122. #CODT2020 https://play.openpolicyagent.org/

  123. #CODT2020 定義済みルール 配布・再利用 拡張性 cfn-nag ◯ × × cfn-guard ×

    × △ Conftest × ◯ ◯ 各ツールの使いどころ
  124. #CODT2020 定義済みルール 配布・再利用 拡張性 cfn-nag ◯ × × cfn-guard ×

    × △ Conftest × ◯ ◯ 各ツールの使いどころ Cfn-nag の定義済みルールを最大限使いつつ、複雑な記述は Conftest でテスト
  125. #CODT2020 Section 3 のまとめ • 何をテストしているのか意識する ◦ CDK のテストあくまでも CDK

    から YAML への変換のテスト • リソースに期待する振る舞い ◦ ポリシー的な性質 / 意味論的な性質 • 生成された YAML をテストするコツ ◦ Conftest で具体的な名前に依存せずテストできる
  126. #CODT2020 #CODT2020 本日のまとめ Wrap Up!

  127. #CODT2020 本日のまとめ • IaC における静的テストの必要性 ◦ デプロイ前に予測可能性を確保したい • AWS のリソース管理と

    CDK ◦ 再現性 / 純粋性 / モジュール性 • YAML に対するテスト戦略とツール ◦ cfn-nag(セキュリティ)/ Conftest(正しく動く条件)
  128. #CODT2020 #CODT2020 Make Your IaC Predictable! Presented by チェシャ猫 (@y_taka_23)