Terraform Cloudの魅力的な機能としてPolicy as codeの機能があります。
Terraformで管理するシステムの規模が大きくなってきたり、システム数が増えてくると、設定のレビューをすべて人力で行うのが難しくなってくるかと思います。
そこでPolicy as codeのSentinelを利用することで、機械的にチェックできるようになり、
レビューも効率的に回せるようになってガバナンスの向上にも繋がってくるでしょう。
- Terraform CloudのPolicy as Code
- Sentinelについて
- Terraform CloudにおけるSentinelの利用方法
- 実践
- いい感じのポリシーセットを適用する
- おわりに
Terraform CloudのPolicy as Code
Terraform CloudではTeam & Governance以上のプランでPolicy as Codeの機能が利用できるようになります。
1ヶ月のトライアル版でもTeam & Governance相当の機能が利用できるのでご興味あれば誰でも触ってみることが可能です。
Policy as codeのツールとしては、Hashicorpが提供するSentinelとOSSでRego言語で記述するOPA (Open Policy Agent)の2つが対応しています。
今回はSentinelについてご紹介しようと思います。
Sentinelについて
Sentinelは独自のSentinel言語で書かれるポリシーのフレームワークです。
一般的なプログラミング言語と同様に演算子や比較、変数定義などができますし、ビルトインのパッケージ(Imports)をインポートして利用することも可能です。
特徴的なのはruleです。
ruleブロックの中で1つないし複数の評価をしてBool値を返し、そのruleを main
に渡すことでポリシーとして機能するようになります。
param a default 1 main = rule { a == 1 }
上の例であれば、a
の値がデフォルトのまま1であればtrue, それ以外の値が渡されていればfalseとなります。
Sentinelには以下のようなPlayground環境が提供されているので、Web上で挙動を確認することができます。
SentinelをTerraformと連携するためにはtfplan
, tfconfig
, tfstate
, tfrun
といったTerraformのデータブロックをimport
文で読み込ませることでそれぞれのデータにアクセスすることができるようになります。
tfrun
以外にはterraform v0.12以降のバージョンに対応したv2が用意されており、特にtfplan/v2
が利用されることが多いようです。
Import | |
---|---|
tfplan | Terraform planのデータ。主に変更されるリソースのパラメータの評価に利用する |
tfconfig | .tf のTerraform構成ファイル内の評価に利用する |
tfstate | ローカルないしリモートにあるtfstateファイルに記述されているデータを評価する |
tfrun | Terraform CloudにおけるWorkspaceの情報(コスト予測など)を評価する |
Terraform CloudにおけるSentinelの利用方法
Sentinel自体がどんなものかを簡単に紹介したところで実際にTerraform Cloudで利用してみましょう。
個別のWorkspaceではなくグローバルのサイドメニューにあるPoliciesとPolicy setsがポリシーに関するメニューです。
PoliciesはTerraform Cloud上でPolicyを直接記述するメニューで、Policy setsでポリシーとWorkspaceとの紐付けを行います。
Policy setsの中でGitHub等のVCS上のポリシーを読み込むことができるので、ポリシーごとコード管理したい場合はPoliciesメニューは利用せずにPolicy setsのみ利用すれば問題ありません。
ということでGitHubのリポジトリを作成してコードを書いていこうと思います。
実践
今回はAKSをTerraformで構築するにあたって、ポリシーとしてノードプールのVMのサイズを許可されたものかどうかをチェックしようと思います。
GitHubリポジトリに設定ファイルを作成
まずはTerraformおよびSentinelの設定ファイルを作成します。以下のようなファイル構成になります。
. ├── main.tf └── policies ├── sentinel.hcl └── vm_size.sentinel
まずチェック対象のTerraformの構成ファイルを記述します。今回はリソースグループとAKSを作っています。
main.tf
provider "azurerm" { features {} } resource "azurerm_resource_group" "rg" { name = "sentinel-test" location = "japaneast" } resource "azurerm_kubernetes_cluster" "k8s" { name = "sentinel-aks" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location dns_prefix = "sentinel-aks" tags = { Environment = "Development" } default_node_pool { name = "agentpool" vm_size = "Standard_D2_v2" node_count = 1 } network_profile { network_plugin = "azure" } identity { type = "SystemAssigned" } }
次にpolicies
ディレクトリを作成し、vm_size.sentinel
というファイルを作成します。
policies/vm_size.sentinel
import "tfplan/v2" as tfplan # Allowed VM Sizes param allowed_vm_sizes default [ "Standard_A1", "Standard_A2", "Standard_A3", "Standard_D2_v2", ] # Find all AKS clusters clusters = filter tfplan.resource_changes as _, rc { rc.mode is "managed" and rc.type is "azurerm_kubernetes_cluster" and rc.change.actions is ["create"] } # check vmsize of nodopools on clusters vm_size_allowed = rule { all clusters as name, instances { all instances.change.after.default_node_pool as _, pool { pool.vm_size in allowed_vm_sizes } } } main = rule { vm_size_allowed }
1行目ではtfplan/v2
をtfpanというエイリアス名でインポートしています。
3-9行目でallowed_vm_sizes
というリストを作成してデフォルト値を設定しています。
この値はTerraform Cloud側から値を上書きすることが可能です。
11-16行目ではtfplanからAKSのリソースを抽出しています。
12行目のfilterはリストやマップ形式のオブジェクトに対してループを回して、
カッコ内の条件分にマッチしたオブジェクトのリストないしマップを返す式です。
ちなみに13行目のrc.mode is "managed"
は、既存リソースを参照するdata型ではなくresource型であることをチェック、
14行目のrc.type is "azurerm_kubernetes_cluster"
は、リソースタイプがazurerm_kubernetes_clusterであることをチェック、
15行目のrc.change.actions is ["create"]
はこのリソースが新規作成であることをチェックしています。
18-25行目では↑で取得したAKSリソースのすべてのノードプールのVMサイズがallowed_vm_sizes
のリストに含まれるかをチェックするルールを作成しています。
27-29行目で上記ルールをmain
ルールに追加しています。
最後にpolicies/sentinel.hcl
ファイルを作成します。
これはTerraform Cloudにポリシーセットに含まれるポリシーを認識させるためのファイルとなっています。
policies/sentinel.hcl
policy "vm_size" { # source = "./vm_size.sentinel" enforcement_level = "soft-mandatory" }
同一ディレクトリ内にSentinelファイルがある場合はポリシー名とファイル名を一致させれば読み込ませられますが、ディレクトリを分けたりする場合はsource
で相対ファイルパスを指定します。
enforcement_levelはポリシーが違反した場合の動作を指定できます。各ポリシーの許容度に合わせてenforcement_levelを設定しましょう。
level | |
---|---|
hard-mandatory | ポリシーに違反した場合、Terraform CloudのApplyは停止されます。違反が解決されるまで基本的に適用できません。 |
soft-mandatory | hard-mandatoryと似ていますが、ポリシーのオーバーライドの管理権限を持つユーザーは、ケースバイケースでポリシーの違反を許容してApplyを実行することができます。 |
advisory | 実行を中断することはなく、ユーザーへの情報としてポリシーの違反のみを表示します。 |
上記のファイル群を作成し、GitHubリポジトリにコミットします。
Terraform Cloudでポリシーセットを設定
続いて作成したポリシーファイルをTerraform Cloudに読み込ませます。
グローバル設定のPolicy sets
メニューを開き、Connect anew policy set
ボタンをクリックします。
VCSとしてGitHubを選択します。
アクセス可能なリポジトリの一覧が出てくるので先程作成したリポジトリを選択します。
こちらの画面でポリシーの設定をしていきます。
Policy frameworkは今回Sentinelを利用するのでデフォルトのままSentinelにします。
Nameにはデフォルトでリポジトリ名が入るので適宜修正してください。
Policy Set Source
の部分は最初は折りたたまれているのでクリックしてメニューを展開してください。
Policies Path
でポリシーファイルが配置されているパスを記述します。今回はここに/policies
と指定する必要があります。
Scope of Policies
ですが、すべてのWorkspaceにポリシーを適用するか、あるいは適用するWorkspaceを指定するか、
後者の場合はどのWorkspaceに適用するかを指定することになります。
今回は簡易的にすべてのWorkspaceに割り当てます。
最後にConnect policy set
をクリックして問題がなければポリシーセットが適用されます。
Workspaceを作成して実行
ポリシーセットが作成できたので最後にWorkspaceを作成して実行してみましょう。
適当なWorkspaceを作ってVersion ControlからGitHubの先程のリポジトリを選択しましょう。(最近微妙にUIが変わったようです)
認証の設定として、Azure側のサービスプリンシパルの作成とTerraform Cloud側の変数設定を行ってください。
こちらのやり方は以前の記事で紹介しているのでご参考ください。
設定が終わったらWebUIもしくはリポジトリに何かしらのコミットをして実行してみましょう。
すると…
このようにPlanの実行の後にPolicy Checkが実施されています。
今回はvm_size
のStandard_D2_v2
がallowed_vm_sizes
に含まれているのでvm_size_allowed
のルールにパスしていますね。
ちなみに今回ポリシーとして設定したvm_size_allowed
ルールの直上の18行目のコメントがDescriptionとして表示されています。
Planだけでポリシーチェックができますので、今回はDiscard Run
をクリックして実行を中止しましょう。
パラメーターを上書きしてポリシー違反の動作確認
今度はパラメーターを上書きしてポリシー違反となるか確認してみましょう。
再度Policy Setsのメニュー画面に移動して、先程作成したポリシーセットをクリックして再度メニューを開きます。
するとUpdate policy set
ボタンの下にSentinel Paramaters
というブロックが増えています。
Sentinelファイルに設定したparamの値は、ここのKey/Valueを設定することで上書きすることができます。
Keyにallowed_vm_sizes
、Valueに[ "Standard_A1", "Standard_A2", "Standard_A3"]
とStandard_D2_v2
を除外して上書きしてみましょう。
入力が終わったらSave parameter
でパラメーターをセーブします。
それでは再度Workspaceに戻り、Plan and Applyを実行してみましょう。
すると今度はvm_size_allowed
がfalse
となりポリシーチェックは失敗になります。
また、今回のポリシーのenforcement_level
はsoft-mandatory
ですので、実行を中止することもできますし、
権限のあるユーザであればOverride & Continue
を選択してポリシー違反を許容してApplyに進めることも選択できます。
このような形でpolicy as codeが実現されました。
いい感じのポリシーセットを適用する
以上でSentinelの適用方法は理解できたかと思いますが、
いい感じのポリシーを書くのはSentinel言語に習熟する必要があったり手間があります。
そこでこちらのリポジトリをご紹介します。
Hashicorpから各種クラウドに対応したポリシーやクラウドに依存しないポリシー例が用意されています。
Azureを例に取るとVMやAppService, AKSなどのポリシーがあります。
これらのポリシー群はTerraform RegistryのPolicy Librariesに移行中のようですが、まだベータということもあってか全てのポリシーがホストされているわけではないようです。
まずはリポジトリを各々の組織にフォークして、ポリシーの取捨選択やパラメーターの調整などカスタマイズして使ってみると良いと思います。
今回書いた例のようにtfplan/v2
などを直接インポートせずに、共通の処理をcommon-functions内にモジュール化することでSentinelネイティブにはない比較用の関数などを再利用していますので、
これらのモジュールを活用するのも良いと思います。
オリジナルのポリシーを作成する上でも様々なSentinelコードがあるのでサンプルとしても利用できるでしょう。
また今回は紹介しませんでしたが、Sentinelではモックを使ったSentinelコードのテストができ、
そのモックやテストコードも書かれていますのでそちらも参考になると思います。
おわりに
最初に書いたようにポリシーチェック機能はシステム規模が大きくなったりサービスレベルを高めるためには避けては通れないものに当たると思います。
今回のSentinelを使ったポリシーチェックのご紹介で、少しでもポリシーチェックのイメージが具体的になれば幸いです。
個人的にはOpen Policy Agentの方も少し触っているので、ニーズがあればそちらのご紹介もしたいと思います。
私達ACS事業部はAzure・AKSなどのクラウドネイティブ技術を活用した内製化のご支援をしております。
また、一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。