Sooner or later someone senior asks where you stand against ISO 27001, or whichever framework your sector answers to. In most organisations that question starts a project. Someone builds a spreadsheet of controls and chases every team for screenshots, producing a report that is out of date before the meeting it was written for.
Which is odd, because the platform underneath it all ships with a compliance engine. Microsoft maintains a catalogue of just over 5,000 ready-written policy definitions and roughly 270 grouped rule-sets, including whole sets mapped control-by-control to the frameworks above. There are no charges for using Azure Policy on Azure resources. And in most estates I look at, nobody has ever switched them on.
What's in the box
Azure Policy has two building blocks. A definition is one rule: what to look for in a resource, and what to do when it matches. An initiative is a named bundle of definitions assigned as a unit, with a compliance score of its own.
I've written before about authoring a custom definition for a rule of your own. The under-used half of the product is the other one: the definitions Microsoft has already written, covering nearly every Azure service and kept current as those services change. There is a built-in rule for storage accounts that allow public access, one for VMs with no backup, one for SQL servers without auditing, one for subnets with no network security group. When someone on the team says "we should have a policy for that", the odds are Microsoft has already written it.
If you use Microsoft Defender for Cloud, you're running a built-in initiative right now: the Microsoft cloud security benchmark is assigned by default, and it's where the portal's security recommendations come from.
One assignment covers the whole estate
A definition sitting in the catalogue does nothing. It starts working when you assign it to a scope, and scope is where the economics get good. Assign an initiative at a management group and every subscription underneath inherits it, along with every resource group and every resource inside those. A subscription created next year lands under the same assignment the day it exists.
The effect is a dial, not a switch
Every rule carries an effect, and the effect is where the fear lives. Nobody wants to be the person who switched on a thousand rules on Tuesday and blocked every deployment by Wednesday. The catalogue is built for a gentler path.
Audit observes. Non-compliant resources are flagged on a report and nothing is
blocked, which makes it free to switch on in every sense. Deny stops
non-compliant resources being created at all; you graduate a rule to deny once its
audit results have gone quiet. Remediation effects such as deployIfNotExists
and modify go further and fix what they find, which is best reserved for the
boring, safe corrections like enabling a diagnostic setting. There are
other effects,
but these three carry the operating model.
Pick your framework
The part of the catalogue that earns budget conversations is the Regulatory Compliance section: initiatives whose groups mirror the control numbering of the framework itself, so the compliance page reads like the audit checklist. A sample of what's sitting there, with the number of policies each carries:
- Any sector: CIS Azure Foundations v3.0.0 (53), ISO/IEC 27001 2022 (58), SOC 2 Type 2 (307)
- UK: UK OFFICIAL and UK NHS (45), Cyber Essentials v3.1 (106), NCSC Cyber Assurance Framework v3.2 (73)
- Finance: PCI DSS v4 (269), DORA (136), SWIFT CSCF 2024 (193), SOX 2022 (90)
- Health: HITRUST/HIPAA (589)
- EU-wide: GDPR (281), NIS2 (236), and a young EU AI Act set (6)
- US federal orbit: NIST SP 800-53 Rev. 5 (696), FedRAMP High (711)
One caveat, because it's the difference between credible and naive: assigning the NIST initiative does not make you NIST-compliant. These sets cover the technically checkable slice of each framework, and an auditor will still want your processes and paperwork. What changes is the evidence. Control-by-control, per-resource compliance becomes a page you open rather than a report you assemble, and it re-checks itself continuously instead of going stale the week after the audit.
Switch one on this week
Assigning a built-in initiative in audit mode is one command. Here it is for CIS, with the lookup done by display name so there are no magic GUIDs:
az policy assignment create \
--name cis-v3-audit \
--display-name "CIS Azure Foundations v3.0.0 (audit)" \
--policy-set-definition "$(az policy set-definition list \
--query "[?displayName=='CIS Azure Foundations v3.0.0'].name | [0]" -o tsv)"
Or as Terraform, which is how it should live long-term:
data "azurerm_subscription" "current" {}
data "azurerm_policy_set_definition" "cis" {
display_name = "CIS Azure Foundations v3.0.0"
}
resource "azurerm_subscription_policy_assignment" "cis_audit" {
name = "cis-v3-audit"
display_name = "CIS Azure Foundations v3.0.0 (audit)"
subscription_id = data.azurerm_subscription.current.id
policy_definition_id = data.azurerm_policy_set_definition.cis.id
}
Initiatives that include remediation effects also want an identity block so the
assignment can act on resources; an audit-only rollout doesn't need one. Within
around half an hour the results start arriving in Portal → Policy → Compliance, or
at the command line:
az policy state summarize \
--query "policyAssignments[].{assignment:policyAssignmentId, nonCompliant:results.nonCompliantResources}" \
-o table
Expect the first number to be ugly. That's fine. An ugly number you can see beats a clean-looking estate nobody has measured, and the number only has one direction to go once people are looking at it.
When to write your own
Custom definitions still have a place: your tag schema, your naming standard, the SKUs your organisation has agreed to pay for. Those rules are yours and nobody will write them for you. The discipline is to search the catalogue first and write second. Most "we need a policy for X" requests are already sitting in the box, versioned and maintained by someone else, which is the cheapest kind of governance there is.
The assignment above, plus the queries to read the results, are in the blog-examples repo.
More useful tidbits coming — one a week.