Terraform CloudでのPolicy as Code

# はじめに
みなさん、こんにちは。
terraform Advent Calendar 2020 も14日目を迎えました。

最近、業務にて頻繁にTerraform Cloudを利用しておりまして、そのTerraform CloudにてPolicy Setsという機能がありますので、今回の記事はそれを取り上げてみたいと思います。

## この記事について
### 触れること
* Terraform CloudのPolicyとPolicy set
* Hashicorp Sentinel

### 触れないこと
* 一般的なTerraformのコーディングに関するルールや説明

## Policy as Codeとは
そもそも、システムの構築や利用においては、以下のような外部要因や内部要因で様々なルールや方針があるかと思います。
　* 法律
　* コーポレート・ガバナンス
　* セキュリティ
　* 予算
などなど。
そのルールや方針の違反が無意識のうちに起こらないよう、一定の範囲内でのみ利用させることによって安全を担保しようとする制約がシステムにおけるポリシーです。
そのポリシーを、インフラの構築管理をコードで実装すること Infrastructure as Code（IaC） と呼ぶのと同様に、コード管理することをPolicy as Code（PaC) と言います。
IaC同様、PaCもコード化することによりデプロイや版管理が容易にしたり、様々なCI/CDに組み込むことが可能になります。

## Terraform CloudのPaC
HashiCorpにはPaCのためにSentinelというプロダクトが用意されています。
https://www.hashicorp.com/sentinel
Sentinel自体はCLI上で実行可能ですので、バイナリをダウンロードすることで利用可能で、Macではbrewでのインストールも用意されています。

Terraform Cloudで利用するには、2020-12-14現在 TEAM & GOVERNANCE より上位のプランが必要で、月額 $70/user と個人で試すには安いとは言えない金額ですが、30日のトライアル利用もありますので今回の記事はトライアルを利用させてもらいます。

# デモンストレーション
## 前提
AWSにて以下のような構成をTerraformを利用して環境構築を行います。
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/372095/f8bf5283-68c1-4647-ef81-4cb86d0bcf17.png)

但し、構築の際は以下の制限が課そうと思います。
 * S3は暗号化とバージョニングを有効化すること
 * ログ用バケット以外のS3はログを有効化すること
 * S3は暗号化を有効化すること
 * 月額コストが$50を上回らないこと

## 構築用コード
https://github.com/kuroseets/qiita_pac_20201214/tree/main/aws
※急ぎで書いたのでツッコまないでくださいねー

## ポリシーのコード
https://github.com/kuroseets/qiita_pac_20201214/tree/main/sentinel
４本のポリシーを作成しています。
https://github.com/kuroseets/qiita_pac_20201214/tree/main/policy_sets
ポリシーセットはsentinel.hclファイルで必要なポリシーと制限を記載します。
Terraform Cloudでは直接Policyを作成する画面があるのですが、公式ドキュメントによるとこちらはテスト用らしくパラメータの使用もできませんし、VCSやAPIを使用して登録してくれとのことです。
VCSで登録する場合は、oauthとrepositoryの指定とともにsentinel.hclのディレクトリを指定します。
sentinel.hclにはsentinelのコードを相対パスで指定してください。
同じsentinel.hclでも異なるパラメータを渡し閾値を変更した別々のポリシーセットを作成することも可能です。
ex)cost_estimateとcost_estimate_test

## 実行画面
![スクリーンショット 2020-12-13 17.03.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/372095/6c9733bb-5898-02ad-02fd-65a38f94d76e.png)
ポリシーで引っ掛かっていることが確認できますね。

## 修正後の構築コード
https://github.com/kuroseets/qiita_pac_20201214/aws_diff
修正する内容は、S3のバージョニング、ログ、暗号化を有効化していることと、コストダウンのためにm5.large→t3.microに変更しています。

## 実行画面
![スクリーンショット 2020-12-13 17.33.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/372095/3425d84a-aaf6-d8a9-8a43-908e4ff4e7af.png)
修正後のコードをGUIにてPlanを確認してみたところ、ポリシーチェックを通過していることが確認できると思います。
この様に、レビュー漏れの帽子にもなりますし、自分たちの目が届かないところでも一定の構築ルールを敷くことが可能になります。

## CLIの実行画面
<details><summary>CLI実行画面</summary><div>

```
Running plan in the remote backend. Output will stream here. Pressing Ctrl-C
will stop streaming the logs, but will not stop the plan running remotely.

Preparing the remote plan...

The remote workspace is configured to work with configuration at
aws relative to the target repository.

Terraform will upload the contents of the following directory,
excluding files or directories as defined by a .terraformignore file
at /Users/etsuo.kurose/Documents/repositories/qiita_pac_20201214/.terraformignore (if it is present),
in order to capture the filesystem context the remote workspace expects:
    /Users/etsuo.kurose/Documents/repositories/qiita_pac_20201214

To view this run in a browser, visit:
https://app.terraform.io/app/kuroseets/qiita_pac_aws/runs/run-g655H4kexfem5TLu

Waiting for the plan to start...

Terraform v0.13.5
Configuring remote state backend...
Initializing Terraform configuration...
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_alb.alb will be created
  + resource "aws_alb" "alb" {
      + arn                        = (known after apply)
      + arn_suffix                 = (known after apply)
      + dns_name                   = (known after apply)
      + drop_invalid_header_fields = false
      + enable_deletion_protection = false
      + enable_http2               = true
      + id                         = (known after apply)
      + idle_timeout               = 60
      + internal                   = false
      + ip_address_type            = (known after apply)
      + load_balancer_type         = "application"
      + name                       = "qiita-20201214-test-alb"
      + security_groups            = (known after apply)
      + subnets                    = (known after apply)
      + vpc_id                     = (known after apply)
      + zone_id                    = (known after apply)

      + access_logs {
          + enabled = false
        }

      + subnet_mapping {
          + allocation_id        = (known after apply)
          + outpost_id           = (known after apply)
          + private_ipv4_address = (known after apply)
          + subnet_id            = (known after apply)
        }
    }

  # aws_alb_target_group.alb-target-group will be created
  + resource "aws_alb_target_group" "alb-target-group" {
      + arn                                = (known after apply)
      + arn_suffix                         = (known after apply)
      + deregistration_delay               = 300
      + id                                 = (known after apply)
      + lambda_multi_value_headers_enabled = false
      + load_balancing_algorithm_type      = (known after apply)
      + name                               = "qiita-20201214-test-alb-tg"
      + port                               = 80
      + protocol                           = "HTTP"
      + proxy_protocol_v2                  = false
      + slow_start                         = 0
      + target_type                        = "instance"
      + vpc_id                             = (known after apply)

      + health_check {
          + enabled             = true
          + healthy_threshold   = 3
          + interval            = 30
          + matcher             = "200"
          + path                = "/index.html"
          + port                = "80"
          + protocol            = "HTTP"
          + timeout             = 5
          + unhealthy_threshold = 2
        }

      + stickiness {
          + cookie_duration = (known after apply)
          + enabled         = (known after apply)
          + type            = (known after apply)
        }
    }

  # aws_alb_target_group_attachment.alb-target-group-attachment[0] will be created
  + resource "aws_alb_target_group_attachment" "alb-target-group-attachment" {
      + id               = (known after apply)
      + port             = 80
      + target_group_arn = (known after apply)
      + target_id        = (known after apply)
    }

  # aws_alb_target_group_attachment.alb-target-group-attachment[1] will be created
  + resource "aws_alb_target_group_attachment" "alb-target-group-attachment" {
      + id               = (known after apply)
      + port             = 80
      + target_group_arn = (known after apply)
      + target_id        = (known after apply)
    }

  # aws_alb_target_group_attachment.alb-target-group-attachment[2] will be created
  + resource "aws_alb_target_group_attachment" "alb-target-group-attachment" {
      + id               = (known after apply)
      + port             = 80
      + target_group_arn = (known after apply)
      + target_id        = (known after apply)
    }

  # aws_eip.nat_gateway_eip[0] will be created
  + resource "aws_eip" "nat_gateway_eip" {
      + allocation_id        = (known after apply)
      + association_id       = (known after apply)
      + customer_owned_ip    = (known after apply)
      + domain               = (known after apply)
      + id                   = (known after apply)
      + instance             = (known after apply)
      + network_border_group = (known after apply)
      + network_interface    = (known after apply)
      + private_dns          = (known after apply)
      + private_ip           = (known after apply)
      + public_dns           = (known after apply)
      + public_ip            = (known after apply)
      + public_ipv4_pool     = (known after apply)
      + tags                 = {
          + "Name" = "nat-gateway-eip-qiita-20201214-test-a"
        }
      + vpc                  = true
    }

  # aws_eip.nat_gateway_eip[1] will be created
  + resource "aws_eip" "nat_gateway_eip" {
      + allocation_id        = (known after apply)
      + association_id       = (known after apply)
      + customer_owned_ip    = (known after apply)
      + domain               = (known after apply)
      + id                   = (known after apply)
      + instance             = (known after apply)
      + network_border_group = (known after apply)
      + network_interface    = (known after apply)
      + private_dns          = (known after apply)
      + private_ip           = (known after apply)
      + public_dns           = (known after apply)
      + public_ip            = (known after apply)
      + public_ipv4_pool     = (known after apply)
      + tags                 = {
          + "Name" = "nat-gateway-eip-qiita-20201214-test-c"
        }
      + vpc                  = true
    }

  # aws_eip.nat_gateway_eip[2] will be created
  + resource "aws_eip" "nat_gateway_eip" {
      + allocation_id        = (known after apply)
      + association_id       = (known after apply)
      + customer_owned_ip    = (known after apply)
      + domain               = (known after apply)
      + id                   = (known after apply)
      + instance             = (known after apply)
      + network_border_group = (known after apply)
      + network_interface    = (known after apply)
      + private_dns          = (known after apply)
      + private_ip           = (known after apply)
      + public_dns           = (known after apply)
      + public_ip            = (known after apply)
      + public_ipv4_pool     = (known after apply)
      + tags                 = {
          + "Name" = "nat-gateway-eip-qiita-20201214-test-d"
        }
      + vpc                  = true
    }

  # aws_instance.web_servers[0] will be created
  + resource "aws_instance" "web_servers" {
      + ami                          = "ami-0053d11f74e9e7f52"
      + arn                          = (known after apply)
      + associate_public_ip_address  = (known after apply)
      + availability_zone            = (known after apply)
      + cpu_core_count               = (known after apply)
      + cpu_threads_per_core         = (known after apply)
      + get_password_data            = false
      + host_id                      = (known after apply)
      + id                           = (known after apply)
      + instance_state               = (known after apply)
      + instance_type                = "t3.micro"
      + ipv6_address_count           = (known after apply)
      + ipv6_addresses               = (known after apply)
      + key_name                     = (known after apply)
      + outpost_arn                  = (known after apply)
      + password_data                = (known after apply)
      + placement_group              = (known after apply)
      + primary_network_interface_id = (known after apply)
      + private_dns                  = (known after apply)
      + private_ip                   = (known after apply)
      + public_dns                   = (known after apply)
      + public_ip                    = (known after apply)
      + secondary_private_ips        = (known after apply)
      + security_groups              = (known after apply)
      + source_dest_check            = true
      + subnet_id                    = (known after apply)
      + tags                         = {
          + "Name" = "qiita-20201214-test-web-server-a"
        }
      + tenancy                      = (known after apply)
      + volume_tags                  = (known after apply)
      + vpc_security_group_ids       = (known after apply)

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

  # aws_instance.web_servers[1] will be created
  + resource "aws_instance" "web_servers" {
      + ami                          = "ami-0053d11f74e9e7f52"
      + arn                          = (known after apply)
      + associate_public_ip_address  = (known after apply)
      + availability_zone            = (known after apply)
      + cpu_core_count               = (known after apply)
      + cpu_threads_per_core         = (known after apply)
      + get_password_data            = false
      + host_id                      = (known after apply)
      + id                           = (known after apply)
      + instance_state               = (known after apply)
      + instance_type                = "t3.micro"
      + ipv6_address_count           = (known after apply)
      + ipv6_addresses               = (known after apply)
      + key_name                     = (known after apply)
      + outpost_arn                  = (known after apply)
      + password_data                = (known after apply)
      + placement_group              = (known after apply)
      + primary_network_interface_id = (known after apply)
      + private_dns                  = (known after apply)
      + private_ip                   = (known after apply)
      + public_dns                   = (known after apply)
      + public_ip                    = (known after apply)
      + secondary_private_ips        = (known after apply)
      + security_groups              = (known after apply)
      + source_dest_check            = true
      + subnet_id                    = (known after apply)
      + tags                         = {
          + "Name" = "qiita-20201214-test-web-server-c"
        }
      + tenancy                      = (known after apply)
      + volume_tags                  = (known after apply)
      + vpc_security_group_ids       = (known after apply)

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

  # aws_instance.web_servers[2] will be created
  + resource "aws_instance" "web_servers" {
      + ami                          = "ami-0053d11f74e9e7f52"
      + arn                          = (known after apply)
      + associate_public_ip_address  = (known after apply)
      + availability_zone            = (known after apply)
      + cpu_core_count               = (known after apply)
      + cpu_threads_per_core         = (known after apply)
      + get_password_data            = false
      + host_id                      = (known after apply)
      + id                           = (known after apply)
      + instance_state               = (known after apply)
      + instance_type                = "t3.micro"
      + ipv6_address_count           = (known after apply)
      + ipv6_addresses               = (known after apply)
      + key_name                     = (known after apply)
      + outpost_arn                  = (known after apply)
      + password_data                = (known after apply)
      + placement_group              = (known after apply)
      + primary_network_interface_id = (known after apply)
      + private_dns                  = (known after apply)
      + private_ip                   = (known after apply)
      + public_dns                   = (known after apply)
      + public_ip                    = (known after apply)
      + secondary_private_ips        = (known after apply)
      + security_groups              = (known after apply)
      + source_dest_check            = true
      + subnet_id                    = (known after apply)
      + tags                         = {
          + "Name" = "qiita-20201214-test-web-server-d"
        }
      + tenancy                      = (known after apply)
      + volume_tags                  = (known after apply)
      + vpc_security_group_ids       = (known after apply)

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

  # aws_internet_gateway.internet_gateway will be created
  + resource "aws_internet_gateway" "internet_gateway" {
      + arn      = (known after apply)
      + id       = (known after apply)
      + owner_id = (known after apply)
      + tags     = {
          + "Name" = "internet-gateway-qiita-20201214-test"
        }
      + vpc_id   = (known after apply)
    }

  # aws_kms_key.kms-key will be created
  + resource "aws_kms_key" "kms-key" {
      + arn                      = (known after apply)
      + customer_master_key_spec = "SYMMETRIC_DEFAULT"
      + deletion_window_in_days  = 10
      + description              = (known after apply)
      + enable_key_rotation      = false
      + id                       = (known after apply)
      + is_enabled               = true
      + key_id                   = (known after apply)
      + key_usage                = "ENCRYPT_DECRYPT"
      + policy                   = (known after apply)
    }

  # aws_nat_gateway.nat_gateways[0] will be created
  + resource "aws_nat_gateway" "nat_gateways" {
      + allocation_id        = (known after apply)
      + id                   = (known after apply)
      + network_interface_id = (known after apply)
      + private_ip           = (known after apply)
      + public_ip            = (known after apply)
      + subnet_id            = (known after apply)
      + tags                 = {
          + "Name" = "nat-gateway-qiita-20201214-test-a"
        }
    }

  # aws_nat_gateway.nat_gateways[1] will be created
  + resource "aws_nat_gateway" "nat_gateways" {
      + allocation_id        = (known after apply)
      + id                   = (known after apply)
      + network_interface_id = (known after apply)
      + private_ip           = (known after apply)
      + public_ip            = (known after apply)
      + subnet_id            = (known after apply)
      + tags                 = {
          + "Name" = "nat-gateway-qiita-20201214-test-c"
        }
    }

  # aws_nat_gateway.nat_gateways[2] will be created
  + resource "aws_nat_gateway" "nat_gateways" {
      + allocation_id        = (known after apply)
      + id                   = (known after apply)
      + network_interface_id = (known after apply)
      + private_ip           = (known after apply)
      + public_ip            = (known after apply)
      + subnet_id            = (known after apply)
      + tags                 = {
          + "Name" = "nat-gateway-qiita-20201214-test-d"
        }
    }

  # aws_route_table.private_route_tables[0] will be created
  + resource "aws_route_table" "private_route_tables" {
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + route            = [
          + {
              + cidr_block                = "0.0.0.0/0"
              + egress_only_gateway_id    = ""
              + gateway_id                = ""
              + instance_id               = ""
              + ipv6_cidr_block           = ""
              + local_gateway_id          = ""
              + nat_gateway_id            = (known after apply)
              + network_interface_id      = ""
              + transit_gateway_id        = ""
              + vpc_endpoint_id           = ""
              + vpc_peering_connection_id = ""
            },
        ]
      + tags             = {
          + "Name" = "private-route-table-qiita-20201214-test-a"
        }
      + vpc_id           = (known after apply)
    }

  # aws_route_table.private_route_tables[1] will be created
  + resource "aws_route_table" "private_route_tables" {
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + route            = [
          + {
              + cidr_block                = "0.0.0.0/0"
              + egress_only_gateway_id    = ""
              + gateway_id                = ""
              + instance_id               = ""
              + ipv6_cidr_block           = ""
              + local_gateway_id          = ""
              + nat_gateway_id            = (known after apply)
              + network_interface_id      = ""
              + transit_gateway_id        = ""
              + vpc_endpoint_id           = ""
              + vpc_peering_connection_id = ""
            },
        ]
      + tags             = {
          + "Name" = "private-route-table-qiita-20201214-test-c"
        }
      + vpc_id           = (known after apply)
    }

  # aws_route_table.private_route_tables[2] will be created
  + resource "aws_route_table" "private_route_tables" {
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + route            = [
          + {
              + cidr_block                = "0.0.0.0/0"
              + egress_only_gateway_id    = ""
              + gateway_id                = ""
              + instance_id               = ""
              + ipv6_cidr_block           = ""
              + local_gateway_id          = ""
              + nat_gateway_id            = (known after apply)
              + network_interface_id      = ""
              + transit_gateway_id        = ""
              + vpc_endpoint_id           = ""
              + vpc_peering_connection_id = ""
            },
        ]
      + tags             = {
          + "Name" = "private-route-table-qiita-20201214-test-d"
        }
      + vpc_id           = (known after apply)
    }

  # aws_route_table.public_route_table will be created
  + resource "aws_route_table" "public_route_table" {
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + route            = [
          + {
              + cidr_block                = "0.0.0.0/0"
              + egress_only_gateway_id    = ""
              + gateway_id                = (known after apply)
              + instance_id               = ""
              + ipv6_cidr_block           = ""
              + local_gateway_id          = ""
              + nat_gateway_id            = ""
              + network_interface_id      = ""
              + transit_gateway_id        = ""
              + vpc_endpoint_id           = ""
              + vpc_peering_connection_id = ""
            },
        ]
      + tags             = {
          + "Name" = "qiita-20201214-test-public-route-table"
        }
      + vpc_id           = (known after apply)
    }

  # aws_route_table_association.private_route_table_association[0] will be created
  + resource "aws_route_table_association" "private_route_table_association" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = (known after apply)
    }

  # aws_route_table_association.private_route_table_association[1] will be created
  + resource "aws_route_table_association" "private_route_table_association" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = (known after apply)
    }

  # aws_route_table_association.private_route_table_association[2] will be created
  + resource "aws_route_table_association" "private_route_table_association" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = (known after apply)
    }

  # aws_route_table_association.public_route_table_association[0] will be created
  + resource "aws_route_table_association" "public_route_table_association" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = (known after apply)
    }

  # aws_route_table_association.public_route_table_association[1] will be created
  + resource "aws_route_table_association" "public_route_table_association" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = (known after apply)
    }

  # aws_route_table_association.public_route_table_association[2] will be created
  + resource "aws_route_table_association" "public_route_table_association" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = (known after apply)
    }

  # aws_s3_bucket.log_bucket will be created
  + resource "aws_s3_bucket" "log_bucket" {
      + acceleration_status         = (known after apply)
      + acl                         = "log-delivery-write"
      + arn                         = (known after apply)
      + bucket                      = "log-bucket-qiita-20201214-test"
      + bucket_domain_name          = (known after apply)
      + bucket_regional_domain_name = (known after apply)
      + force_destroy               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + region                      = (known after apply)
      + request_payer               = (known after apply)
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + server_side_encryption_configuration {
          + rule {
              + apply_server_side_encryption_by_default {
                  + kms_master_key_id = (known after apply)
                  + sse_algorithm     = "aws:kms"
                }
            }
        }

      + versioning {
          + enabled    = true
          + mfa_delete = false
        }
    }

  # aws_s3_bucket.web_bucket will be created
  + resource "aws_s3_bucket" "web_bucket" {
      + acceleration_status         = (known after apply)
      + acl                         = "private"
      + arn                         = (known after apply)
      + bucket                      = "web-bucket-qiita-20201214-test"
      + bucket_domain_name          = (known after apply)
      + bucket_regional_domain_name = (known after apply)
      + force_destroy               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + region                      = (known after apply)
      + request_payer               = (known after apply)
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + logging {
          + target_bucket = (known after apply)
          + target_prefix = "log/"
        }

      + server_side_encryption_configuration {
          + rule {
              + apply_server_side_encryption_by_default {
                  + kms_master_key_id = (known after apply)
                  + sse_algorithm     = "aws:kms"
                }
            }
        }

      + versioning {
          + enabled    = true
          + mfa_delete = false
        }
    }

  # aws_security_group.alb will be created
  + resource "aws_security_group" "alb" {
      + arn                    = (known after apply)
      + description            = "Allow TLS inbound traffic"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = ""
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
            },
        ]
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = "allow http"
              + from_port        = 80
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 80
            },
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = "allow https"
              + from_port        = 443
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 443
            },
        ]
      + name                   = "qiita-20201214-test-alb-sg"
      + owner_id               = (known after apply)
      + revoke_rules_on_delete = false
      + tags                   = {
          + "Name" = "qiita-20201214-test-alb-sg"
        }
      + vpc_id                 = (known after apply)
    }

  # aws_subnet.private_subnet[0] will be created
  + resource "aws_subnet" "private_subnet" {
      + arn                             = (known after apply)
      + assign_ipv6_address_on_creation = false
      + availability_zone               = "a"
      + availability_zone_id            = (known after apply)
      + cidr_block                      = "10.0.3.0/24"
      + id                              = (known after apply)
      + ipv6_cidr_block_association_id  = (known after apply)
      + map_public_ip_on_launch         = false
      + owner_id                        = (known after apply)
      + tags                            = {
          + "Name" = "subnet-qiita-20201214-test-private-a"
        }
      + vpc_id                          = (known after apply)
    }

  # aws_subnet.private_subnet[1] will be created
  + resource "aws_subnet" "private_subnet" {
      + arn                             = (known after apply)
      + assign_ipv6_address_on_creation = false
      + availability_zone               = "c"
      + availability_zone_id            = (known after apply)
      + cidr_block                      = "10.0.4.0/24"
      + id                              = (known after apply)
      + ipv6_cidr_block_association_id  = (known after apply)
      + map_public_ip_on_launch         = false
      + owner_id                        = (known after apply)
      + tags                            = {
          + "Name" = "subnet-qiita-20201214-test-private-c"
        }
      + vpc_id                          = (known after apply)
    }

  # aws_subnet.private_subnet[2] will be created
  + resource "aws_subnet" "private_subnet" {
      + arn                             = (known after apply)
      + assign_ipv6_address_on_creation = false
      + availability_zone               = "d"
      + availability_zone_id            = (known after apply)
      + cidr_block                      = "10.0.5.0/24"
      + id                              = (known after apply)
      + ipv6_cidr_block_association_id  = (known after apply)
      + map_public_ip_on_launch         = false
      + owner_id                        = (known after apply)
      + tags                            = {
          + "Name" = "subnet-qiita-20201214-test-private-d"
        }
      + vpc_id                          = (known after apply)
    }

  # aws_subnet.public_subnet[0] will be created
  + resource "aws_subnet" "public_subnet" {
      + arn                             = (known after apply)
      + assign_ipv6_address_on_creation = false
      + availability_zone               = "ap-northeast-1a"
      + availability_zone_id            = (known after apply)
      + cidr_block                      = "10.0.0.0/24"
      + id                              = (known after apply)
      + ipv6_cidr_block_association_id  = (known after apply)
      + map_public_ip_on_launch         = false
      + owner_id                        = (known after apply)
      + tags                            = {
          + "Name" = "subnet-qiita-20201214-test-public-a"
        }
      + vpc_id                          = (known after apply)
    }

  # aws_subnet.public_subnet[1] will be created
  + resource "aws_subnet" "public_subnet" {
      + arn                             = (known after apply)
      + assign_ipv6_address_on_creation = false
      + availability_zone               = "ap-northeast-1c"
      + availability_zone_id            = (known after apply)
      + cidr_block                      = "10.0.1.0/24"
      + id                              = (known after apply)
      + ipv6_cidr_block_association_id  = (known after apply)
      + map_public_ip_on_launch         = false
      + owner_id                        = (known after apply)
      + tags                            = {
          + "Name" = "subnet-qiita-20201214-test-public-c"
        }
      + vpc_id                          = (known after apply)
    }

  # aws_subnet.public_subnet[2] will be created
  + resource "aws_subnet" "public_subnet" {
      + arn                             = (known after apply)
      + assign_ipv6_address_on_creation = false
      + availability_zone               = "ap-northeast-1d"
      + availability_zone_id            = (known after apply)
      + cidr_block                      = "10.0.2.0/24"
      + id                              = (known after apply)
      + ipv6_cidr_block_association_id  = (known after apply)
      + map_public_ip_on_launch         = false
      + owner_id                        = (known after apply)
      + tags                            = {
          + "Name" = "subnet-qiita-20201214-test-public-d"
        }
      + vpc_id                          = (known after apply)
    }

  # aws_vpc.vpc will be created
  + resource "aws_vpc" "vpc" {
      + arn                              = (known after apply)
      + assign_generated_ipv6_cidr_block = false
      + cidr_block                       = "10.0.0.0/16"
      + default_network_acl_id           = (known after apply)
      + default_route_table_id           = (known after apply)
      + default_security_group_id        = (known after apply)
      + dhcp_options_id                  = (known after apply)
      + enable_classiclink               = (known after apply)
      + enable_classiclink_dns_support   = (known after apply)
      + enable_dns_hostnames             = (known after apply)
      + enable_dns_support               = true
      + id                               = (known after apply)
      + instance_tenancy                 = "default"
      + ipv6_association_id              = (known after apply)
      + ipv6_cidr_block                  = (known after apply)
      + main_route_table_id              = (known after apply)
      + owner_id                         = (known after apply)
      + tags                             = {
          + "Name" = "vpc-qiita-20201214-test"
        }
    }

Plan: 36 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Cost estimation:

Waiting for cost estimate to complete...

Resources: 5 of 11 estimated
           $39.6317419354838736/mo +$39.6317419354838736

------------------------------------------------------------------------

Organization policy check:

================== Results for policy set: cost_estimate_test ==================

Sentinel Result: true

This result means that Sentinel policies returned true and the protected
behavior is allowed by Sentinel policies.

1 policies evaluated.

## Policy 1: cost_estimate_test/cost (soft-mandatory)

Result: true

Print messages:

Proposed monthly cost 39.6317419354838736 of workspace qiita_pac_aws is under the limit: $ 50.000000

TRUE - ./cost.sentinel:33:1 - Rule "main"


===================== Results for policy set: s3_policy_set ====================

Sentinel Result: true

This result means that Sentinel policies returned true and the protected
behavior is allowed by Sentinel policies.

3 policies evaluated.

## Policy 1: s3_policy_set/logging (soft-mandatory)

Result: true

TRUE - ./logging.sentinel:18:1 - Rule "main"
## Policy 2: s3_policy_set/versioning (soft-mandatory)

Result: true

TRUE - ./versioning.sentinel:34:1 - Rule "main"
## Policy 3: s3_policy_set/encryption (soft-mandatory)

Result: true

TRUE - ./encryption.sentinel:17:1 - Rule "main"


```
</div></details>
Terraform CloudのbackendをRemoteに設定しておけば、CLI上でもCost EstimationやPolicyの評価が可能になります。

## テストTerraform Cloud環境構築用コード
https://github.com/kuroseets/qiita_pac_20201214/tree/main/tfe
テストTerraform Cloud環境の構築するコードも公開します。
tokenやorganization, oauthの変数を登録することで環境構築が可能です。

# 終わりに
ポリシーなのでわざわざCI/CDに組み込む必要はないという意見もあるかと思います。
確かにレビューにて完全に拾えるのなら不要な仕組みかもしれませんが、組織が大きくなると細部までレビューが行き届かなくなりますし、またレビュアーの負担が大きくなります。
PaCを導入することで安全にかつレビューの精度を上げながらレビュアーの負担軽減につなげられると個人的に考えています。
もっとSentinelの評価コードが公開されれば利用者も増えると思うので、その点を期待したいところです。

それでは、良いクリスマス＆年末をお過ごしください。


# 参考サイト
https://github.com/hashicorp/terraform-guides
https://github.com/ausmartway/aws-s3-security-best-practice-sentinel