Azure PolicyでNSG規則を管理する

この記事は更新から24ヶ月以上経過しているため、最新の情報を別途確認することを推奨いたします。

こんにちはパーソルプロセス&テクノロジーの内田です。

Azure PolicyはAzureリソースに一定のガバナンスを効かせるためのサービスとなります。具体的にはリソースのデプロイ リージョンを日本のみに限定したり、コスト管理のためのタグ付けを強制したりといった内容となります。

新規にデプロイまたは更新しようとしているリソースが、JSON形式で定義したPolicyと乖離がある場合、それを検知し必要に応じて修正するという内容になります。

この特性を生かして、設定漏れのあるNSGリソースを検知するAzure Policyを今回は作成してみました。

次のようにAzure Portal上で準拠している/いないリソースを視覚的に確認することができます。

Azure PolicyのJSONファイルを作成する

公式ドキュメントに依れば、JSONには次の要素が含まれているようです。

  • displayName (表示名)
  • description (説明)
  • mode (モード)
  • metadata (メタデータ)
  • parameters (パラメータ)
  • policyRule (ポリシー規則)
    • if (条件)
    • then (効果)

参考:ポリシー定義の構造の詳細 – Azure Policy | Microsoft Docs

policyRuleのifで条件を指定し、真のときの効果をthenに指定するだけのようです。
今回は設定漏れを検知したいので、正しい値をparametersに指定しておきます。

"parameters": {
        "reservedNsgRules": {
            "type": "array",
            "defaultValue": [
                {
                    "destinationPortRange": "8080",
                    "destinationAddressPrefix": "*",
                    "sourcePortRange": "*",
                    "sourceAddressPrefix": "*",
                    "access": "Allow",
                    "priority": 100,
                    "direction": "Inbound",
                    "description": "Reserved rule"
                },
                {
                    "destinationPortRange": "8080",
                    "destinationAddressPrefix": "*",
                    "sourcePortRange": "*",
                    "sourceAddressPrefix": "*",
                    "access": "Allow",
                    "priority": 100,
                    "direction": "Outbound",
                    "description": "Reserved rule"
                }
            ]
        }
    }

上記は宛先ポート8080, Protocol Any, 優先度 100、説明欄にReserved ruleと記されたAllow送受信規則を定義しています。続いて、policyRuleのifキーで条件を指定していきます。allOfにて論理積を表現することができるので、非演算子に次を指定することにします。

  • リソースの種類がNSGである
  • NSGの規則に指定したものが含まれない

これは次のように書くことができます。

"allOf": [
  {
    "field": "type",
    "equals": "Microsoft.Network/networkSecurityGroups"
  },
  {
    "count": {
      "value": "[parameters('reservedNsgRules')]",
      "name": "reservedNsgRule",
      "where": {
        "count": {
          "field": "Microsoft.Network/networkSecurityGroups/securityRules[*]",
          "where": {
            "allOf": [
              {
                "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].destinationPortRange",
                "equals": "[current('reservedNsgRule').destinationPortRange]"
              },
              {
                "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].destinationAddressPrefix",
                "equals": "[current('reservedNsgRule').destinationAddressPrefix]"
              },
              {
                "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourcePortRange",
                "equals": "[current('reservedNsgRule').sourcePortRange]"
              },
              {
                "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefix",
                "equals": "[current('reservedNsgRule').sourceAddressPrefix]"
              },
              {
                "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].access",
                "equals": "[current('reservedNsgRule').access]"
              },
              {
                "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].priority",
                "equals": "[current('reservedNsgRule').priority]"
              },
              {
                "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].direction",
                "equals": "[current('reservedNsgRule').direction]"
              },
              {
                "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].description",
                "equals": "[current('reservedNsgRule').description]"
              }
            ]
          }
        },
        "equals": 1
      }
    },
    "notequals": "[length(parameters('reservedNsgRules'))]"
  }
]

2つ目の非演算子内のcountは特定の条件を満たす配列要素の数をカウントするものです。これでNSGに特定の規則が含まれていないことを評価することが可能となりました。最後にthenキーにaudit(監査)を指定すれば完了です。

実際にできあがったjsonファイルは次のようになります。

{
  "mode": "All",
  "parameters": {
    "reservedNsgRules": {
      "type": "array",
      "defaultValue": [
        {
          "destinationPortRange": "8080",
          "destinationAddressPrefix": "*",
          "sourcePortRange": "*",
          "sourceAddressPrefix": "*",
          "access": "Allow",
          "priority": 100,
          "direction": "Inbound",
          "description": "Reserved rule"
        },
        {
          "destinationPortRange": "8080",
          "destinationAddressPrefix": "*",
          "sourcePortRange": "*",
          "sourceAddressPrefix": "*",
          "access": "Allow",
          "priority": 100,
          "direction": "Outbound",
          "description": "Reserved rule"
        }
      ]
    }
  },
  "policyRule": {
    "if": {
      "allOf": [
        {
          "field": "type",
          "equals": "Microsoft.Network/networkSecurityGroups"
        },
        {
          "count": {
            "value": "[parameters('reservedNsgRules')]",
            "name": "reservedNsgRule",
            "where": {
              "count": {
                "field": "Microsoft.Network/networkSecurityGroups/securityRules[*]",
                "where": {
                  "allOf": [
                    {
                      "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].destinationPortRange",
                      "equals": "[current('reservedNsgRule').destinationPortRange]"
                    },
                    {
                      "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].destinationAddressPrefix",
                      "equals": "[current('reservedNsgRule').destinationAddressPrefix]"
                    },
                    {
                      "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourcePortRange",
                      "equals": "[current('reservedNsgRule').sourcePortRange]"
                    },
                    {
                      "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefix",
                      "equals": "[current('reservedNsgRule').sourceAddressPrefix]"
                    },
                    {
                      "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].access",
                      "equals": "[current('reservedNsgRule').access]"
                    },
                    {
                      "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].priority",
                      "equals": "[current('reservedNsgRule').priority]"
                    },
                    {
                      "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].direction",
                      "equals": "[current('reservedNsgRule').direction]"
                    },
                    {
                      "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].description",
                      "equals": "[current('reservedNsgRule').description]"
                    }
                  ]
                }
              },
              "equals": 1
            }
          },
          "notequals": "[length(parameters('reservedNsgRules'))]"
        }
      ]
    },
    "then": {
      "effect": "audit"
    }
  }
}

Azure Policyを用いてメンテナンス負荷を削減することができそうですね。

いいね (この記事が参考になった人の数:1)
(↑参考になった場合はハートマークを押して評価お願いします)
読み込み中...

注意事項・免責事項

※技術情報につきましては投稿日時点の情報となります。投稿日以降に仕様等が変更されていることがありますのでご了承ください。

※公式な技術情報の紹介の他、当社による検証結果および経験に基づく独自の見解が含まれている場合がございます。

※これらの技術情報によって被ったいかなる損害についても、当社は一切責任を負わないものといたします。十分な確認・検証の上、ご活用お願いたします。

※当サイトはマイクロソフト社によるサポートページではございません。パーソルクロステクノロジー株式会社が運営しているサイトのため、マイクロソフト社によるサポートを希望される方は適切な問い合わせ先にご確認ください。
 【重要】マイクロソフト社のサポートをお求めの方は、問い合わせ窓口をご確認ください