Skip to main content

Azure Policy Advisor

Assess ARM template resources for Azure Policy compliance. Analyse the template, query existing subscription assignments via az policy assignment list, identify unassigned built-in and custom policies (CIS, NIST, FedRAMP), and emit a two-part report: template-fixable gaps (Part 1) and subscription-level policy assignments (Part 2). USE FOR: recommending Azure Policy assignments for an ARM template, auditing a subscription against CIS/NIST/general best practices, deciding which initiatives to assign at sub or management-group scope, distinguishing template-fixable vs platform-level governance gaps. DO NOT USE FOR: per-resource security configuration assessment (use azure-security-analyzer), RBAC role recommendations (use azure-role-selector), CAF naming abbreviations (use azure-naming-research), or pricing estimates (use azure-cost-estimator). INVOKES: az policy assignment list, az policy set-definition list, microsoft_docs_search, microsoft_docs_fetch.

Detailsโ€‹

PropertyValue
Skill Directory.github/skills/azure-policy-advisor/
PhasePre-Deploy
User Invocableโœ… Yes
Usage/azure-policy-advisor

Documentationโ€‹

Azure Policy Advisor

Recommend Azure Policy assignments for ARM template resources by combining three sources of truth: existing Azure subscription policy state (assignments + definitions), Microsoft Learn built-in recommendations, and ARM template configuration analysis. The skill analyses each resource, queries the live subscription, classifies gaps, and produces per-resource recommendations with severity ratings, built-in/custom definition IDs, and ready-to-use implementation options.

USE FORโ€‹

  • "What Azure Policies should we enforce on this template?"
  • "Audit our subscription against CIS Azure Foundations / NIST SP 800-53"
  • "Which built-in initiative covers governance for this deployment?"
  • "Split policy work between template fixes vs subscription-level assignments"
  • "Recommend baseline policy assignments for a new subscription"

DO NOT USE FORโ€‹

  • Per-resource security configuration assessment (HTTPS-only, public access, shared-key access, TLS version, etc.) โ†’ use azure-security-analyzer
  • RBAC role recommendations / least-privilege role selection โ†’ use azure-role-selector
  • CAF naming abbreviations or name-string length/character constraints โ†’ use azure-naming-research
  • Pricing or monthly cost estimation โ†’ use azure-cost-estimator
  • VM SKU / API version / quota availability checks โ†’ use azure-resource-availability
  • Comparing deployed state vs stored template state (configuration drift) โ†’ use azure-drift-detector
  • Generating a new ARM template from requirements โ†’ invoke the Azure Template Generator agent

Scope: This skill assesses (a) ARM template resources you supply and (b) the existing policy state in the target Azure subscription (active assignments + unassigned custom definitions). It does not enumerate the live configuration of deployed resources โ€” for that, use azure-drift-detector.

MCP Toolsโ€‹

ToolPurpose
microsoft_docs_searchSearch Microsoft Learn for built-in policy definitions per resource type
microsoft_docs_fetchRetrieve full Microsoft Learn pages (built-in policies index, framework initiatives)

Prerequisitesโ€‹

RequirementHow
Azure CLI logged inaz login (skill degrades gracefully โ€” see Troubleshooting if az is unavailable)
MCP server microsoft.docs.mcp availableConfigure in your MCP client; used by Step 4 to look up built-in policy IDs
ARM template OR resource type listProvided by the user or generated by Azure Template Generator

Procedureโ€‹

1. Load Compliance Context and Identify Resourcesโ€‹

Read compliance preferences from the ## Compliance & Azure Policy section in copilot-instructions.md (available automatically in conversation context). Extract:

  • Compliance frameworks (e.g., CIS Azure Foundations v3.0, NIST SP 800-53 Rev 5, general best practices)
  • Enforcement mode (Audit or Deny)
  • Policy categories (identity, networking, storage, compute, monitoring, tagging)

If no compliance section exists in copilot-instructions.md, ask the user:

Which compliance approach should I assess against?
1. General Azure best practices (recommended)
2. CIS Azure Foundations v3.0
3. NIST SP 800-53 Rev 5
4. Custom โ€” tell me what to check

Then parse the ARM template (if provided) to extract all resource types:

Extract for each resource:
- Resource type (e.g., Microsoft.Storage/storageAccounts)
- Resource name
- Current security-relevant properties (cross-reference with security-analyzer output if available)

1b. Resolve Subscription and Management Group Contextโ€‹

If {subscription-id} is not provided, discover it:

  • az account show --query id -o tsv โ€” current default subscription
  • az account list --query "[].{id:id, name:name}" -o table โ€” all subscriptions the user has access to

If {mg-name} is needed (for management-group-scoped queries) and not provided, list available management groups:

  • az account management-group list --query "[].{name:name, displayName:displayName}" -o table

If multiple subscriptions or management groups exist, ask the user which one to assess โ€” do not guess.

2. Discover Existing Policy State (Assignments + Custom Definitions)โ€‹

Before recommending new policies, discover what is already enforced in the target subscription and what custom definitions exist but are unassigned. This prevents redundant recommendations, surfaces enforcement gaps, and lets you prefer unassigned custom definitions over equivalent built-ins.

Run the discovery script:

bash .github/skills/azure-policy-advisor/scripts/discover_policy_state.sh \
--subscription "{subscription-id}" \
[--management-group "{mg-name}"] \
--output /tmp/policy-state.json

The script wraps az policy assignment list, az policy definition list, and az policy set-definition list at both subscription and (optional) management-group scope, then normalises the output into a single JSON document:

{
"subscription_id": "...", "management_group": "..." | null,
"assigned_policies": [ { policy_definition_id, enforcement_mode, scope, source: "subscription" | "management-group-inherited", ... } ],
"assigned_initiatives": [ { policy_set_definition_id, ... } ],
"unassigned_custom_policies": [ { definition_id, display_name, category, target_resource_type, effect, source: "subscription" | "management-group" } ],
"unassigned_custom_initiatives": [ { definition_id, display_name, ... } ],
"errors": [ "..." ] // non-fatal; partial results still usable
}

Use .assigned_policies keyed by policy_definition_id for the "already-assigned" lookup in Step 5. Use .unassigned_custom_policies filtered by target_resource_type matching ARM template resources to surface unassigned custom definitions that the platform team should consider assigning.

If az is not available or az login has not been run, the script exits non-zero and prints an error to stderr. Skip Step 5's assigned-vs-unassigned classification and note in the report:

โš ๏ธ Could not query Azure subscription โ€” existing assignments and custom
definitions unknown. Recommendations are based on Microsoft Learn and
template analysis only. Run `az login` and re-run for subscription-aware
assessment.

3. Verify Definition IDs Before Recommendingโ€‹

Always verify policy and initiative definition IDs from Microsoft Learn (microsoft_docs_search / microsoft_docs_fetch) or by calling az policy set-definition list --query "[?contains(displayName, 'CIS')]" -o table and az policy definition list before recommending them for assignment. Definition IDs and display names change over time and across Microsoft cloud regions (Public, Government, China). Do not rely on memorized IDs from training data โ€” emit only IDs you have verified live in this run.

4. Research Applicable Built-in Policies via Microsoft Learnโ€‹

For EACH resource type, query Microsoft Learn for current built-in policy definitions:

Tool: microsoft_docs_search
Query: "Azure Policy built-in {resource-type-category}"

Examples:
- "Azure Policy built-in Storage Accounts"
- "Azure Policy built-in App Service Function Apps"
- "Azure Policy built-in SQL Server database"
- "Azure Policy built-in Key Vault"
- "Azure Policy built-in Kubernetes AKS"
- "Azure Policy built-in virtual machines compute"
- "Azure Policy built-in network security"
- "Azure Policy built-in monitoring diagnostic settings"

For compliance frameworks, also query:

Tool: microsoft_docs_search
Query: "Azure Policy built-in initiative {framework-name}"

Examples:
- "Azure Policy built-in initiative CIS Azure Foundations"
- "Azure Policy built-in initiative NIST SP 800-53"
- "Azure Policy regulatory compliance initiative"

When search results reference a high-value page, use microsoft_docs_fetch to retrieve the full content:

Tool: microsoft_docs_fetch
URL: https://learn.microsoft.com/azure/governance/policy/samples/built-in-policies

Use this to get the complete list of built-in policies organized by category (Storage, App Service, SQL, Key Vault, Network, Monitoring, etc.)

Key Microsoft Learn reference pages: Read references/ms-learn-policy-pages.md when you need a specific Microsoft Learn URL (canonical built-in policies list, framework-specific pages for CIS/NIST/FedRAMP/PCI-DSS, ARM assignment syntax) โ€” it lists the high-value entry points with guidance on which to fetch when.

5. Classify and Prioritize Recommendationsโ€‹

For each recommended policy, assign a severity tier, classify its current status against the ARM template and Steps 2โ€“3 inventory, and resolve precedence when multiple sources cover the same control. Read references/classification-rules.yaml for the full rule set โ€” it defines the four severity tiers (Critical/High/Medium/Low with Audit/Deny effects), the five status classifications (โœ… Already assigned, ๐Ÿ”ต Compliant via template, ๐ŸŸฃ Unassigned custom available, โš ๏ธ Gap, ๐Ÿ”„ Complementary), and the three-rule precedence ladder (assigned_wins โ†’ custom_over_builtin โ†’ builtin_fallback).

Per resource type, prioritize policy categories ranked by severity. Read references/per-resource-policy-priorities.md when classifying recommendations for any of these resource types: Storage Accounts, App Service / Function Apps, SQL Servers / Databases, Key Vault, Compute / VMs, AKS / Kubernetes, Networking, or general cross-cutting controls. For resource types not in that list, fall back to the microsoft_docs_search query template in Step 4.

6. Generate Policy Recommendations Reportโ€‹

Present findings split into two clear action tracks: template improvements (changes to the ARM template) and subscription-level actions (policy/initiative assignments). This separation clarifies who needs to act and where.

Report outline โ€” Read references/policy-assessment-template.md before producing the final markdown report. The template includes the canonical heading order, table column definitions for each section, and example rows. Skim the headings here, then fetch the full template:

## Azure Policy Compliance Assessment
**Scope** ยท **Deployment** ยท **Compliance Framework** ยท **Enforcement Mode** ยท **Subscription Policy State**

### Summary โ€” table of {Recommended, Already Assigned, Template Compliant, Template Fixable, Subscription-Level Gap} per category

## Part 1: Template Improvements (developer acts; edit the ARM template)
### Gaps Fixable in the Template โ€” table of {Resource Type, Gap, Compliance Control, Fix}
### Already Compliant in Template โ€” table of {Resource Type, Property, Template Value, Compliance Control}

## Part 2: Subscription-Level Actions (platform team acts; assign at sub/mg scope)
### Existing Policy Assignments โ€” table of {Policy/Initiative, Scope, Enforcement, Type, Relevant?}
### Unassigned Custom Policies โ€” table of {Policy, Category, Target Resource, Effect, Scope}
### Recommended Built-in Assignments โ€” table of {Policy, Effect, Severity, Definition ID, Category, Source}
### Recommended Compliance Initiative โ€” table of {Initiative, Policies, Built-in ID, Status}

7. Provide Implementation Optionsโ€‹

Split implementation guidance into both tracks:

Track 1: Template Changes (for gaps in Part 1)

For each gap listed in Part 1, provide the exact ARM template JSON to add. Include:

  • The full resource definition (type, apiVersion, name, properties)
  • Required dependsOn references
  • Any new variables needed (e.g., Log Analytics workspace name)

Present as ready-to-copy code blocks that the developer can insert into the nested template's resources array. Group related resources together (e.g., Log Analytics workspace + all diagnostic settings that depend on it).

Track 2: Subscription-Level Assignments (for gaps in Part 2)

Option A: Azure CLI (quickest for individual policies)

# Assign unassigned custom policies (prioritize โ€” they already exist)
az policy assignment create \
--name "{custom-policy-short-name}" \
--display-name "{custom policy display name}" \
--policy "{full-custom-definition-id-with-mg-scope}" \
--scope "/subscriptions/{subscription-id}" \
--enforcement-mode DoNotEnforce

# Assign a built-in policy at subscription scope
az policy assignment create \
--name "{policy-short-name}" \
--display-name "{policy display name}" \
--policy "{built-in-definition-id}" \
--scope "/subscriptions/{subscription-id}" \
--params '{}' \
--enforcement-mode Default

# Assign a compliance initiative
az policy assignment create \
--name "{initiative-short-name}" \
--display-name "{initiative display name}" \
--policy-set-definition "{initiative-id}" \
--scope "/subscriptions/{subscription-id}"

Option B: ARM Template (for IaC-managed policies)

{
"type": "Microsoft.Authorization/policyAssignments",
"apiVersion": "2024-04-01",
"name": "{assignment-name}",
"properties": {
"displayName": "{display name}",
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/{built-in-id}",
"scope": "/subscriptions/{subscription-id}",
"enforcementMode": "Default",
"parameters": {}
}
}

Enforcement mode guidance:

ModeWhen to Use
DefaultActive enforcement โ€” new non-compliant resources are denied or audited
DoNotEnforceAudit-only โ€” evaluates compliance without blocking. Recommended for initial rollout

Policy Gateโ€‹

The policy gate is advisory โ€” it surfaces findings without blocking deployment.

### Policy Gate: ADVISORY

**Part 1 โ€” Template Compliance:**
๐Ÿ”ต {T} of {R} checks pass via template configuration
๐Ÿ”ง {F} gaps can be fixed by updating the template (see Part 1)

**Part 2 โ€” Subscription Enforcement:**
โœ… {A} policies already assigned in subscription
๐ŸŸฃ {C} custom policies available but unassigned โ€” assign for immediate coverage
โš ๏ธ {S} subscription-level gaps โ€” recommend assigning built-in policies or initiative
Enforcement coverage: {percentage}% of recommended policies actively assigned

**Action items:**

_Template (developer):_
1. ๐Ÿ”ง {list of template changes from Part 1, e.g. "Add blob soft delete", "Add diagnostic settings"}

_Subscription (platform team):_
1. ๐ŸŸฃ Assign existing custom policies: {list โ€” these already exist, just need assignment}
2. โš ๏ธ Assign built-in policies: {list of built-in policies to assign}
3. {initiative to assign if framework selected}

Output Artifactsโ€‹

When invoked during a deployment workflow, save results to the deployment directory:

FileFormatContent
policy-assessment.mdMarkdownFull assessment report (Section 4 output)
policy-recommendations.jsonJSONStructured policy data for automation

JSON structure for policy-recommendations.json โ€” Read references/policy-recommendations-schema.json when emitting the JSON sidecar. The reference includes complete field definitions, status/actionTrack enum values, and a fully-worked example. Skeleton:

{
"assessedAt": "...", "deploymentId": "...", "framework": "...",
"enforcementMode": "Audit|Deny", "subscriptionState": "queried|unavailable",
"summary": { "totalRecommended": 16, "alreadyAssigned": 2, "templateCompliant": 8,
"templateFixable": 3, "customAvailable": 1, "subscriptionGaps": 3 },
"existingAssignments": [ /* from Step 2 .assigned_policies */ ],
"unassignedCustomDefinitions": [ /* from Step 2 .unassigned_custom_policies */ ],
"templateImprovements": [ /* Part 1 gaps */ ],
"policies": [ /* full recommendation list with status + actionTrack */ ],
"initiative": { /* compliance-framework initiative if selected */ }
}

Status values for the status field:

ValueMeaningAction Track
already-assignedPolicy actively assigned in subscription (โœ…)none
template-compliantARM template satisfies the policy, not assigned (๐Ÿ”ต)subscription (assign to prevent drift)
template-fixableGap that can be closed by modifying the template (๐Ÿ”ง)template
custom-availableCustom definition exists but not assigned (๐ŸŸฃ)subscription
gapNot covered โ€” recommend built-in policy assignment (โš ๏ธ)subscription
complementaryWould add enforcement on top of existing config (๐Ÿ”„)subscription

Action track values for the actionTrack field:

ValueMeaning
templateFix by modifying the ARM template (Part 1 โ€” developer)
subscriptionFix by assigning a policy at subscription scope (Part 2 โ€” platform team)
noneNo action needed (already assigned or compliant)

Integration with Git-Apeโ€‹

  • Template Generator: After /azure-security-analyzer, optionally invoke /azure-policy-advisor to recommend subscription-level policies that complement the template
  • Onboarding: After RBAC setup, the onboarding flow captures compliance preferences and adds them to copilot-instructions.md โ€” this skill reads them automatically
  • Drift Detector: Cross-reference drift findings with policy recommendations โ€” drift items covered by assigned policies will auto-remediate

Examplesโ€‹

Example 1 โ€” Post-template recommendation (general best practices)

"I just generated an ARM template with a Storage Account, Function App, and Key Vault for production. What Azure Policies should we enforce? Separate template fixes from subscription-level assignments."

Skill activates โ†’ Step 1 reads "General best practices" + Audit from copilot-instructions โ†’ Step 2 runs discover_policy_state.sh โ†’ finds SecurityCenterBuiltIn (MCSB) initiative covers 13 of 17 recommendations โ†’ Step 6 emits Part 1 (4 template fixes: blob soft delete + 3 diagnostic settings) and Part 2 (3 monitoring built-ins to assign).

Example 2 โ€” Compliance framework audit

"Audit our subscription resources against CIS Azure Foundations v3.0 โ€” what's covered and what's missing?"

Skill activates โ†’ Step 1 captures framework: CIS Azure Foundations v3.0 โ†’ Step 2 discovers existing assignments โ†’ Step 4 fetches the CIS Azure Foundations Benchmark v3.0.0 built-in initiative ID from Microsoft Learn โ†’ Step 3 verifies the ID via az policy set-definition list --query "[?contains(displayName, 'CIS')]" โ†’ Step 6 emits per-control coverage + recommended initiative assignment.

Troubleshootingโ€‹

SymptomCauseResolution
discover_policy_state.sh exits non-zero, errors about az loginAzure CLI not authenticatedRun az login and retry. If unauthenticated by design (e.g., template-only review), the skill still produces Part 1 + recommended built-ins from Microsoft Learn, with subscriptionState: "unavailable" in the JSON sidecar
Recommended definition ID returns "not found" from az policy definition showDisplay-name drift or wrong cloud (Public vs Government vs China)Re-verify via Step 3: az policy set-definition list --query "[?contains(displayName, '<keyword>')]" -o table. Definition IDs are stable; display names evolve (e.g., "purge protection" โ†’ "deletion protection")
Skill reports โš ๏ธ for a policy already enforced via initiativeassigned_policies lookup keyed on individual policy IDs; initiative members aren't expandedCross-check assigned_initiatives in the policy-state JSON โ€” if MCSB (1f3afdf9-d0c9-4c3d-847f-89da613e70a8) is assigned, 200+ individual policies are covered indirectly. Filed as a known limitation; expansion via az policy set-definition show --query "policyDefinitions[].policyDefinitionId" is on the roadmap
microsoft_docs_search returns stale or empty resultsMCP server not configured, or Microsoft Learn page movedFall back to microsoft_docs_fetch with the canonical URL from references/ms-learn-policy-pages.md (built-in policies index)