Azure Policyで実現するRGタグ継承と自動同期:組み込みModify・修復タスク・カスタムPolicy・Event Grid完全ガイド

Azureの大規模運用では「リソースグループ(RG)に付けたタグを、配下の全リソースへ確実に反映する」ことがガバナンスの入口です。本記事では、組み込みのAzure Policyで新規/更新リソースにRGタグを継承させる基本から、既存資産の一括反映、RG側のタグ変更を自動で追随させる高度化(カスタムPolicy、Event Grid+Functions/Logic Apps)まで、実運用に耐える設計・手順・コード例をまとめて解説します。

目次

なぜ「RGのタグ継承」を自動化するのか

タグはコスト配賦、責任分界、セキュリティ境界、運用自動化の鍵です。しかし人の手による付け忘れ・表記揺れ・値の齟齬は必ず起こります。そこでRGに正しいタグを付けるだけで、配下の全リソースへ統一反映される仕組みを用意すると、運用負荷とミスを劇的に減らせます。Azure PolicyのModify効果と修復タスク(Remediation)を使えば、Azureネイティブだけで実現できます。

結論(クイックアンサー)

やりたいこと方法補足
新規または更新されたリソースにRGのタグを自動付与組み込みポリシー「Inherit a tag from the resource group」をEffect = Modifyで割り当て。割り当て後に作成/更新されるリソースで、対象タグが存在しない場合のみRGからコピーされます。
既存リソースにもタグを一括反映ポリシーの修復タスク(Remediation)を実行。ポリシー割り当てに付与されたマネージドIDへTag Contributor以上の権限を付けること。
RGタグを変更したらリソース側も自動更新カスタムAzure Policy:タグ値がRGと異なる場合に置換する条件+Modify。 Event Grid + Azure Functions/Logic Apps:RGタグ変更イベントで配下リソースのタグを更新。組み込みは「タグが無い」場合のみ動くため、値の差分更新まで自動化するならカスタムやイベント駆動が必要。

前提と用語

  • 対象スコープ:管理グループ / サブスクリプション / リソースグループにポリシーを割り当て可能。最小はRG、推奨はサブスクリプション以上。
  • 権限:
    • 人間(割り当て・修復開始):Policy Contributor またはSecurity Admin相当。
    • ポリシー割り当てのマネージドID(実際にタグを書き込む):Tag Contributor(またはContributor)。
  • 対象外:一部の子リソースや古いリソース種別はタグ非対応。非対応リソースは「NotApplicable」となり変更されません。

組み込みポリシーを使った実装(最短ルート)

Azure Portal での設定手順

  1. [ポリシー] → [定義] で Inherit a tag from the resource group を検索。
  2. [割り当て] をクリックし、適用範囲(RGまたはサブスクリプション)を指定。
  3. [パラメータ] で Tag Name に継承したいタグ名(例:Environment)を入力。
  4. [効果(Effect)] は Modify を選択。
  5. マネージドID(システム割当)を有効化して割り当てを完了。
  6. 割り当て直後にロール割り当てで、このマネージドIDへ Tag Contributor を付与。

これで、割り当て後に作成・更新されるリソースに、対象タグが無ければRGの値がコピーされます。

既存リソースへ一括反映(修復タスク)

  1. 対象のポリシー割り当てを開き、[修復] を選択。
  2. 修復ジョブのスコープを選び、開始します(多数のリソースに及ぶ場合は時間がかかります)。

修復は評価結果(Non-compliant)のリソースを順次更新します。Activity Logが増えるため、本番ではウィンドウを設けて実行すると安全です。

CLI/PowerShellでの自動化

Azure CLI

# 1) 組み込みポリシー定義IDを取得
POLICY_DEF_ID=$(az policy definition list \
  --query "[?displayName=='Inherit a tag from the resource group'] | [0].id" -o tsv)

# 2) ポリシーをRGへ割り当て(Effect=Modify, MI有効)

SUB_ID=
RG=
az policy assignment create 
--name "inherit-tag-environment" 
--scope "/subscriptions/$SUB_ID/resourceGroups/$RG" 
--policy "$POLICY_DEF_ID" 
--identity-type SystemAssigned 
--params '{ "tagName": { "value": "Environment" } }'

# 3) 割り当てのマネージドIDへTag Contributorを付与

MI_PRINCIPAL_ID=$(az policy assignment show 
--name "inherit-tag-environment" 
--scope "/subscriptions/$SUB_ID/resourceGroups/$RG" 
--query "identity.principalId" -o tsv)

az role assignment create 
--assignee-object-id "$MI_PRINCIPAL_ID" 
--assignee-principal-type ServicePrincipal 
--role "Tag Contributor" 
--scope "/subscriptions/$SUB_ID/resourceGroups/$RG"

# 4) 既存リソースへ修復タスク

az policy remediation create 
--name "remediate-inherit-env" 
--policy-assignment "inherit-tag-environment" 
--resource-group "$RG" 

PowerShell(Azモジュール)

$def = Get-AzPolicyDefinition | Where-Object {$_.Properties.DisplayName -eq 'Inherit a tag from the resource group'}
$scope = "/subscriptions/<subId>/resourceGroups/<rgName>"

$assignment = New-AzPolicyAssignment `  -Name "inherit-tag-environment"`
-PolicyDefinition $def `  -Scope $scope`
-Location "japaneast" `  -PolicyParameterObject @{ "tagName" = @{ "value" = "Environment" } }`
-IdentityType SystemAssigned

# MIへTag Contributorを付与

New-AzRoleAssignment `  -ObjectId $assignment.Identity.PrincipalId`
-RoleDefinitionName "Tag Contributor" `
-Scope $scope

# 修復

Start-AzPolicyRemediation -Name "remediate-inherit-env" -PolicyAssignmentId $assignment.Id -ResourceGroupName "" 

Infrastructure as Code での再現

Bicep(組み込みポリシー割り当て)

param tagName string = 'Environment'
param assignmentName string = 'pa-inherit-tag-env'
param policyDefinitionId string = '/providers/Microsoft.Authorization/policyDefinitions/<GUID_of_built_in_policy>'

resource pa 'Microsoft.Authorization/policyAssignments@2022-06-01' = {
name: assignmentName
scope: resourceGroup()
identity: {
type: 'SystemAssigned'
}
properties: {
displayName: 'Inherit RG tag: ${tagName}'
policyDefinitionId: policyDefinitionId
enforcementMode: 'Default'
parameters: {
tagName: { value: tagName }
}
}
} 

Terraform

data "azurerm_policy_definition" "inherit" {
  display_name = "Inherit a tag from the resource group"
}

resource "azurerm_policy_assignment" "inherit_env" {
name                 = "inherit-tag-env"
scope                = azurerm_resource_group.rg.id
policy_definition_id = data.azurerm_policy_definition.inherit.id
parameters           = jsonencode({ tagName = { value = "Environment" } })
identity {
type = "SystemAssigned"
}
}

resource "azurerm_role_assignment" "mi_tag_contributor" {
scope                = azurerm_resource_group.rg.id
role_definition_name = "Tag Contributor"
principal_id         = azurerm_policy_assignment.inherit_env.identity[0].principal_id
} 

「値の差分も自動更新」するカスタムPolicy(サンプル)

組み込みは「タグが無いときだけ追加」です。RGの値変更を既存リソースへ置換したい場合は、次のようなカスタムPolicyを作成します(単一タグ版)。

{
  "properties": {
    "displayName": "Inherit &amp; Sync a tag from the RG",
    "mode": "All",
    "parameters": {
      "tagName": {
        "type": "String",
        "metadata": { "displayName": "Tag Name" }
      }
    },
    "policyRule": {
      "if": {
        "anyOf": [
          { "field": "[format('tags[''{0}'']', parameters('tagName'))]", "exists": false },
          {
            "not": {
              "field": "[format('tags[''{0}'']', parameters('tagName'))]",
              "equals": "[resourceGroup().tags[parameters('tagName')]]"
            }
          }
        ]
      },
      "then": {
        "effect": "modify",
        "details": {
          "roleDefinitionIds": [
            "/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7"  // Tag Contributor
          ],
          "operations": [
            {
              "operation": "addOrReplace",
              "field": "[format('tags[''{0}'']', parameters('tagName'))]",
              "value": "[resourceGroup().tags[parameters('tagName')]]"
            }
          ]
        }
      }
    }
  }
}

このポリシーを割り当てると、タグ未設定と値不一致の両方が修復対象になり、修復タスクや将来の更新時にRGの値へ自動整合されます。複数タグを扱うには、イニシアチブ(Policy Set)に同ポリシーをタグごとに組み込み、共通パラメータで一括割り当てするのが管理しやすいです。

RGタグ変更をトリガに「即時反映」するイベント駆動

「RGのタグ値を更新したら、数分以内に配下リソースも更新したい」場合は、Azure Policyだけでは待ち時間が読みにくいことがあります。そこでActivity Log(Event Grid)をトリガに自動更新します。

  1. Event Grid サブスクリプション:イベント種類は Microsoft.Resources.ResourceWriteSuccess、サブジェクトフィルターで .../resourceGroups/<RG名> を対象にします。
  2. Azure Functions/Logic Apps:受信イベントの対象RG名を抽出し、RGのタグを取得 → RG配下リソースを列挙 → 各リソースへタグの addOrUpdate を実行。
  3. 実行IDの権限:実行アプリのマネージドIDへRGスコープでTag Contributorを付与。

Functions(JavaScript)例(概念)

module.exports = async function (context, event) {
  const sub = process.env['SUBSCRIPTION_ID'];
  const rgName = event.subject.split('/resourceGroups/')[1].split('/')[0];

// 1) RGのタグを取得(Azure SDK: @azure/arm-resources)
// 2) RG配下のリソースを列挙(listByResourceGroup)
// 3) 各リソースのtagsにRGの対象タグを加筆/置換して更新
// ※ 実装ではリトライ/スロットリング/並列度制御を考慮
}; 

Event Gridは変更イベントに即応できるため、タグ整合のSLAを短くしたいユースケースに適します。

テスト手順(安全に動作確認)

  1. ステージングRGを作成し、RGに Environment=Staging 等のタグを付与。
  2. 組み込みポリシーを割り当て(Effect=Modify、MI+Tag Contributor)。
  3. RG内にテストVMやStorageを作成 → 生成直後にタグが継承されることを確認。
  4. 既存リソースのタグを意図的に削除し、修復タスクで復元されるか確認。
  5. RGのタグ値を Staging → UAT に変更。
    • 組み込みのみ:新規更新時にしか置換されないことを理解。
    • カスタムPolicy or Event駆動:差分置換されることを確認。

よくあるつまずきと対処

症状原因対処
修復が失敗する/進まないポリシー割り当てのマネージドIDに書き込み権限が無いRG/サブスクリプションスコープでTag Contributor(またはContributor)を付与
一部リソースにタグが付かないリソース種別がタグ非対応、または子リソースでタグ不可親リソースへ付与、別の属性で管理、または除外スコープに配置
RGタグ変更が配下にすぐ反映されない組み込みポリシーは「タグ未設定のみ」、評価サイクル依存カスタムPolicy(差分置換)+修復、またはEvent Gridで即時反映
複数タグを一気に継承したい単一タグ前提の定義を多重管理している同一カスタムPolicyをタグ数分パラメータ違いでイニシアチブ化
監査だけ先にやりたいいきなりModifyだと運用に影響が読めないまずAuditで傾向把握 → 段階的にModifyへ切替

運用設計(スコープ・例外・監査)

  • 割り当てスコープ:原則はサブスクリプション全体。特別なタグ方針があるRGは除外スコープ(Exclusions)へ。
  • 命名規則:タグ名は Environment / Owner / CostCenter 等を英語に統一。値はコード体系化。
  • 監査:最初はAuditで「欠落」「不一致」を可視化。レポートを合意してからModifyへ。
  • 分離:本番・共通基盤は別イニシアチブにして適用/修復の時間帯を棲み分け。
  • IaC組み込み:Bicep/Terraformでポリシー割り当て・ロール割り当て・修復(初回)まで自動化。

セキュリティと最小権限

  • ポリシー割り当てのマネージドIDは最小権限で。タグ操作のみならTag Contributorで十分。
  • 修復タスクの実行権限(人)は、Change管理に沿って限定。実行ログはActivity Logで監査。
  • Event駆動の実行IDもRGスコープに限定し、不要なサブスクリプション全域付与は避ける。

パフォーマンスとコストの注意

  • Modify/修復はリソース数に比例してAPI呼び出しが増えます。大規模修復は時間帯を分散。
  • Azure Policy自体に課金はありませんが、修復に伴うActivity Logの増加やFunctions実行の費用は考慮。

チェックリスト(導入〜運用)

  • RGに正しいタグ体系を定義・付与したか。
  • 組み込みポリシー(Effect=Modify)を正しいスコープに割り当てたか。
  • ポリシー割り当てのマネージドIDへTag Contributorを付与したか。
  • ステージングで修復タスクを試し、影響範囲と所要を把握したか。
  • 値の差分更新が必要ならカスタムPolicy/イベント駆動を設計したか。
  • IaC化し、再現性を担保したか。

付録:よく使うコマンド&スクリプト断片

タグの状態確認(CLI)

az resource list -g &lt;rgName&gt; --query "[].{name:name,type:type,tags:tags}" -o table

カスタムPolicyの割り当て(CLI)

# 事前にポリシー定義(custom)を作成済みとして
az policy assignment create \
  --name "sync-tag-environment" \
  --scope "/subscriptions/&lt;subId&gt;/resourceGroups/&lt;rgName&gt;" \
  --policy "/subscriptions/&lt;subId&gt;/providers/Microsoft.Authorization/policyDefinitions/&lt;customPolicyName&gt;" \
  --identity-type SystemAssigned \
  --params '{ "tagName": { "value": "Environment" } }'

まとめ

RGタグ継承は、組み込みポリシー(Modify)+修復タスクで最小コスト・最短時間に実現できます。さらに、カスタムPolicyで値の差分置換まで広げれば、RGを真の「タグの単一情報源(Single Source of Truth)」にできます。即時性が重要ならEvent Grid+Functions/Logic Appsで変更通知に追随。最後はIaCで配布し、権限と運用窓口を整備して完成です。これで「RGのタグを更新すると、同じタグがリソース側にも付く」仕組みをAzureネイティブだけで堅牢に運用できます。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次