1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

HCLで書ける!新しいPolicy as Code "Terraform Policy"とは? ー触ってみた編ー

1
Last updated at Posted at 2026-08-17

HCLでポリシーが書けるようになった!!ということで、概要をまとめてみました。 本記事は実際の操作や画面などの紹介がメインとなっています。

Terraform Policyの概要についてはこちら


※本記事は 2026年8月時点の情報です。Terraform Policy は現在ベータ(beta)機能であり、本番環境での利用は非推奨とされています。最新情報は必ず公式ドキュメントを確認してください。
※本機能はHCP Terraform有償版の利用が前提となっています。Pay-as-Goもありますのでこれを機にぜひHCP Terraformをご検討ください!

この記事で紹介していること

HCLポリシーを作って実際にHCP Terraform上でRunを実行しtfpolicyに評価させてみました。

全体の流れ

  1. 事前準備(Beta特有の準備事項とtfpolicy CLIインストール)
  2. ポリシーを書く(.policy.hcl
  3. ローカルでテスト(tfpolicy validate / tfpolicy test
  4. VCSリポジトリに配置してpush
  5. HCP Terraformでポリシーセットを作成
  6. Runを実行して評価結果を見る
  7. わざと違反させてみる
  8. apply後の値を検証する(post-apply)

事前準備

★ベータ版特有の準備事項

現在Terraform Policy は Terraform v1.16以上が必須ですが、2026年8月時点でv1.16以上は正式版(GA)が存在せず、プレリリース版のみです。
そのためベータ版特有の事前準備が存在します。

手順1. Orgで「Show Terraform pre-releases」を有効化する

Workspaceの設定にあるバージョン選択からプレリリース版を一覧に出すため、まず以下の設定をします。

  1. HCP Terraform の Organization Settings → General を開く
  2. Show Terraform pre-releases にチェックを入れて保存

image.png

これをオンにしないと、次のステップでv1.16系がプルダウンに出てきません。

手順2. 対象WorkspaceのTerraformバージョンを 1.16.x(alphaYYYYMMDD) に変更する

  1. 対象ワークスペース(例:tfpolicy-test)→ Settings → General を開く
  2. Terraform Version のプルダウンから、alphaビルド(例:1.16.0-alpha20260715)を選択して保存

補足:1.16以降はぱっと見だとプルダウンのリスト内に出てこないので、検索窓で1.16...と入力して選択肢を表示させます。またalpha版を選ぶことに注意してください。

image.png

手順3. 対象Workspaceに個別タグ(例:Non-Pro)を付与する

ポリシーセットは「全体/プロジェクト/ワークスペース/タグ」のスコープで適用先を絞れます。
検証対象だけにポリシーを効かせたいので、対象ワークスペースにタグを付けておき、後でポリシーセットをそのタグに付与する方式が管理しやすいです。

  1. ワークスペース → Settings → Tags(またはOverviewのTags)
  2. Non-ProBeta など、識別しやすいタグを作成して付与

★GA後も必須となるであろう準備事項

ローカルに 最新版のTerraform CLIとtfpolicy CLI を用意する

ローカルでポリシーを書いてテストするために、tfpolicy CLI をインストールします(Terraform CLIとは別物で個別インストールが必要)。
tfpolicy test などを実行するtfpolicy CLIには Terraform v1.16以上 が必要なので、最新のTerraform CLIにアップグレードする必要があります。
ただし前述した通りv1.16以上は現在GAではないため、本番環境の操作に使用しない端末にインストールすることを推奨します。

# tfpolicy CLI(linux_amd64の例)
cd /tmp
curl -LO https://releases.hashicorp.com/tfpolicy/0.1.0/tfpolicy_0.1.0_linux_amd64.zip
unzip -o tfpolicy_0.1.0_linux_amd64.zip
sudo mv tfpolicy /usr/local/bin/
tfpolicy version   # => 0.1.0

STEP1. ポリシーを書く(.policy.hcl

今回使ったポリシーは以下です。ファイルは .policy.hcl 拡張子で policies/ 配下に置きます。

① EBS暗号化

# policies/ebs_encryption.policy.hcl
resource_policy "aws_ebs_volume" "encryption_required" {
  enforcement_level = "mandatory_overridable"

  enforce {
    condition     = attrs.encrypted == true
    error_message = "すべてのEBSボリュームは暗号化が必須です。"
  }
}

② 全AWSリソースにタグ必須

# policies/require_tags.policy.hcl
resource_policy "aws_*" "require_tags" {
  enforcement_level = "mandatory_overridable"

  enforce {
    # tags が null でも落ちないようにする(後述)
    condition     = core::try(core::length(attrs.tags), 0) > 0
    error_message = "すべてのAWSリソースに最低1つのタグを付与してください。"
  }
}

STEP2. ローカルでテストする(tfpolicy validate / test

HCP TerraformでPolicy setを作る前に、手元で構文チェックとテストをします。
テストは .policytest.hcl に書きます。

# tests/ebs_encryption.policytest.hcl
policytest {
  targets = ["../policies/ebs_encryption.policy.hcl"]
}

resource "aws_ebs_volume" "pass" {
  attrs = { availability_zone = "ap-northeast-3a", size = 1, encrypted = true }
}

resource "aws_ebs_volume" "fail_not_encrypted" {
  expect_failure = true
  attrs = { availability_zone = "ap-northeast-3a", size = 1, encrypted = false }
}
cd /path/to/repo
tfpolicy validate --policies=policies/
tfpolicy test --policies=policies/ --tests=tests/

まずはポリシーの構文チェックでvalidateを実行します。

image.png

次にtestを実行します。testの場合、--policies / --tests フラグはほぼ必須です(基本的にはルートフォルダもしくはtestやpoliciesフォルダの親フォルダなどで実行するため。付けないと "No policies found" になります)。
全ケースが pass になればOKです。

image.png

STEP3. VCSリポジトリに配置してpushする

ポリシーとテスト、そして検証対象のTerraform構成を、同じGitリポジトリにまとめます。
ディレクトリ構成例は以下です。

.
├── policies/
│   ├── ebs_encryption.policy.hcl
│   ├── require_tags.policy.hcl
│   └── post_apply_ebs.policy.hcl
├── tests/
    ├── ebs_encryption.policytest.hcl
    └── require_tags.policytest.hcl

STEP4. HCP Terraformでポリシーセットを作成する

ここからはHCP Terraformを操作し、ポリシーセットを作成してVCSのリポジトリとリンクさせます。
参考:VCS setup for Terraform policy

  1. HCP Terraform にサインインし、Organization を開く

  2. Settings → Policy Sets を開く

  3. Create a new policy set をクリック

  4. Source に VCS を選択(※tfpolicyは現状VCS連携方式のみです)
    image.png

  5. Policy framework に Terraform policy を選択

  6. 設定を入力:

    • Name:ポリシーセット名(例:tfpolicy-01
    • Scope of policies手順3で付けたタグ(例:beta) に紐づけて適用先を絞る

image.png

7.対象のVCSリポジトリを接続

image.png

STEP5. Runを実行して評価結果を見る

対象ワークスペースでRunを実行します。
planの前後やApply後に 「Terraform policy evaluations」 ステージが現れれば、新フレームワークがちゃんと有効になっている証拠です。

image.png

ポリシーが準拠していると、Terraform policy evaluations passed と表示され、All タブに評価済みポリシーの一覧が出ます。

image.png

STEP6. わざと違反させてみる

タグなしのEBSボリュームを1個追加して、ポリシーに違反させます。

# main.tf に追記
resource "aws_ebs_volume" "test-vol2" {
  availability_zone = data.aws_availability_zones.available.names[0]
  size              = 1
  encrypted         = true
  # tags を書かない            → 違反(mandatory_overridable)
}

再Runすると、Terraform policy evaluationsfailed になります。

image.png

STEP7. apply後の値を検証する(post-apply)

途中でApply後の値を検証するポリシーを追加してみました。

apply後の値(ARN/ID)を検証(post-apply)

# policies/post_apply_ebs.policy.hcl
resource_policy "aws_ebs_volume" "validate_post_apply" {
  enforcement_level = "mandatory"

  # id は apply後に採番される(vol-xxxx)
  enforce {
    condition     = core::startswith(attrs.id, "vol-")
    error_message = "EBSボリュームIDが不正です: ${attrs.id}"
  }

  # arn も apply後に確定。承認済みリージョンか検証
  enforce {
    condition     = core::startswith(attrs.arn, "arn:aws:ec2:ap-northeast-3:")
    error_message = "EBSボリュームが承認済みリージョンにありません: ${attrs.arn}"
  }
}

attrs. でリソース属性を参照します。plan時点では未確定(known after apply)な属性を参照すると、そのポリシーは自動的に apply後に評価されます。

image.png

ひとつ注意点としては、apply後評価はapplyを止められるわけではありません(既に作成済みのため)。
違反があっても止まるのではなく、apply後にエラーとして通知されるため、修正は別作業になります。

image.png

各クラウドサービスのポリシーと同じタイミングの検知にはなりますが、管理者のTerraform UI利用頻度が高かったり、Apply直後のちょっとしたテストに利用したりなど、活用できる場面はありそうです。

おまけ:ミニトラブル

「Failed」ではなく「Errored」になる

最初にtagなしのリソースを作成してrequire_tags を違反させたとき、Failed ではなく Errored になり、mandatory_overridable なのにオーバーライドできず強制停止してしまいました。

image.png

ログを見ると:

Error: Invalid function argument
  condition = attrs.tags != null && core::length(core::keys(attrs.tags)) > 0
- aws_ebs_volume.violation
  Error: Invalid value for "inputMap" parameter: argument must not be null.
  attrs.tags is null

原因は、tagsを丸ごと書かないと attrs.tagsnull になり、core::keys(null) の評価時に例外が発生することでした。
さらに、&& が短絡評価されず、前半の条件結果に関わらず後半の式も評価されたため、エラーが発生してしまいました。

対処としては nullでも安全な条件にしておくことが必要です。

# NG(nullでErroredになる)
condition = attrs.tags != null && core::length(core::keys(attrs.tags)) > 0

# OK(nullでも落ちず、正常にFailedになる)
condition = core::try(core::length(attrs.tags), 0) > 0

core::try(core::length(attrs.tags), 0) は、attrs.tags がnullで length が失敗したら 0 を返すので、0 > 0 が false となり 正常な違反判定(Failed) になります。
修正後は、想定通り Failed(Mandatory_overridable)になり、Overrideボタンが出るようになりました。

今回のように、最低1つ以上値を入れさせるような制御をしたいものの、null値が起こり得る場合にはこの書き方が有効だと思われます。
でもGAまでに直っていてほしいので開発チームにFeedbackしようと思います。

まとめ

conditionの記述については少し慣れが必要かもしれませんが、全体的にHCLはやはり可読性が高いなと思いました。
ベータ機能ゆえの制約や事前準備はありますが、HCLを書いたことがある方にはぜひ使い倒してほしいと思います。

まずは検証用ワークスペースで試してみてください!

参考リンク

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?