By the end of this you have a compliance standard assigned to the Northfork estate, a breakdown separating what you own from what Microsoft answers for, one manual control attested with real evidence and an expiry date, and a written coverage statement to accompany any number that leaves your team.
How this was verified: every command, setting and value below is taken from current Microsoft documentation, checked against the product’s own command reference in August 2026, and shaped by estates I have built. The sequence has not been executed end to end as a single unbroken lab run for this article. Where a step is more likely than others to differ at a console, it says so in place.
Before you start
You need Resource Policy Contributor or Owner on the subscription and the Azure CLI. Nothing here enforces anything: regulatory initiatives are built from audit and manual effects, so this build cannot block a deployment or change a resource. It can, however, produce a number that somebody puts in front of an auditor, which is a different kind of risk and the reason step 6 exists.
SUB_ID=$(az account show --query id -o tsv)
SCOPE="/subscriptions/$SUB_ID"
ASSIGN="NFK-Asgn-Std"
Step 1. Choose the standard, and see its size first
az policy set-definition list \
--query "[?metadata.category=='Regulatory Compliance'].{name:name, display:displayName, version:version}" \
--output table
Pick the one you are actually obligated to, take its name, and read how many definitions it carries before assigning it.
STD=<the name from the previous command>
az policy set-definition show --name "$STD" --query "{
display: displayName,
version: version,
members: length(policyDefinitions),
controls: length(policyDefinitionGroups)
}" --output json
Two numbers come back and they mean different things. The member count is how many policy definitions run. The group count is how many controls of the standard are represented. They are not equal, because a control maps to zero, one or several definitions, and the gap between them is the first piece of the coverage story.
Expect wide variation between standards. As at August 2026 the HITRUST and HIPAA initiative carries 589 definitions while ISO/IEC 27001:2022 carries 58. If you are moving from an older mapping to a newer one, run this against both before you switch, because the number on the dashboard is about to change for reasons that have nothing to do with your estate.
Step 2. Separate what is yours
Count the members by policy type. This is the step that prevents a year of confused reporting, and it takes a few minutes to run once.
az policy set-definition show --name "$STD" \
--query "policyDefinitions[].policyDefinitionId" -o tsv \
| while read -r ID; do
az policy definition show --name "${ID##*/}" \
--query "[policyType, to_string(parameters.effect.allowedValues)] | join(' ', @)" -o tsv 2>/dev/null
done | sort | uniq -c | sort -rn
Expected output groups the members three ways, and each group means something different for your programme.
| Group | What it is | What you can do about it |
|---|---|---|
Static | Microsoft-owned controls. Results come from third-party audits of Microsoft’s infrastructure, not from your estate. | Nothing. They are counted and they are not yours. Know the number. |
BuiltIn offering Manual | The CMA-prefixed controls no scanner can observe. Default state Unknown. | Attest them, with evidence. Step 4. Nothing else will ever move them. |
BuiltIn offering Audit | The controls actually measured against your resources. | The real worklist. Fix, exempt, or record as accepted. |
Step 3. Assign, and wait properly
az policy assignment create \
--name "$ASSIGN" \
--display-name "Northfork regulatory assessment" \
--scope "$SCOPE" \
--policy-set-definition "$STD" \
--description "Assessment against a published standard. Assessment only, no enforcing effects. Owner: compliance lead."
sleep 300
az policy state trigger-scan --no-wait
An initiative of this size takes a while to evaluate and the first scan on a new scope completes in around thirty minutes. A number read before that is not a result, and reading one early is how a standard gets reported as substantially met on the day it was assigned.
Step 4. Attest a manual control, properly
Find one manual control sitting at Unknown, and move it the way it should be moved. The command accepts everything an auditor would want and defaults to none of it, which is precisely why most attestations carry none of it.
ASSIGN_ID=$(az policy assignment show --name "$ASSIGN" --scope "$SCOPE" --query id -o tsv)
az policy state list --scope "$SCOPE" \
--filter "policyAssignmentName eq '$ASSIGN' and complianceState eq 'Unknown'" \
--query "[0].policyDefinitionReferenceId" -o tsv
REF=<the reference id from the previous command>
az policy attestation create \
--name "NFK-Att-${REF}" \
--policy-assignment "$ASSIGN_ID" \
--definition-reference-id "$REF" \
--compliance-state Compliant \
--assessment-date "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--expires-on "$(date -u -d '+1 year' +%Y-%m-%dT%H:%M:%SZ)" \
--owner "[email protected]" \
--comments "Quarterly access review process evidenced below. Reviewed by the compliance lead." \
--evidence source-uri="https://northfork.example/grc/access-review-2026Q3.pdf" description="Q3 2026 access review sign-off"
| Setting | Value here | Why |
|---|---|---|
--definition-reference-id | One control | Omit it and the attestation sets the state for the entire initiative. That is almost never what anyone means and it is a single missing flag away. |
--expires-on | One year | An attestation without an expiry is a claim that never gets revisited. Match the expiry to the cadence of the process being attested, not to the audit calendar. |
--evidence | A real link plus a description | Repeatable. This is the difference between an attestation and an assertion, and it is optional, which is why so few have it. |
--owner | A named person | Somebody is responsible for this claim being true. Record who. |
--assessment-date | When the evidence was assessed | Distinct from when the attestation was created. An auditor will care about the gap. |
Confirm it took, rather than assuming, since attestation state moves on the compliance cycle rather than instantly.
az policy attestation list --filter "PolicyAssignmentId eq '$ASSIGN_ID'" \
--query "[].{name:name, state:complianceState, expires:expiresOn, owner:owner}" --output table
Step 5. The end-to-end test
Prove the attestation actually moved the control, which is the only assertion in this build that tests the mechanism rather than the reporting.
az policy state trigger-scan --no-wait
# allow the cycle to complete, then:
az policy state list --scope "$SCOPE" \
--filter "policyAssignmentName eq '$ASSIGN' and policyDefinitionReferenceId eq '$REF'" \
--query "[0].complianceState" -o tsv
Expected output is Compliant where it previously read Unknown. If it still reads Unknown, the usual cause is a missing or mistyped definition reference identifier, which fails silently because the attestation is a valid object regardless of whether anything matches it.
Step 6. Write the coverage statement
This is the deliverable and it is prose rather than a command. Any compliance figure leaving your team travels with it. Assemble it from the numbers you have already gathered.
| Line | Source |
|---|---|
| Standard, initiative version, and date assessed | Step 1 |
| Controls in the standard represented by the initiative, and the definition count behind them | Step 1, the group and member counts |
| How many controls are Microsoft-owned and therefore not influenced by your work | Step 2, the Static count |
| How many are manual, how many are attested, and when those attestations expire | Steps 2 and 4 |
| How many are exempt, in which category, with expiry dates | The exemption ledger |
| The sentence Microsoft prints: compliance in Azure Policy is a partial view of overall compliance status, because controls map imperfectly and some are not addressed by any definition | The standard’s mapping page |
Six lines. It takes twenty minutes and it converts a percentage that overstates your position into a defensible statement about what has been measured, by whom, and what remains outside the instrument. I have never seen an auditor react badly to it, and I have seen several react badly to a bare number.
Step 7. Roll back
az policy attestation delete --name "NFK-Att-${REF}"
az policy assignment delete --name "$ASSIGN" --scope "$SCOPE"
Delete the attestation first. An attestation referencing an assignment that no longer exists is an orphan that will outlive its purpose and confuse the next person to audit your attestations, which somebody eventually will.
From here
Everything to this point has been done by hand, one object at a time, which is how you learn a product rather than how you run one. The next article moves the whole estate into a repository.
Azure Policy
‹ Previous: [AP 11] Regulatory Initiatives: What Azure Can Prove and What It Cannot
Next: [AP 12] Policy as Code: The Operating Model ›




