Skip to content

Microsoft Defender for Cloud -- How-To Guide

Audience: Team lead, team members, and future reference Subscription: HISD (9639039c-86c6-4859-b014-d28688863cd5) Jira Project: DEVOPS Last updated: 2026-02-18


Table of Contents

  1. Overview
  2. Understanding Severity
  3. Jira Structure
  4. Monthly Intake Workflow
  5. Triage Checklist
  6. Wave Prioritization
  7. Closing a Finding
  8. Common JQL Queries
  9. az CLI Quick Reference

1. Overview

What Is Defender for Cloud?

Microsoft Defender for Cloud is Azure’s built-in cloud security posture management (CSPM) and workload protection platform. It continuously evaluates your Azure resources against security best practices and regulatory standards, then surfaces recommendations — actionable findings that tell you what to fix and why.

For eshars, Defender monitors everything in the HISD subscription: App Services, Azure SQL databases, Key Vaults, storage accounts, virtual machines, networking, and identity configuration.

Why We Track It

  • Security score: Defender assigns a score (0-100%) to the subscription. Each unresolved recommendation lowers the score. Resolving findings raises it. The score is visible to auditors, leadership, and Microsoft.
  • Compliance: As a SHARS/Medicaid platform handling student health data, we operate under HIPAA and state compliance requirements. Defender findings often map directly to compliance gaps.
  • Attack surface: Unresolved findings represent real risk. Defender’s attack path analysis connects findings to show how an attacker could chain vulnerabilities (e.g., internet-exposed VM + unpatched CVE = remote code execution path).
  • Auditability: Tracking findings in Jira creates a documented trail of what was found, when, what was done about it, and who approved any risk acceptance decisions.

Security Score Impact

Each recommendation carries a max score increase — the number of points the security score gains when the finding is fully resolved across all affected resources. Partially resolving a finding (e.g., fixing 3 of 5 affected resources) gives proportional credit.

Key points:

  • Resolving a single high-impact recommendation can raise the score by several percentage points.
  • Findings affecting many resources (e.g., “Storage accounts should restrict network access” across 12 accounts) have outsized score impact because every affected resource contributes to the total.
  • The score only updates when Defender’s next assessment cycle runs (typically within 24 hours of a change).

2. Understanding Severity

Defender for Cloud presents findings through two different lenses. We track both.

Recommendation Severity (Static)

Every Defender recommendation has a fixed severity assigned by Microsoft:

SeverityMeaning
HighSignificant security weakness. Exploitation could lead to data breach, privilege escalation, or compliance violation.
MediumModerate security gap. Increases attack surface but typically requires additional conditions to exploit.
LowBest-practice hardening. Defense-in-depth measure or configuration improvement.

This severity is static — it is a property of the recommendation itself and does not change based on your environment. “Storage accounts should prevent shared key access” is always High, regardless of which storage account it applies to.

This is how our Jira Epics are organized. Each Epic corresponds to a recommendation severity level.

Risk-Based View (Dynamic)

Defender also provides a risk-based prioritization that overlays context on top of recommendation severity:

Risk LevelMeaning
CriticalRecommendation + exposure factors (internet-facing, attack path exists, sensitive data involved)
HighRecommendation + moderate exposure factors
MediumStandard risk, no aggravating factors
LowMinimal exposure, low exploitability

This view is dynamic — it changes as your environment changes. A High-severity recommendation on an internet-exposed VM with known CVEs might be elevated to Critical risk. The same recommendation on an isolated dev VM might stay at High or Medium risk.

Risk-based factors include:

  • Attack paths: Defender maps chains of findings that an attacker could traverse (e.g., internet exposure + vulnerability + privileged identity = attack path to data).
  • Internet exposure: Is the affected resource reachable from the public internet?
  • Sensitive data: Does the resource hold or process sensitive information?
  • Lateral movement potential: Could compromising this resource lead to others?

How We Track Both

DimensionWhere We Track ItChanges Over Time?
Recommendation severity (H/M/L)Jira Epics (DEVOPS-82 through DEVOPS-86)No — static per recommendation
Risk-based level (Critical/High/Medium/Low)Jira labels (risk-critical, risk-high)Yes — re-evaluate periodically
Attack path involvementJira label (attack-path)Yes — Defender updates this dynamically
EnvironmentJira labels (env-prod, env-nonprod)No — resource is either prod or not

This dual-tracking lets us answer two different questions:

  • “What kind of problem is this?” — Look at the Epic (recommendation severity).
  • “How urgent is it right now?” — Look at the risk/environment labels.

3. Jira Structure

Severity Epics

Epic KeyNameRecommendation SeverityPrioritySLAUse For
DEVOPS-82Defender - HighHighHighest1 weekHigh-severity Defender recommendations
DEVOPS-83Defender - MediumMediumMedium2 weeksMedium-severity Defender recommendations
DEVOPS-84Defender - LowLowLow1-2 sprintsLow-severity / best-practice recommendations
DEVOPS-85Defender - Non-IssueN/ALowestDocument and closeFalse positives, not applicable, compensating control exists
DEVOPS-86Defender - Won’t DoN/ALowAwaiting approvalAccepted risk — team lead must approve before closing

Label Taxonomy

Every Defender finding in Jira must have the Defender label. Additional labels provide filtering dimensions:

LabelCategoryPurpose
DefenderRequiredIdentifies all Defender-sourced findings
defender-highSeverityMatches High recommendation severity (Epic DEVOPS-82)
defender-mediumSeverityMatches Medium recommendation severity (Epic DEVOPS-83)
defender-lowSeverityMatches Low recommendation severity (Epic DEVOPS-84)
env-prodEnvironmentFinding affects at least one production resource
env-nonprodEnvironmentFinding affects at least one non-production resource
risk-criticalRiskRisk-based view rates this as Critical
risk-highRiskRisk-based view rates this as High
attack-pathRiskDefender identified this in an attack path analysis

Notes:

  • A finding can have both env-prod and env-nonprod if the same recommendation applies to resources in both environments.
  • Risk labels (risk-critical, risk-high) are added based on the dynamic risk-based view and should be re-evaluated periodically.
  • The attack-path label is for findings that Defender has connected into an exploitable chain. These deserve elevated attention regardless of recommendation severity.

Priority Mapping

EpicJira PriorityRationale
DEVOPS-82 (High)HighestDirect security weakness with potential for breach
DEVOPS-83 (Medium)MediumSecurity gap requiring remediation within sprint cycle
DEVOPS-84 (Low)LowBest-practice improvement, backlog-appropriate
DEVOPS-85 (Non-Issue)LowestDocument rationale, then close
DEVOPS-86 (Won’t Do)LowAwaiting team lead approval for risk acceptance

Ticket Creation Checklist

When creating a new Jira Task for a Defender finding:

  1. Issue Type: Task
  2. Epic Link: Set to the matching severity Epic
  3. Priority: Set to match the Epic (see table above)
  4. Labels: Defender + severity label + environment label(s) + risk label (if applicable)
  5. Summary: Use the Defender recommendation title verbatim (e.g., “Storage accounts should restrict network access using VNet rules”)
  6. Description: Include:
    • Defender recommendation name
    • Affected resource(s) — name, resource group, environment
    • Defender’s remediation guidance
    • Link to Defender recommendation in Azure Portal (if available)
    • Any notes on impact to eshars applications

JQL Quick Examples

# All Defender findings
project = DEVOPS AND labels = Defender ORDER BY priority DESC
# Open findings only
project = DEVOPS AND labels = Defender AND status != Done ORDER BY priority DESC
# High severity, production
project = DEVOPS AND labels = Defender AND labels = defender-high AND labels = env-prod AND status != Done
# Everything with an attack path
project = DEVOPS AND labels = Defender AND labels = attack-path AND status != Done

4. Monthly Intake Workflow

Perform this workflow at least monthly to pull new Defender findings and reconcile with Jira.

Step 1: Set the subscription context

Terminal window
az account set --subscription 9639039c-86c6-4859-b014-d28688863cd5

Verify:

Terminal window
az account show --query "{name:name, id:id}" -o table

Step 2: Pull assessment metadata (recommendation definitions)

Terminal window
az security assessment-metadata list -o json > metadata.json

This file contains the full catalog of Defender recommendations, including their displayName, severity, description, and remediationDescription. It provides the static severity (High/Medium/Low) for each recommendation.

Step 3: Pull unhealthy assessments (active findings)

Terminal window
az security assessment list \
--query "[?status.code=='Unhealthy']" \
-o json > assessments.json

This file contains every recommendation that currently has at least one non-compliant resource. Each entry includes:

  • displayName: The recommendation title
  • status.code: Unhealthy (filtered above)
  • resourceDetails: Which specific resource is affected
  • name: The assessment ID (used to join with metadata)

Step 4: Parse and join the data

Terminal window
python security/parse_defender.py metadata.json assessments.json > findings.json

The script (security/parse_defender.py) joins the two files on assessment ID to produce a unified output with:

  • Each unique recommendation title
  • Its Defender severity
  • Count of affected resources
  • List of affected resource names, resource groups, and resource IDs

Output goes to stdout as JSON. Progress messages go to stderr.

Step 5: Review the parsed output

Open findings.json and review:

Terminal window
# Quick summary -- how many recommendations by severity?
python -c "
import json
with open('findings.json') as f:
data = json.load(f)
print(f'Total unhealthy: {data[\"total_unhealthy\"]}')
print(f'Unique recommendations: {data[\"unique_recommendations\"]}')
for sev, count in data['by_severity'].items():
print(f' {sev}: {count}')
"

Step 6: Cross-reference against register and Jira

For each recommendation in findings.json:

  1. Check the register (security/defender-register.md) — is this recommendation already tracked?
  2. Check Jira — run the JQL: project = DEVOPS AND labels = Defender AND summary ~ "recommendation title"
  3. Classify each result:
SituationAction
New finding, not in register or JiraCreate new Jira ticket + add to register
In register and Jira, still openNo action needed (already tracked)
In register and Jira, marked Done, but still UnhealthyReopen the Jira ticket — the fix was incomplete or has regressed
In register but not in JiraCreate the Jira ticket (register was not kept in sync)
In Jira but not in findingsGood news — finding may have been resolved. Verify in Azure Portal before closing.

Step 7: Create new tickets and update the register

For each new finding:

  1. Create a Jira Task following the Ticket Creation Checklist
  2. Classify the affected resources as prod or non-prod using naming conventions:
    • Prod: names containing prod, prodwest, sharsprod
    • Non-prod: names containing nonprod, dev, uat, qa, training, regression
    • Ambiguous: flag for team lead confirmation
  3. Add the appropriate environment labels (env-prod, env-nonprod, or both)
  4. Add a row to the register under the correct severity section
  5. Update the register summary counts

Step 8: Archive the raw data

Move the raw files to a dated archive:

Terminal window
mv metadata.json security/metadata-$(date +%Y-%m-%d).json
mv assessments.json security/assessments-$(date +%Y-%m-%d).json
mv findings.json security/findings-$(date +%Y-%m-%d).json

Consider adding raw JSON files to .gitignore if they are too large to commit. The register and Jira are the sources of truth.


5. Triage Checklist

Use this checklist when evaluating a new Defender finding. Work through each question and document the answers in the Jira ticket description or a comment.

Assessment Questions

#QuestionWhy It Matters
1What is the Defender recommendation severity? (High/Medium/Low)Determines which Epic the ticket belongs to
2Which resources are affected? List names and resource groups.Scoping: How many resources? Which environments?
3Are any affected resources in production?Production findings get higher wave priority
4Is there an attack path involving this finding? Check Defender’s attack path analysis.Attack paths indicate chained exploitability
5What is the risk-based severity? (Critical/High/Medium/Low in Defender’s risk view)May elevate priority beyond the static recommendation severity
6Is the resource internet-exposed?Internet exposure significantly increases risk
7Does this affect data at rest or in transit? (PII, PHI, credentials)Compliance implications for HIPAA/Medicaid
8Is there a compensating control already in place?If yes, document it — may qualify as Non-Issue
9What does Defender recommend for remediation?Guides the implementation work
10What is the blast radius? Could the fix break application functionality?Determines QA requirements and rollout caution

Decision Outcomes

After working through the checklist, assign one of these dispositions:

DispositionAction
RemediateAssign to severity Epic, add to sprint backlog per wave priority
Non-IssueMove to DEVOPS-85, document justification (compensating control, false positive, not applicable)
Won’t DoMove to DEVOPS-86, document justification + residual risk. Requires team lead approval (see below)
Needs InvestigationKeep in severity Epic, add comment noting what needs to be determined before remediation can begin

”Won’t Do” Approval Process

No finding may be closed as “Won’t Do” without team lead sign-off:

  1. Engineer adds a comment to the ticket with:
    • Justification: Why the recommendation will not be implemented
    • Compensating controls: What mitigations exist instead
    • Residual risk: What risk remains and why it is acceptable
  2. Engineer moves the ticket to Epic DEVOPS-86 (Won’t Do — Pending Approval)
  3. Team lead reviews and adds a comment: [APPROVED - Won't Do]
  4. Only after approval can the ticket be transitioned to Done
  5. If the team lead disagrees, they reassign back with revised instructions

6. Wave Prioritization

Remediation work is organized into waves that balance severity with environment impact. Production findings of the same severity always come before non-production.

Wave Definitions

WaveScopeTarget TimelineJQL Filter
Wave 1High + prodCurrent sprintproject = DEVOPS AND labels = Defender AND labels = defender-high AND labels = env-prod AND status != Done
Wave 2High + non-prod, Medium + prodNext sprintproject = DEVOPS AND labels = Defender AND ((labels = defender-high AND labels = env-nonprod) OR (labels = defender-medium AND labels = env-prod)) AND status != Done
Wave 3Medium + non-prodFollowing sprintproject = DEVOPS AND labels = Defender AND labels = defender-medium AND labels = env-nonprod AND status != Done
Wave 4Low + prodBacklogproject = DEVOPS AND labels = Defender AND labels = defender-low AND labels = env-prod AND status != Done
Wave 5Low + non-prodBacklogproject = DEVOPS AND labels = Defender AND labels = defender-low AND labels = env-nonprod AND status != Done

Wave Escalation

Findings with these characteristics should be escalated to an earlier wave regardless of recommendation severity:

  • attack-path label present — Defender has identified an exploitable chain
  • risk-critical label present — Risk-based view rates the finding as Critical
  • Finding involves credentials, secrets, or identity (PII/PHI exposure potential)
  • Finding has been open for longer than the SLA for its severity tier

Sprint Planning Integration

During sprint planning:

  1. Run the Wave 1 JQL to identify must-do items for the current sprint
  2. Run the Wave 2 JQL to identify candidates for the sprint if capacity allows
  3. Check for any escalated items from lower waves
  4. Review attack-path items separately: project = DEVOPS AND labels = attack-path AND status != Done

7. Closing a Finding

Closing a Defender finding requires verification across multiple systems. Do not skip steps.

Step 1: Verify in Defender

Confirm the finding is resolved in Azure:

Terminal window
# Check status of a specific recommendation by title
az security assessment list \
--query "[?displayName=='<RECOMMENDATION TITLE>' && status.code=='Unhealthy']" \
-o table
  • If the query returns no results, the finding is resolved (Healthy).
  • If it returns results, the finding is still active. Investigate further before closing.

Note: Defender assessments can take up to 24 hours to reflect changes. If you just applied a fix, wait and re-check.

You can also verify in the Azure Portal:

  1. Navigate to Defender for Cloud > Recommendations
  2. Search for the recommendation by name
  3. Confirm all previously affected resources now show as Healthy

Step 2: Update the Jira ticket

  1. Add a closing comment with:
    • Confirmation that the finding shows as Healthy in Defender
    • Summary of what was done to resolve it
    • Date verified
    • Any follow-up actions (e.g., monitoring for regression)
  2. Transition the ticket to Done

Step 3: Update the register

In security/defender-register.md:

  1. Move the finding row from the Open section to the Completed section under the appropriate severity
  2. Update the Status column to Done
  3. Update the Summary counts table

Step 4: Write release notes

For each batch of resolved findings:

  1. Copy the template from security/release-notes/TEMPLATE.md
  2. Create a new file: security/release-notes/YYYY-MM-DD-batch-N.md
  3. Fill in each section per the template:
    • What happened: The Defender finding and why it matters
    • Solution: Step-by-step remediation actions taken
    • QA considerations: Testing done to verify no application impact
    • Deployment considerations: Any restarts, downtime, or deployment steps
  4. Commit to the repository

Step 5: Handle “Won’t Do” closures

If closing as Won’t Do (rather than resolved):

  1. Ensure team lead has added [APPROVED - Won't Do] comment on the Jira ticket
  2. Move the ticket to Epic DEVOPS-86
  3. In the register, add the finding to a “Won’t Do” section with the justification
  4. No release notes needed — the approval comment and register entry serve as the audit trail
  5. The finding will remain Unhealthy in Defender. This is expected and accepted.

8. Common JQL Queries

Copy-paste these into Jira’s search bar or save as filters.

By Status

-- All Defender findings (any status)
project = DEVOPS AND labels = Defender ORDER BY priority DESC
-- Open findings only
project = DEVOPS AND labels = Defender AND status != Done ORDER BY priority DESC
-- Completed findings
project = DEVOPS AND labels = Defender AND status = Done ORDER BY resolved DESC

By Severity Epic

-- High severity
project = DEVOPS AND "Epic Link" = DEVOPS-82 AND status != Done ORDER BY priority DESC
-- Medium severity
project = DEVOPS AND "Epic Link" = DEVOPS-83 AND status != Done ORDER BY priority DESC
-- Low severity
project = DEVOPS AND "Epic Link" = DEVOPS-84 AND status != Done ORDER BY priority DESC
-- Non-Issue
project = DEVOPS AND "Epic Link" = DEVOPS-85
-- Won't Do (pending approval)
project = DEVOPS AND "Epic Link" = DEVOPS-86 AND status != Done

By Environment

-- All open production findings
project = DEVOPS AND labels = Defender AND labels = env-prod AND status != Done ORDER BY priority DESC
-- All open non-production findings
project = DEVOPS AND labels = Defender AND labels = env-nonprod AND status != Done ORDER BY priority DESC
-- Findings affecting both prod and non-prod
project = DEVOPS AND labels = Defender AND labels = env-prod AND labels = env-nonprod AND status != Done

By Risk / Attack Path

-- Critical risk findings
project = DEVOPS AND labels = Defender AND labels = risk-critical AND status != Done
-- High risk findings
project = DEVOPS AND labels = Defender AND labels = risk-high AND status != Done
-- Attack path findings
project = DEVOPS AND labels = Defender AND labels = attack-path AND status != Done
-- Critical risk OR attack path (most urgent)
project = DEVOPS AND labels = Defender AND (labels = risk-critical OR labels = attack-path) AND status != Done

Wave Queries

-- Wave 1: High + prod
project = DEVOPS AND labels = Defender AND labels = defender-high AND labels = env-prod AND status != Done
-- Wave 2: High + non-prod OR Medium + prod
project = DEVOPS AND labels = Defender AND ((labels = defender-high AND labels = env-nonprod) OR (labels = defender-medium AND labels = env-prod)) AND status != Done
-- Wave 3: Medium + non-prod
project = DEVOPS AND labels = Defender AND labels = defender-medium AND labels = env-nonprod AND status != Done
-- Wave 4: Low + prod
project = DEVOPS AND labels = Defender AND labels = defender-low AND labels = env-prod AND status != Done
-- Wave 5: Low + non-prod
project = DEVOPS AND labels = Defender AND labels = defender-low AND labels = env-nonprod AND status != Done

Operational

-- Overdue items (created more than 30 days ago, still open)
project = DEVOPS AND labels = Defender AND status != Done AND created <= -30d ORDER BY created ASC
-- Recently resolved (last 14 days)
project = DEVOPS AND labels = Defender AND status = Done AND resolved >= -14d ORDER BY resolved DESC
-- Needs triage (To Do, no severity label)
project = DEVOPS AND labels = Defender AND status = "To Do" AND labels NOT IN (defender-high, defender-medium, defender-low)

9. az CLI Quick Reference

All commands assume the subscription is already set:

Terminal window
az account set --subscription 9639039c-86c6-4859-b014-d28688863cd5

Security Assessments

Terminal window
# List all unhealthy assessments (summary table)
az security assessment list \
--query "[?status.code=='Unhealthy'].{name:displayName, severity:metadata.severity, status:status.code}" \
-o table
# Export all unhealthy assessments to JSON
az security assessment list \
--query "[?status.code=='Unhealthy']" \
-o json > assessments.json
# Check a specific recommendation by name
az security assessment list \
--query "[?displayName=='Storage accounts should restrict network access using VNet rules']" \
-o table
# Count unhealthy findings
az security assessment list \
--query "[?status.code=='Unhealthy'] | length(@)"

Assessment Metadata

Terminal window
# Export full recommendation catalog (severity, description, remediation)
az security assessment-metadata list -o json > metadata.json
# Look up a specific recommendation's details
az security assessment-metadata list \
--query "[?displayName=='Storage accounts should restrict network access using VNet rules'].{name:displayName, severity:severity, description:description}" \
-o table

Security Score

Terminal window
# Get current secure score
az security secure-score list -o table
# Get score controls (which categories contribute to score)
az security secure-score-controls list -o table
# Get score controls with detail on recommendations
az security secure-score-control-definitions list -o table

Defender Plans

Terminal window
# List active Defender plans (which resource types have protection enabled)
az security pricing list -o table
# Check a specific plan
az security pricing show --name VirtualMachines -o table

Alerts

Terminal window
# List active security alerts
az security alert list \
--query "[?status=='Active'].{name:alertDisplayName, severity:severity, time:timeGeneratedUtc}" \
-o table
# Dismiss an alert (after investigation)
az security alert update --name <ALERT_NAME> --status Dismissed --location <LOCATION>

Useful Filters

Terminal window
# Unhealthy findings for a specific resource group
az security assessment list \
--query "[?status.code=='Unhealthy' && resourceGroup=='eshars-prod-rg']" \
-o table
# Unhealthy findings by severity
az security assessment list \
--query "[?status.code=='Unhealthy' && metadata.severity=='High']" \
-o table
# Resources affected by a specific recommendation
az security assessment list \
--query "[?displayName=='<RECOMMENDATION>' && status.code=='Unhealthy'].resourceDetails.Id" \
-o tsv

Parse Script

Terminal window
# Full intake pipeline (pull, parse, output)
az security assessment-metadata list -o json > metadata.json
az security assessment list --query "[?status.code=='Unhealthy']" -o json > assessments.json
python security/parse_defender.py metadata.json assessments.json > findings.json

Appendix: Key File Locations

FilePurpose
security/defender-process.mdTriage workflow, categorization criteria, approval process
security/defender-register.mdMaster register mapping Defender findings to Jira tickets
security/defender-guide.mdThis file — comprehensive how-to guide
security/parse_defender.pyScript to join metadata + assessments into structured findings
security/release-notes/TEMPLATE.mdTemplate for documenting resolved findings
security/release-notes/Batch release notes for resolved findings
security/findings-YYYY-MM-DD.jsonArchived raw findings output from parse script

Appendix: Cadence Summary

FrequencyActivityOwner
ImmediatelyHigh-severity + prod findings go into current sprintTeam lead
MonthlyRun intake workflow (Steps 1-8 above), create new tickets, update registerAssigned engineer
Sprint planningInclude Defender items per wave priorityTeam
Sprint closeWrite release notes for resolved findingsAssigned engineer
QuarterlyFull review of all open findings with team lead; re-evaluate risk labelsTeam lead + team
QuarterlyReview “Won’t Do” items — are compensating controls still valid?Team lead