はじめに
Azure Policy はリソースの構成にルールを適用し、組織のガバナンスを維持するためのサービスです。
しかし、ポリシーの数が増えてくると Azure Portal 上での手動管理は大変になってきます。
Policy as Code は、ポリシー定義を JSON ファイルとして GitHub で管理し、GitHub Actions で自動デプロイする手法です。
これにより、ポリシーの変更履歴をコードとして追跡でき、レビュープロセスも組み込めるようになります。
本記事では「特定のリージョン以外での Storage Account の作成を拒否する」というシナリオを例に、
GitHub リポジトリにポリシーを配置し、GitHub Actions で Azure にデプロイする一連の流れを解説します。
想定読者
- Azure Policy の基本的な概念を知っている方
- GitHub / GitHub Actions の操作に慣れている方
- Azure のガバナンスをコードで管理したい方
背景
手動管理の課題
Azure Portal でポリシーを管理していると、以下のような課題が生じます:
- 変更履歴が追えない – 誰がいつ何を変更したかが分かりにくい
- レビューの仕組みがない – ポリシー変更を他のメンバーが確認するフローがない
- 環境間の差分が生まれやすい – 本番環境とテスト環境でポリシーが異なる状態になりがち
コード管理のメリット
Policy as Code のアプローチを取ることで、以下のメリットが得られます:
- Git による変更履歴の管理 – すべての変更がコミットとして記録される
- Pull Request によるレビュー – ポリシー変更に対して承認フローを組み込める
- CI/CD による自動デプロイ –
mainブランチへのマージで自動的に Azure へ反映される - 環境の一貫性 – 同じコードから複数環境にデプロイできる
全体像
まず、Policy as Code のワークフロー全体像を確認しましょう。
graph TB
A[ポリシー定義を<br>JSON で作成] --> B[feature ブランチに<br>Push]
B --> C[GitHub Actions<br>がトリガー]
C --> D[検証環境に<br>DoNotEnforce で<br>デプロイ]
D --> E[コンプライアンス<br>結果を確認]
E --> F[PR レビュー・承認]
F --> G[main ブランチに<br>マージ]
G --> H[GitHub Actions<br>がトリガー]
H --> I[本番環境に<br>Default で<br>デプロイ]
classDef code fill:#f9f0ff,stroke:#6f42c1,color:#333;
classDef github fill:#f0f7ff,stroke:#0366d6,color:#333;
classDef staging fill:#fff8e1,stroke:#f9a825,color:#333;
classDef prod fill:#e6f3ff,stroke:#0078d4,color:#333;
class A code;
class B,F,G github;
class C,D,E staging;
class H,I prod;
ポイントは以下の通りです:
- ポリシーの変更はすべて GitHub リポジトリ を経由する
- feature ブランチへの push で検証環境に
enforcementMode: DoNotEnforceでデプロイされる(リソース作成はブロックしないが、コンプライアンス評価は実行される) - PR レビュー・承認後に main ブランチへマージすると、本番環境に
enforcementMode: Defaultでデプロイされる policies/フォルダ配下のファイルが変更されたときだけ GitHub Actions が実行される- Azure へのデプロイは Azure CLI を使い、認証には Workload Identity Federation(OIDC) を使用する
ざっくり手順
本記事では、以下の手順でポリシーの GitHub 管理を構築します:
- GitHub リポジトリにポリシー定義を作成
- フォルダ構成を決めて、ポリシールールとパラメータを JSON で定義
- サービスプリンシパルを作成してシークレットに登録
- Microsoft Entra ID でアプリ登録を行い、OIDC 用のフェデレーション資格情報を設定
- 検証環境用・本番環境用のサブスクリプション情報をそれぞれ登録
- GitHub Actions ワークフローを作成
- feature ブランチ → 検証環境(
DoNotEnforce)、main ブランチ → 本番環境(Default)の 2 段階デプロイを定義
- feature ブランチ → 検証環境(
- 検証環境でポリシーのデプロイを確認
- feature ブランチへの push でワークフローを実行し、検証環境にポリシーが作成されたことを確認
- 本番環境への反映と動作確認(Storage Account の作成テスト)
- main ブランチへのマージで本番環境にデプロイし、リソース作成が拒否されることを確認
手順 1: GitHub リポジトリにポリシー定義を作成
フォルダ構成
GitHub リポジトリに以下のフォルダ構成でポリシー定義を配置します。
公式ドキュメントの推奨構成 を参考にしつつ、シンプルにまとめています。
|
|
ポイントは以下の 4 点です:
policies/フォルダにポリシー定義をまとめる- ポリシーごとに サブフォルダ(
deny-storage-location/)を切る - ポリシー定義は ルール (
policy.rules.json) と パラメータ (policy.parameters.json) に分割する - 各ポリシーに メタデータ (
metadata.json) を配置し、ワークフローが動的に読み取る
versions/ サブフォルダを推奨していますが、本記事ではシンプルさを優先して省略しています。メタデータの作成 (metadata.json)
ワークフローが各ポリシーの定義名や割り当て情報を動的に読み取るための設定ファイルです。
このファイルがあることで、ポリシーを追加してもワークフローの修正が不要になります。
|
|
各フィールドの役割は以下の通りです:
name– ポリシー定義の一意な名前(az policy definition create --nameに使用)displayName/description– Azure Portal 上での表示名と説明mode– 評価モード(Allですべてのリソースタイプを対象にする)assignment– ポリシー割り当ての名前・表示名・パラメータ値
新しいポリシーを追加する場合は、policies/ 配下にサブフォルダを作成し、このファイルと policy.rules.json、policy.parameters.json を配置するだけで OK です。
ポリシールールの作成 (policy.rules.json)
「指定されたリージョン以外で Storage Account を作成しようとした場合に拒否する」ルールです。
|
|
ルール部分だけを抜き出しているため、非常にシンプルです。
allOf の条件で「リソースタイプが Storage Account かつ リージョンが許可リストに含まれない」場合に deny(拒否)します。
パラメータ定義の作成 (policy.parameters.json)
許可するリージョンをパラメータとして定義します。
|
|
デフォルト値として japaneast(東日本)と japanwest(西日本)を指定しています。
割り当て時にこのパラメータを上書きすることも可能です。
割り当て定義(参考用)(assign.subscription.json)
ポリシーの割り当て定義も JSON で管理しておくと、設定内容を記録できて便利です。
本記事では GitHub Actions 内で az policy assignment create を直接実行する方式を採用していますが、参考として配置しています。
|
|
{subscription-id} はご自身のサブスクリプション ID に置き換えてください。手順 2: サービスプリンシパルを作成してシークレットに登録
GitHub Actions から Azure にデプロイするために、Workload Identity Federation(OIDC) を使った認証を設定します。
従来のクライアントシークレット方式と比べて、シークレットの有効期限管理が不要で、よりセキュアです。
2-1. Microsoft Entra ID でアプリ登録を作成
まず、Azure CLI でアプリ登録とサービスプリンシパルを作成します。
|
|
2-2. サービスプリンシパルにロールを割り当て
ポリシーの作成と割り当てに必要な権限を付与します。
サブスクリプションスコープで Resource Policy Contributor ロールを割り当てます。
検証環境と本番環境で異なるサブスクリプションを使用する場合は、両方のサブスクリプションに対してロールを割り当てる必要があります。
|
|
2-3. フェデレーション資格情報(OIDC)の設定
GitHub Actions が OIDC トークンを使って Azure に認証できるようにするため、フェデレーション資格情報を作成します。
本ワークフローでは GitHub の Environments 機能(staging / production)を使用しているため、環境ごとにフェデレーション資格情報を登録します。
|
|
subject の <your-org>/<your-repo> は、ご自身の GitHub オーナー名とリポジトリ名に置き換えてください。
subject の形式は、ワークフローの構成によって変わります。 本ワークフローでは environment: staging / environment: production を指定しているため、subject claim は repo:<owner>/<repo>:environment:<environment-name> の形式になります。
environment を使わない場合は ref:refs/heads/main や :pull_request 形式を使用しますが、本記事の構成では 必ず environment: 形式を使用してください。
この設定により、GitHub Actions が実行時に発行する OIDC トークンと Azure 側のアプリ登録が信頼関係で結ばれ、クライアントシークレットなしで認証が行えるようになります。
2-4. GitHub リポジトリにシークレットを登録
GitHub リポジトリの Settings > Secrets and variables > Actions で、シークレットを登録します。
本ワークフローでは GitHub の Environments 機能(staging / production)を使い分けているため、シークレットも Repository secrets と Environment secrets を適切に使い分けます。
Repository secrets(両環境共通)
リポジトリの Settings > Secrets and variables > Actions > Repository secrets に登録します。
| シークレット名 | 値 | 説明 |
|---|---|---|
AZURE_CLIENT_ID |
アプリ登録のクライアント ID | az ad app list --display-name "github-policy-deployer" --query "[0].appId" -o tsv |
AZURE_TENANT_ID |
テナント ID | az account show --query tenantId -o tsv |
Environment secrets(環境ごとに異なる値)
リポジトリの Settings > Environments で staging と production の各環境を作成し、それぞれに以下のシークレットを登録します。
同じシークレット名 AZURE_SUBSCRIPTION_ID を使い、環境ごとに異なる値を設定します。
| 環境 | シークレット名 | 値 |
|---|---|---|
staging |
AZURE_SUBSCRIPTION_ID |
検証環境のサブスクリプション ID |
production |
AZURE_SUBSCRIPTION_ID |
本番環境のサブスクリプション ID |
ワークフローの environment: staging / environment: production の指定により、GitHub Actions が実行時に対応する環境のシークレット値を自動的に解決します。
このため、ワークフロー側では secrets.AZURE_SUBSCRIPTION_ID という単一の参照で済みます。
Environment secrets を使うメリット:
- シークレット名を統一できるため、ワークフローがシンプルになる
- 環境ごとに保護ルール(承認者の設定、デプロイ待ち時間など)を設定できる
- 本番環境(
production)に承認者を設定すれば、デプロイ前に人間の承認を必須にできる
Workload Identity Federation を使う場合、クライアントシークレットやパスワードをリポジトリに保存する必要はありません。登録するのは ID 情報のみです。
手順 3: GitHub Actions ワークフローを作成
いよいよ GitHub Actions のワークフローを作成します。
.github/workflows/deploy-policy.yml に以下の内容を配置します。
feature ブランチの push で検証環境にデプロイし、main ブランチへのマージで本番環境にデプロイする 2 段階構成です。
|
|
ワークフローの解説
このワークフローの各ポイントを詳しく見ていきましょう。
トリガー条件 (on)
|
|
push+branches: main–mainブランチへの push(マージ含む)で 本番環境デプロイジョブがトリガーされますpull_request+branches: main–mainブランチ向けの PR が作成・更新されたときに 検証環境デプロイジョブがトリガーされます。これにより、feature ブランチでの変更が自動的に検証環境に反映されますpaths: 'policies/**'–policies/フォルダ配下のファイルが変更された場合のみ実行されます。ワークフローファイルや README の変更では実行されないため、無駄な実行を防げますworkflow_dispatch– GitHub の Actions タブから手動で実行することもできます。初回デプロイやトラブルシューティング時に便利です
パーミッション (permissions)
|
|
id-token: write– Workload Identity Federation(OIDC)認証に必須の設定です。GitHub Actions が OIDC トークンを発行するために必要ですcontents: read– リポジトリのコードを checkout するための読み取り権限です
id-token: write を忘れると、azure/login ステップで「OIDC token を取得できない」旨のエラーが発生します。OIDC 認証を使う場合は必ず設定してください。Azure ログイン (azure/login@v2)
|
|
azure/login@v2 アクションに client-id、tenant-id、subscription-id を指定することで、自動的に OIDC(OpenID Connect) による認証が行われます。
クライアントシークレットを渡す必要がないのが、Workload Identity Federation の大きなメリットです。
ワークフローの environment 指定により、同じ secrets.AZURE_SUBSCRIPTION_ID でも 環境ごとに異なる値が自動的に解決されます。
ポリシーの動的デプロイ
従来はポリシーごとにデプロイステップを個別に記述する必要がありましたが、本ワークフローでは policies/*/ ディレクトリを動的にループして、すべてのポリシーを自動的にデプロイします。
|
|
処理の流れは以下の通りです:
for policy_dir in policies/*/–policies/配下のすべてのサブディレクトリをループしますmetadata.jsonの存在チェック –metadata.jsonが無いディレクトリはスキップしますjqでメタデータを読み取り – ポリシー定義名・表示名・説明などをmetadata.jsonから取得しますaz policy definition create– ポリシー定義を作成します。--rulesと--paramsは各ディレクトリ内の JSON ファイルを参照しますaz policy assignment create– ポリシー割り当てを作成します。検証環境では--enforcement-mode DoNotEnforceを付与し、本番環境では省略(Default)します
この仕組みにより、新しいポリシーを追加する際はワークフローの修正が不要です。policies/ 配下に新しいサブフォルダを作成し、metadata.json・policy.rules.json・policy.parameters.json を配置するだけで自動的にデプロイ対象になります。
az policy definition create はエラーになります。更新する場合は az policy definition update を使用してください。CI/CD で運用する場合は、スクリプト内で存在チェックを行って create と update を切り替えるのが一般的です。検証環境と本番環境の違いは --enforcement-mode のみです:
- 検証環境(
DoNotEnforce) – リソース作成をブロックせず、コンプライアンス評価のみ実行します。Azure Portal のダッシュボードで違反リソースを確認できます - 本番環境(
Default) – ポリシーに違反するリソースの作成を実際に拒否します
ジョブの使い分け (if 条件)
|
|
deploy-staging–pull_requestイベント時のみ実行されます。feature ブランチから main ブランチへの PR を作成・更新すると、自動的に検証環境にポリシーがデプロイされますdeploy-production–mainブランチへのpush(PR マージ含む)時のみ実行されますenvironment– GitHub の Environments 機能を利用しています。本番環境には承認者を設定することで、追加の保護レイヤーを設けることもできます
手順 4: 検証環境でポリシーのデプロイを確認
feature ブランチでの作業
まず、feature ブランチを作成してポリシーファイルを変更し、Pull Request を作成します。
|
|
GitHub 上で main ブランチ向けの Pull Request を作成すると、検証環境デプロイジョブ(deploy-staging)が自動的に実行されます。
検証環境でのデプロイ確認
ワークフローが正常に完了したら、検証環境の Azure CLI でポリシーが作成されたことを確認します。
|
|
実行結果の例:
|
|
|
|
実行結果の例:
|
|
検証環境では enforcementMode が DoNotEnforce なので、リソースの作成はブロックされません。
ただし、コンプライアンス評価は実行されるため、Azure Portal の ポリシー > コンプライアンス ダッシュボードで、ポリシーに違反するリソースが存在するかどうかを確認できます。
DoNotEnforce モードでのコンプライアンス評価結果を確認し、問題がなければ PR をマージして本番環境にデプロイしましょう。
手順 5: 本番環境への反映と動作確認(Storage Account の作成テスト)
PR のマージ
検証環境でのテストが問題なければ、Pull Request をレビュー・承認して main ブランチにマージします。
マージすると 本番環境デプロイジョブ(deploy-production)が自動的に実行され、enforcementMode: Default(強制)でポリシーが適用されます。
|
|
実行結果の例:
|
|
動作確認
本番環境でポリシーの enforcementMode が Default(強制)になったことを確認したら、実際にリソースの作成を試みて動作を確認します。
テスト用リソースグループの作成
|
|
テスト 1: 許可されていないリージョンでの作成(拒否されること)
東日本・西日本以外のリージョン(eastus)で Storage Account を作成しようとします。
|
|
ポリシーにより拒否され、以下のようなエラーが表示されるはずです:
テスト 2: 許可されたリージョンでの作成(成功すること)
許可されたリージョン(japaneast)で Storage Account を作成します。
|
|
こちらは正常に作成できることを確認します。
テスト後のクリーンアップ
テストで作成したリソースは削除しておきましょう。
|
|
まとめ
本記事では、Azure Policy を GitHub で管理する Policy as Code のアプローチを紹介しました。
- ポリシー定義を JSON ファイルとして GitHub リポジトリで管理 することで、変更履歴の追跡と Pull Request によるレビューが可能になりました
- Workload Identity Federation(OIDC) を使って、クライアントシークレットを保存せずにセキュアな認証を実現しました
- GitHub Actions の 2 段階デプロイにより、feature ブランチの変更は検証環境に
DoNotEnforceで、main ブランチへのマージは本番環境にDefaultでデプロイする安全なワークフローを構築しました - enforcementMode を活用して、検証環境ではコンプライアンス評価のみ行い、本番環境で強制するという段階的なアプローチを実現しました
Policy as Code を導入することで、Azure のガバナンスをチーム全体で管理・レビューできる体制が整います。
ポリシーの数が増えてきたら、イニシアティブ(ポリシーセット)を活用してグルーピングすることも検討してみてください。
参考
公式ドキュメント
- コードとしての Azure Policy ワークフローを設計する
- チュートリアル: コンプライアンスを強制するポリシーの作成と管理
- Azure Policy の定義の構造の基本
- Azure CLI を使用したポリシーの管理
GitHub Actions 関連
- azure/login - GitHub Action
- GitHub Actions での OpenID Connect を使った Azure 認証
- Workload Identity Federation の概要
更新履歴
| 日付 | 内容 |
|---|---|
| 2026/03/16 | 初版作成 |