🐷

Azure Policyの基本のホ(制御の種類)

2021/06/13に公開

はじめに

前回、"Azure Policyの基本のキ"ということでAzureポリシーの定義の読み解き方を記事にしました。
https://zenn.dev/tomot/articles/f3b216b8f82ed1
この中で次回に持ち越しとした一番大事な制御ルール、policyRuleに書ける内容についてが今回の論点です。中でも特に効果の使い分けについて書きます。

制御の種別

前回の記事で書いた通り、Azureポリシーの制御はpolicyRuleで記述していきます。このpolicyRuleはif節とthen節の2つのブロックに分かれており、デプロイしようとしたリソースがif節に記載した条件にマッチする場合、then節の効果が発動します。それぞれの書き方を見ていきます。

if節(条件)

意識すべきポイントは、if節にはリソースの正しい状態を書くのではなく、間違った状態を記述するということです。「(if)こういう(NGな状態)だったら」→「(then)こういう制御を行う」という書き方をする都合上、if節には望ましくないリソースの設定を書くことになります。

if節の書き方の詳細は下のドキュメントの通りですが、ポイントとなるところを掻い摘んでまとめます。
https://docs.microsoft.com/ja-jp/azure/governance/policy/concepts/definition-structure

if節に書く内容は「論理演算子」と「条件」にさらに細かく分かれますので、さらに掘り下げます。

論理演算子

論理演算子は3種類使えます。先ほどのドキュメントからサンプルを引っ張ってくると…

"if": {
    "allOf": [{
            "not": {
                "field": "tags",
                "containsKey": "application"
            }
        },
        {
            "field": "type",
            "equals": "Microsoft.Storage/storageAccounts"
        }
    ]
},

not

「条件に一致しなければ」という書き方をします。条件の反転を行いますので、条件部分に望ましい設定を書いた上で「望ましい状態でなければ」という書き方ができます。シンプルで一番使いますね。上の例で言えば、「所定のタグが設定されて"いなければ"」という条件になります。

allOf

条件を"and"でまとめるための演算子です。上の例で言えば、前半の「所定のタグが設定されていなければ」と後半の「リソースタイプがストレージアカウントだったら」が両方揃った場合に制御が行われることになります。また、定義はJSONで書きますのでカンマでつないでいけばいくらでも条件を増やしていくことができ、allOf句の中に書かれている条件が「全て真だった場合」制御が発動します。

anyOf

allOfと同じように使う演算子ですが、こちらは"or"で条件をまとめることになります。すなわち、こちらは「anyOf句の中に書かれている条件のうちどれか1つでも真だった場合」制御が発動するという動きにです。

以上のように条件を組み合わせてif節を組み合わせていきますが、anyOfやallOfを入れ子にすることもできるのでポリシーによってはかなり複雑なものが出来上がってしまいます。難しいポリシーを書いた場合は、自分が制御したい条件が正しく判定されているか、必ずテストするようにしましょう。

条件

条件部分も、2つの要素に分かれます。

field

リソースのパラメータのどの部分に注目するかを記述します。論理演算子の説明のところでは当たり前のように書きましたが、サンプルの条件の前者あれば「tags」というパラメータについて、後者であれば「type」について判定せよ、ということが指定されています。

条件判定

用意されたいくつかの条件判定を使います。使用できる全量は先ほどのドキュメントをみれば良いのですが、ここではよく使われる条件判定を紹介します。
https://docs.microsoft.com/ja-jp/azure/governance/policy/concepts/definition-structure#conditions

  • equals/notEquals
    上のサンプルにもある通り、filedが条件と「一致したら」という判定されます。
    別のサンプルもみてみると
      "if": {
        "allOf": [{
            "field": "type",
            "equals": "Microsoft.DocumentDB/databaseAccounts"
          },
          {
            "field": "Microsoft.DocumentDB/databaseAccounts/enableAutomaticFailover",
            "equals": "false"
          },
          {
            "field": "Microsoft.DocumentDB/databaseAccounts/enableMultipleWriteLocations",
            "equals": "false"
          }
        ]

こういった形で、リソースのタイプがCosmosDBで、自動フェイルオーバー機能と複数の書き込み場所が有効な場合、といった判定で使われます。

  • in/NotIn
    いくつかリストを指定して、「リストの中に含まれていたら」という判定を行います。前回紹介したサンプルポリシーでは、"in"を使うことで、リソースが存在しても良いリージョンを表現していました。
            "if": {
                "not": {
                    "field": "location",
                    "in": "[parameters('allowedLocations')]"
                }
            }

then節(効果)

if節にマッチしたリソースがデプロイ(作成または変更)されようとしているときに、then節に記載した効果が発動します。2021年6月時点でサポートされている効果は7種類あります。
https://docs.microsoft.com/ja-jp/azure/governance/policy/concepts/effects
ドキュメントを読むだけではわかりづらいコツがあるので、今回主に書き残したかったのはこの部分です。

# 効果 働き 制御のタイミング
1 Append デプロイ対象リソースがif節にマッチした場合、パラメータを追加してデプロイする。なお、Appendにより追加されるパラメータがもともとリクエストに含まれていて不一致な場合、Denyと同様の効果になる(パラメータの追加はできるが更新はできない)
2 Audit デプロイ対象リソースがif節にマッチした場合、アクティビティログに警告を出力する
3 AuditIfNotExists デプロイ対象リソースがif節にマッチした場合、「関連するリソースの設定」を追加判定し「マッチしない場合」にアクティビティログに警告を出力する
4 Deny デプロイ対象リソースがif節にマッチした場合、デプロイを拒否してリソース作成に失敗する
5 DeployIfNotExists AuditIfNotExistsと同様の判定を行なった上で、警告対象となる場合に「ARMテンプレート」でデプロイを行う
6 Disabled 無効なポリシーとして処理される(よって、リソースに対しては何も制御を行わない)
7 Modify Appendと基本的に同じ、ただし修正対象が「タグ」の場合に使う

効果が発動するタイミング

Azureポリシーは効果の種類によって条件判定・効果が発動するタイミングが違います。
各効果の動きや制約を見ていると不思議な動きをすることも多いのですが、このタイミングを理解すると腹落ちしやすいと思います。ということで、私の理解を図にすると↓の通りです。

技術的に正確かどうかというよりも「こういう振る舞いに見える」ということに主眼を置いています。(正しい動きは、中の人じゃないとわからないですしね。)

①Disabled

「無効」ですので、まずこのポリシーが評価されます。というよりも、このポリシーは動作させませんね、という確認が行われ、以降はチェックされなくなります。ですので先頭で

②Append/Modify

ユーザーからAzure PortalやCLI(Powershellなど)からリクエストが入ってきたのち、最初に評価されます。リクエストの中身を評価し、不足しているパラメータを追記することになりますし、追記しようとする内容と相反するパラメータが含まれる場合は拒否(Denyと同様)されます。

③Deny

次に評価されるのはDenyです。リクエストの内容=作ろうとしているリソースのパラメータがポリシーに違反する場合、リソースの作成に失敗します。よって、いわゆる「予防的統制」が働き、「そもそも違反することができない」という状態を作り出します。

④Audit

出来上がったリソースに対して、パラメータのチェックを実施します。望ましくないパラメータの場合、違反としてアクティビティログに警告を発します。Denyとの大きな違いとして、Auditは「発見的統制」になるので、違反しているリソースに気づくことができますが、違反事態を防止することはできません。
なお、Auditの判定は初回デプロイ時以降、24時間に一度行われているようですが、アクティビティログへの出力は初回のみしか行われないようです。私の知る限り、2020/6頃までは24時間毎にログ出力されていたのですが、ある時出なくなったのでサポートに問い合わせたところ、仕様変更があったとのことでした。
ポリシーによる診断結果は、AzurePortalのポリシー管理画面で確認するか、APIなどで取得する昼用がありますので、定期的にチェックして違反リソースを見つける運用を行う必要があるということです。

⑤AuditIfNotExists/DeployIfNotExists

1段階目の判定後、2段階目の判定/追加デプロイが動く効果です。特にDeployIfNotExistsの効果の場合にわかりやすいのですが、初回のリソースのデプロイ後、15分ほど経ってから追加デプロイが走ります。すなわち、DeployIfNotExistsによりリソースの設定修正を行うとしても、この15分ほどの間は違反したママということになります。また、別の理由により追加デプロイが失敗した場合は、ポリシーによる制御が効かなかったのと同じ状態ですので、これも注意が必要です。

効果の使い分けの注意点

実際にポリシーを使おうとしたときに、「Audit」は組み込みポリシーも豊富にあり、特に迷うことはないと思います。また、Denyポリシーは、Audit効果からDenyに置き換えれば動くものが多く、これも効果がわかりやすくイメージできると思います。
使い分けが難しいのが「Append」と「DeployIfNotExist」です。どちらも日本語にすれば「何かリソースをデプロイしたときに、強制的に設定を修正する」ですが、明確な使い分けがあります。

  • Append
    Appendは、デプロイしようとしているリソースに対して、パラメータを追加する動きをします。ストレージアカウントに対してAppendする例では、
"then": {
    "effect": "append",
    "details": [{
        "field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules",
        "value": [{
            "action": "Allow",
            "value": "134.5.0.0/21"
        }]
    }]
}

ネットワークACLを追加し、「ストレージアカウントをデプロイすると必ず134.5.0.0/21からの通信を許可するルールが追加される」動きをします。

  • DeployIfNotExist
    DeployIfNotExistは、ARMテンプレートのデプロイを行います。このとき、最初に判定したリソース「以外」を対象にデプロイすることがポイントになります。
    下の例で言えば、SQLDatabaseのデプロイ時に、暗号化設定を行うためのポリシーですが、
"if": {
    "field": "type",
    "equals": "Microsoft.Sql/servers/databases"
},
"then": {
    "effect": "DeployIfNotExists",
    "details": {
        "type": "Microsoft.Sql/servers/databases/transparentDataEncryption",
        "name": "current",
        "roleDefinitionIds": [
            "/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/{roleGUID}",
            "/providers/Microsoft.Authorization/roleDefinitions/{builtinroleGUID}"
        ],
        "existenceCondition": {
            "field": "Microsoft.Sql/transparentDataEncryption.status",
            "equals": "Enabled"
        },
        "deployment": {
            "properties": {
                "mode": "incremental",
                "template": {
                    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
                    "contentVersion": "1.0.0.0",
                    "parameters": {
                        "fullDbName": {
                            "type": "string"
                        }
                    },
                    "resources": [{
                        "name": "[concat(parameters('fullDbName'), '/current')]",
                        "type": "Microsoft.Sql/servers/databases/transparentDataEncryption",
                        "apiVersion": "2014-04-01",
                        "properties": {
                            "status": "Enabled"
                        }
                    }]
                },
                "parameters": {
                    "fullDbName": {
                        "value": "[field('fullName')]"
                    }
                }
            }
        }
    }
}

暗号化の設定は "Microsoft.Sql/servers/databases" のパラメータではないので、Append効果では直すことができません。(これ大事)
そこで、DeployIfNotExistsを使い、"Microsoft.Sql/servers/databases/transparentDataEncryption" のリソースをデプロイするARMテンプレートを記述します。

終わりに

今回はPolicyの制御の書き方、特に効果の使い分けについてポイントを書きました。
次回、ポリシーを使って実際に環境を制御しようとしたときに、denyができずにハマった話を書きたいと思います。

Discussion