Type to search 66 articles.

    Practical engineering guidance

    Azure Policy as a guardrail: audit, deny and deployIfNotExists

    Policy is the only control that stops a permitted action producing an unacceptable resource. Deploying it in the wrong order is how you break a platform team's deployments on a Monday.

    Series: Azure architecture

    • Azure
    • Governance
    • Azure Policy

    Role-based access control decides who may act. Azure Policy decides what the result is allowed to look like. They solve different problems, and the distinction matters because almost every governance requirement people bring to RBAC is actually a policy requirement.

    “Contributors must not create storage accounts with public access” is not expressible as a role. It is a policy.

    Requirements: what to know before assigning anything

    Policy evaluates at creation, at update, and periodically. A new deny assignment blocks non-conforming new deployments immediately. It does not delete existing non-conforming resources — they are simply reported as non-compliant. This asymmetry is what makes a staged rollout possible.

    Scope is the management group, subscription or resource group, inheriting downward in the same way as RBAC. Exemptions carve out specific scopes or resources, and unlike RBAC, policy does have a subtraction mechanism.

    Remediating effects need an identity. deployIfNotExists and modify act on your behalf, so the assignment needs a managed identity with sufficient permissions. That identity is a privileged object and deserves the scrutiny in Azure RBAC scope decisions.

    Design: the effects, and what each is for

    The effects are audit, auditIfNotExists, deny, denyAction, deployIfNotExists, modify, append and disabled. In practice you will use five of them.

    audit reports non-compliance and changes nothing. This is where every policy should start. It costs nothing, breaks nothing, and produces the evidence you need to decide whether denying is safe.

    auditIfNotExists reports when a related resource is missing — diagnostic settings not configured, an extension not installed. Useful for the “this exists but is not fully configured” class of finding, which plain audit cannot express.

    deny blocks the request. This is the guardrail proper, and the effect that generates incidents if deployed carelessly.

    denyAction blocks a specific action rather than a resource shape — the obvious use being to block deletion of resources that must not be deleted. Different from deny, and worth knowing because people reach for resource locks when this is a better fit at scale.

    deployIfNotExists deploys a remediating template when the condition is met. This is how you get diagnostic settings onto every resource without asking teams to remember. Powerful, and it changes things on your behalf, so it earns the same caution as deny.

    modify alters the request as it arrives — adding or replacing tags, for instance. It runs before the resource is created, which makes it gentler than remediating afterwards.

    append adds fields to a request. Largely superseded by modify for most uses.

    disabled turns a definition off without removing the assignment, which is your fastest rollback.

    Design: what belongs in a baseline

    A useful starting baseline is small and mostly not deny:

    • Allowed regions. A deny on regions outside your data residency requirements. This one is safe to deny early because it is unambiguous and the requirement usually comes from outside IT.
    • Required tags. modify to add a default, rather than deny for absence. Denying a deployment because of a missing cost centre tag makes governance the enemy; adding the tag silently does not.
    • Diagnostic settings. deployIfNotExists to send platform logs where they belong. This is the single highest-value remediating policy, because the alternative is discovering during an incident that nothing was logged.
    • Public network access. Start with audit across storage, databases and key vaults. The results will tell you whether a deny is survivable.
    • Storage and transport security. Secure transfer, minimum TLS version. Usually safe to deny once audited, because non-compliance is rare and clearly wrong.

    Use initiatives — policy sets — to group related definitions so that assignment and exemption happen once rather than per definition. The built-in regulatory compliance initiatives are worth assigning in audit mode purely for the visibility, even if you have no compliance obligation.

    Deployment: the sequence that avoids an outage

    This is the part that matters, and it mirrors the Conditional Access approach in Conditional Access baselines for exactly the same reason: you are deploying an enforcement control into an environment you do not fully know.

    1. Assign with audit, at the scope you eventually intend to enforce at.
    2. Wait for evaluation and read the compliance results. Every non-compliant resource is either something to fix, something to exempt, or evidence that the policy is wrong.
    3. Fix or exempt the existing non-compliance. Exemptions should carry an expiry date and a reason; an exemption without either becomes permanent by neglect.
    4. Change the effect to deny, starting at a narrow scope — one non-production subscription — rather than at the management group root.
    5. Widen the scope in stages, watching for deployment failures at each step.
    6. Only then consider remediating effects for existing resources, and run remediation tasks deliberately rather than leaving them to run automatically the first time.

    Microsoft documents a safe deployment practice for policy assignments, and it is worth following rather than improvising. The failure mode this prevents is specific and common: a deny assigned at the root management group on a Friday, discovered on Monday when every pipeline in the organisation fails.

    Use the enforcement mode. Assignments can be created with enforcement disabled, which evaluates and reports without applying deny or remediation. It is the policy equivalent of report-only and it is underused.

    Validation

    Compliance percentage is the headline number and the least useful one. What to check instead:

    • Deploy a deliberately non-conforming resource into a test scope and confirm it is actually blocked. A deny policy that has never blocked anything may be scoped to nothing.
    • Deploy a conforming resource and confirm it succeeds. Over-broad policy conditions block legitimate deployments, and this is how you find out before a team does.
    • Confirm remediation ran, and check what it created. A deployIfNotExists that silently failed because its managed identity lacked permission is a common and quiet failure.
    • Check the exemption list and its expiry dates.
    • Confirm the policy applies to the deployment methods you actually use. Policy evaluates Resource Manager requests, so ARM templates, Bicep, Terraform via the Azure provider and portal actions are all covered. Changes made through a service’s own data plane are not.

    Operations

    Exemptions need a lifecycle. Give each one an expiry and an owner. Review them on a schedule and require a fresh justification rather than an extension by default.

    Policy definitions are code. Keep custom definitions and assignments in version control and deploy them through a pipeline. Clicking policy assignments into a portal produces an estate nobody can reproduce or review.

    Watch for policy as an accidental outage source. When a deployment fails mysteriously, policy should be an early suspect. The activity log records the denying policy assignment by name, which makes this fast once people know to look.

    Re-evaluate the baseline when Azure changes. New resource types appear that your policies do not cover, and built-in definitions are updated. A baseline assigned two years ago and never revisited is reporting compliance against a narrower set of resources than you think.

    Rollback

    Three levels, fastest first: set the effect parameter to disabled, set the assignment’s enforcement mode to disabled, or delete the assignment. All take effect quickly for new deployments.

    What does not roll back is anything a remediating effect already changed. A modify that rewrote tags or a deployIfNotExists that created diagnostic settings has altered resources, and removing the policy does not revert them. That asymmetry is why remediating effects come last in the sequence above, and why remediation tasks should be run deliberately on a known scope rather than left to sweep an estate unattended.

    Verification and limits

    The effect names and their behaviours, evaluation at create and update plus periodic evaluation, the non-deletion of existing non-compliant resources, managed identity requirements for remediating effects, exemptions and enforcement mode were checked against current Microsoft documentation on 20 September 2026. The suggested baseline and the rollout sequence are my own practice.

    No policy was assigned to an Azure environment for this article. A deny assignment blocks deployments as soon as it is in place, and remediating effects change resources: assign in audit mode first, use enforcement mode while testing, start at a narrow non-production scope, and confirm the activity log shows what you expect before widening.

    References