Terraform CloudでSentinelを使ってPolicy as Codeやってみた

2022.12.19

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

「Terraform Cloudを使って、Policy as Codeでガバナンスを効かせたい」

Terraform Cloudの魅力的な機能の一つにSentinelやOPAを使って作成したポリシーを簡単に適用できることがあると思います。

今回は、Sentinelを使ったポリシー作成とワークスペースへのポリシー適用の流れについて書きます。

Terraform CloudのPolicy as Code

Terraform CloudではTeam & Governance以上のプランでSentinelやOPAを使ったポリシーを、組織やワークスペースに適用できる機能があります。

この機能を使うことで、Terraform Cloud上でインフラを作成する際にポリシーに違反している場合はデプロイできないといったことが可能です。

ポリシーの例)

  • EC2インスタンスのインスタンスサイズがt2.micro~mediumのみ作成を許可
  • Nameタグがついていないインスタンスは作成不可
  • 作成するリソースのコストが100USD/月未満の場合は許可

Install the Sentinel CLI | Terraform | HashiCorp Developer

上記のようなポリシーをTerraform Cloud上でPolicy Setとして登録して使用します。

Policy Setの概要は以下です。

図中はPolicy Setを1つのWork Spaceに関連づけているますが複数のワークスペースや組織自体に関連づけることも可能です。

Terraform Cloud Policy Set適用の流れ

Terraform CloudでPolicy Setを適用する流れを、説明していきます。 Terraform Cloudのチュートリアルを参考にしています。

ローカルにSentinel CLIをインストール

ローカルにSentinel CLIをインストールします。

Sentinel CLIを実行するためにTerraform Planのデータが必要なため、次のステップでデータを取得します。

Install the Sentinel CLI | Terraform | HashiCorp Developer

既存のワークスペースからMock Dataを取得

データの取得は簡単です。既存のワークスペースからRuns>Download Sentinel mocksを選択するだけです。

Generate Policy Mock Data | Terraform | HashiCorp Developer

mock*.sentinelというファイルがいくつかダウンロードできると思います。 plan・run・configといった形で、それぞれにポリシールールを定義できます。(今回は次のステップでplanを使用しています)

ダウンロードしたファイルの情報は、sentinel.hclにも書かれています。

sentinel.hcl

mock "tfconfig" {
  module {
    source = "mock-tfconfig.sentinel"
  }
}

mock "tfconfig/v1" {
  module {
    source = "mock-tfconfig.sentinel"
  }
}

mock "tfconfig/v2" {
  module {
    source = "mock-tfconfig-v2.sentinel"
  }
}

mock "tfplan" {
  module {
    source = "mock-tfplan.sentinel"
  }
}

mock "tfplan/v1" {
  module {
    source = "mock-tfplan.sentinel"
  }
}

mock "tfplan/v2" {
  module {
    source = "mock-tfplan-v2.sentinel"
  }
}

mock "tfstate" {
  module {
    source = "mock-tfstate.sentinel"
  }
}

mock "tfstate/v1" {
  module {
    source = "mock-tfstate.sentinel"
  }
}

mock "tfstate/v2" {
  module {
    source = "mock-tfstate-v2.sentinel"
  }
}

mock "tfrun" {
  module {
    source = "mock-tfrun.sentinel"
  }
}

Generate Policy Mock Data | Terraform | HashiCorp Developer

ローカルでPolicyを作成・テストする

ポリシー作成に必要なファイルをサンプルリポジトリを例に説明します。

sentinel.hcl

Sentinelの設定を定義しています。 先程ダウンロードしたファイルの中にあった、sentinel.hclです。

必要な部分だけ残して他を削除します。

sentinel.hcl

mock "tfplan/v2" {
  module {
    source = "mock-tfplan-v2.sentinel"
  }
}

restrict-aws-instances-type-and-tag.sentinel

ポリシーを定義するファイルです。 今回は、Nameタグ・EC2インスタンスタイプのポリシーを定義しています。

# Imports mock data
import "tfplan/v2" as tfplan

# Get all AWS instances from all modules
ec2_instances = filter tfplan.resource_changes as _, rc {
    rc.type is "aws_instance" and
        (rc.change.actions contains "create" or rc.change.actions is ["update"])
}

# Mandatory Instance Tags
mandatory_tags = [
    "Name",
]

# Allowed Types
allowed_types = [
    "t2.micro",
    "t2.small",
    "t2.medium",
]

# Rule to enforce "Name" tag on all instances
mandatory_instance_tags = rule {
    all ec2_instances as _, instance {
        all mandatory_tags as mt {
            instance.change.after.tags contains mt
        }
    }
}

# Rule to restrict instance types
instance_type_allowed = rule {
    all ec2_instances as _, instance {
        instance.change.after.instance_type in allowed_types
    }
}

# Main rule that requires other rules to be true
main = rule {
    (instance_type_allowed and mandatory_instance_tags) else true
}

mock-tfplan-v2.sentinel

作成したポリシーのテストに使用します。(長いため、抜粋しています。)

mock-tfplan-v2.sentinel

resource_changes = {
    "aws_instance.ubuntu": {
        "address": "aws_instance.ubuntu",
        "change": {
            ## ...
            "after": {
                ## ..
                "tags": {
                    "Name": "Provisioned by Terraform",
                },
                ## ..
            }
            ## ...
        },
        ## ...
    },
}

Policyのテスト

ポリシーのテストは以下のコマンドで実行できます。

$ sentinel apply restrict-aws-instances-type-and-tag.sentinel
Pass - restrict-aws-instances-type-and-tag.sentinel

Write a Sentinel Policy for a Terraform Deployment | Terraform | HashiCorp Developer

Terraform CloudでPolicy Setを作成する

Policyのテストが完了したら、実際にTerraform Cloud上で使用してみます。

sentinel.hclenforcement_levelの設定を追加します。

この設定でポリシーに対する寛容度を設定することができます。

sentinel.hcl

policy "restrict-aws-instances-type-and-tag" {
  enforcement_level = "hard-mandatory"
}
mock "tfplan/v2" {
  module {
    source = "mock-tfplan-v2.sentinel"
  }
}

例) - ポリシー違反していても、ログに残してTerraformの実行を許可 - ポリシー違反していたら、Terraform実行を拒否

詳しくは以下をご確認ください。

Upload Your Sentinel Policy Set to Terraform Cloud | Terraform | HashiCorp Developer

上記の変更が完了してGithubにも反映したらTerraform Cloudのコンソールから、Policy Setsを選択します。 ワークスペースを作る時と同様に、Githubリポジトリと接続することができます。

Policyの適用範囲を設定して、Policy Sets作成します。

Policy Setをワークスペースに適用して、実行されることを確認する

最後にPolicyが適用されているかをワークスペースで確認します。

ルール外のインスタンスサイズで作成しようとすると、以下のようにポリシーでエラーが出ます。

インスタンスサイズ変更後は、Apply可能な状態になりました。

Control Costs with Policies | Terraform | HashiCorp Developer

おわりに

Terraform CloudでSentinelを使ったPolicy適用でした。

Terraformリポジトリが複数ある場合、Policyを一括して適用するのは少し手間がかかるかもしれません。

Terraform Cloudを使用すれば、簡単に組織やワークスペースに対してPolicyを適用できて運用を楽にできそうです。

以上、AWS事業本部の佐藤(@chari7311)でした。