Skip to content

Manage Subscription Defender Settings

The engineer's guide to Microsoft Defender for Cloud on a vended subscription. Use this runbook to:

  • Turn a paid Defender plan on or off for a subscription.
  • Change where a subscription's security alerts go.
  • Triage a Defender alert or a new recommendation.
  • Stop a recommendation from counting against one resource.

For which plans to choose and why, see Defender Plans. To switch an MCSB control off for every subscription, see Manage MCSB Control Overrides.

Plans cost money per resource

Foundational CSPM is free. Every other plan is billed per protected resource (per VM-hour, per storage account, per vault). Before turning one on, check how many of that resource the subscription has, and say the expected monthly cost in the MR. Raise the budget in the same MR if needed.

What every subscription gets

Setting Set by Value
Foundational CSPM Vending (foundational_cspm, default on) On: Secure Score, recommendations, asset inventory
Security contact Vending (security_contact_email) neil@grinntec.net, High severity and above, plus subscription Owners
Paid plans Vending (defender_plans, default {}) None managed. Every plan stays at Azure's default, Free.
MCSB control overrides azure-mcsb-overrides, nightly The by-design not-applicable controls, switched off

EPAC's psa-sec-defender-001 at mg-intermediate-root audits that Foundational CSPM is on and a security contact is set. It runs as DoNotEnforce, so it reports drift but doesn't fix it. Vending is what actually sets them.

The projects

Project Lane Holds
subscription-vending-workload B defender_plans and security_contact_email in each workload subscription's <name>.tf. CI applies on merge.
subscription-vending-platform A The same inputs for platform subscriptions. You apply with ./apply.ps1.
azure-governance-epac B The audit initiative, and policy exemptions in Definitions/policyExemptions/grinntec/exemptions.jsonc.

Turn a plan on

📁 subscription-vending-*: 📃 <name>.tf

Add defender_plans with the plan's resource type as the key:

  defender_plans = {
    Arm             = "on"   # Defender for Resource Manager
    KeyVaults       = "on"
    StorageAccounts = "on"   # sub-plan DefenderForStorageV2
  }
Key Plan Sub-plan when on
Arm Resource Manager —
KeyVaults Key Vault —
StorageAccounts Storage DefenderForStorageV2
VirtualMachines Servers P1
AppServices App Service —
SqlServers, SqlServerVirtualMachines, OpenSourceRelationalDatabases, CosmosDbs Databases —
Containers, ContainerRegistry, KubernetesService Containers —
Api, AI, Dns, CloudPosture APIs, AI, DNS, Defender CSPM (paid) —

Open an MR. The plan should show one azapi_update_resource.defender_plan add per key, setting pricingTier = "Standard", and nothing else. Merge (and for a platform subscription, apply).

Servers is Plan 1 only

VirtualMachines = "on" always selects P1. Plan 2 (file integrity monitoring, the vulnerability assessment add-ons) needs a change to local.defender_subplans in the solution and a new release.

Expect the Secure Score to drop

A new plan brings recommendations that didn't count before. Mention it in the MR so the weekly governance report's drop isn't mistaken for a regression.


Turn a plan off

Set it to "off" first. Don't just delete the line.

  defender_plans = {
    Arm             = "on"
    KeyVaults       = "off"   # ← was "on"
  }

Plans are Azure singletons: Terraform updates them but can't delete them. Removing the key only stops Terraform managing the plan, and it stays on, and billed, at whatever it was last set to. Apply with "off", and only then remove the line if you want to.

"off" also holds the plan off: if someone turns it on in the portal, the next apply turns it off again.


Change the security contact

  security_contact_email = "payments-security@grinntec.net"

Use a monitored team mailbox. The plan should show one in-place update to azapi_resource.security_contact. The setting takes one address. Owners of the subscription are also notified, but vended subscriptions have no standing Owners, so in practice this address is the only one that gets the alerts.


Triage an alert or recommendation

Security alert (from a paid plan, by email to the security contact):

  1. Open it from the email, or Defender for Cloud → Security alerts, filtered to the subscription.
  2. Read Affected resource, Activity start time and the Take action tab, which lists Defender's own mitigation steps.
  3. If it looks real, follow the incident response process. Don't delete or rebuild the resource before evidence is kept.
  4. If it's a known, expected activity (a pen test, a scheduled job), dismiss it with a reason, or create a suppression rule scoped to the resource and alert type, with an expiry date.

New recommendation (Secure Score dropped, or the weekly report flagged it):

  1. Defender for Cloud → Recommendations, filtered to the subscription.
  2. Open the recommendation and read Affected resources.
  3. Decide which applies:
Situation Do this
It can be fixed Fix it in the workload's Terraform, not in the portal.
It can't be fixed yet for one resource Exempt that resource, up to 90 days.
It can never apply under our operating model, anywhere Manage MCSB Control Overrides.

Exempt one resource

📁 azure-governance-epac: 📃 Definitions/policyExemptions/grinntec/exemptions.jsonc

Add one entry, scoped as narrowly as possible, with an expiry no more than 90 days out:

{
  "name": "exm-sec-mcsb-st-payments-001",
  "displayName": "MCSB waiver - st-payments-export public network access",
  "description": "Export account needs a public endpoint until the partner's private link is live (GRIN-1234).",
  "exemptionCategory": "Waiver",
  "expiresOn": "2027-01-15",
  "scope": "/subscriptions/<sub-id>/resourceGroups/rg-payments-prod/providers/Microsoft.Storage/storageAccounts/stpaymentsexport",
  "policyAssignmentId": "psa-sec-mcsb-001",
  "policyDefinitionReferenceIds": [ "<policyDefinitionReferenceId>" ],
  "metadata": {
    "requestedBy": "someone@grinntec.net",
    "approvedBy": "neil@grinntec.net",
    "ticket": "GRIN-1234"
  }
}

A CODEOWNER other than you approves the MR. The full rules are in the EPAC repo's Definitions/policyExemptions/README.md.

An EPAC exemption covers the compliance dashboard only

psa-sec-mcsb-001 feeds the regulatory compliance dashboard. Defender's own Secure Score comes from SecurityCenterBuiltIn on the subscription, which EPAC doesn't manage. To take one resource out of the Secure Score as well, use Exempt on the recommendation in Defender for Cloud, with the same expiry and reason as the EPAC entry.


Check it worked

# Plans on the subscription
az security pricing list --subscription <sub-id> \
  --query "value[?pricingTier=='Standard'].{plan:name, subPlan:subPlan}" -o table

# Security contact
az rest --method get \
  --url "https://management.azure.com/subscriptions/<sub-id>/providers/Microsoft.Security/securityContacts?api-version=2020-01-01-preview" \
  --query "value[].properties.emails"

In the portal: Defender for Cloud → Environment settings → shows each plan's status. New plans start producing recommendations within about 24 hours.


When it fails

Symptom Cause Fix
404 Subscription Not Registered on Microsoft.Security/pricings Microsoft.Security registration hadn't finished on a new subscription Re-run the pipeline. Registration is idempotent.
Plan removed from the file but the subscription still shows it on, and billed The key was deleted instead of set to "off" Add it back as "off", apply, then remove it.
A plan someone turned on in the portal keeps turning off The subscription file holds it "off" Intended. Turn it on in code.
psa-sec-defender-001 shows the subscription non-compliant The plan or contact was changed outside Terraform, or foundational_cspm = "off" Re-run the pipeline on main to restore it. Find who changed it in the Activity Log.
Secure Score still counts an EPAC-exempted resource EPAC exemptions don't reach SecurityCenterBuiltIn Exempt it in Defender for Cloud as well (see above).