Defender Secure Score Monitor
Purpose
Track and improve your Microsoft Defender for Cloud Secure Score through focused monitoring, trend analysis, and actionable remediation guidance. Answers "what's our security score and how do we improve it?"
When to use this skill
- User asks "what's our secure score?"
- User asks how to improve security posture
- Weekly security posture check
- Before a security audit or customer questionnaire
- After deploying new resources (to check score impact)
Difference from Compliance/Governance Audit
The Compliance skill does a broad governance audit (policy, RBAC, tagging, locks, naming). This skill is laser-focused on Defender for Cloud Secure Score — the single most important security KPI — and provides targeted actions to improve it.
Assessment procedure
Step 1: Current Secure Score
Get the overall score and per-control breakdown.
az rest --method get --url "https://management.azure.com/subscriptions/<sub-id>/providers/Microsoft.Security/secureScores/ascScore?api-version=2020-01-01"
az rest --method get --url "https://management.azure.com/subscriptions/<sub-id>/providers/Microsoft.Security/secureScores/ascScore/secureScoreControls?api-version=2020-01-01"
Note: The az security secure-scores list CLI command may fail in some environments. The REST API approach above is more reliable. Replace <sub-id> with the target subscription ID.
Report:
- Current Secure Score: X% (X/Y points)
- Trend (if memory available): improved/declined vs. last check
- Top 5 controls with the most room for improvement (highest
max - current)
Step 2: Unhealthy assessments (recommendations)
Get the list of active security recommendations.
az rest --method get --url "https://management.azure.com/subscriptions/<sub-id>/providers/Microsoft.Security/assessments?api-version=2021-06-01" --query "value[?properties.status.code=='Unhealthy'].{displayName:properties.displayName, severity:properties.metadata.severity, category:properties.metadata.categories[0], resourceId:properties.resourceDetails.id}"
Group by severity:
- 🔴 High: Must fix — significant risk
- 🟡 Medium: Should fix — moderate risk
- 🟢 Low: Nice to have — minimal risk
Step 3: Quick wins (most points for least effort)
Identify recommendations that:
- Affect the most resources (batch fix possible)
- Have the highest point value per control
- Can be fixed with a single CLI command or policy
Examples of quick wins:
- Enable HTTPS-only on App Services
- Enable secure transfer on storage accounts
- Enable Defender plans that are off
- Restrict public network access
Step 4: Defender plan coverage
Check which Defender plans are enabled.
az security pricing list --query "[].{plan:name, tier:pricingTier, subPlan:subPlan}" -o table
Expected plans to be enabled for full coverage:
- Servers (P2 recommended)
- App Service
- SQL (databases)
- Storage
- Containers (with vulnerability assessment)
- Key Vault
- Resource Manager (ARM)
- DNS
Flag disabled plans and estimate score impact of enabling them.
Step 5: Attack surface indicators
Check the most exploitable security gaps.
Public management ports (SSH/RDP open to internet):
az graph query -q "resources | where type == 'microsoft.network/networksecuritygroups' | mvexpand rules = properties.securityRules | where rules.properties.access == 'Allow' and rules.properties.direction == 'Inbound' and (rules.properties.destinationPortRange == '22' or rules.properties.destinationPortRange == '3389' or rules.properties.destinationPortRange == '*') and rules.properties.sourceAddressPrefix in ('*', '0.0.0.0/0', 'Internet')" -o tableStorage accounts with public blob access:
az storage account list --query "[?allowBlobPublicAccess==true].{name:name, rg:resourceGroup}" -o tableSQL servers with public endpoint:
az sql server list --query "[?properties.publicNetworkAccess=='Enabled'].{name:name, rg:resourceGroup}" -o tableKey Vaults accessible from public network:
az keyvault list --query "[?properties.publicNetworkAccess=='Enabled'].{name:name, rg:resourceGroup}" -o table
Step 6: Credential and certificate hygiene
Check for expiring secrets that could cause outages or security drift.
Key Vault secrets expiring within 30 days (per vault):
az keyvault secret list --vault-name <vault> --query "[?attributes.expires!=null && attributes.expires<'$(date -u -d '30 days' +%Y-%m-%dT%H:%M:%SZ)']" -o tableKey Vault certificates expiring within 30 days (per vault):
az keyvault certificate list --vault-name <vault> --query "[?attributes.expires!=null && attributes.expires<'$(date -u -d '30 days' +%Y-%m-%dT%H:%M:%SZ)']" -o tableApp registrations with expiring secrets:
az ad app list --query "[].{name:displayName, creds:passwordCredentials[?endDateTime<'$(date -u -d '30 days' +%Y-%m-%dT%H:%M:%SZ)']}" --all -o table
Note on date handling: The SRE Agent sandbox runs Linux. Use $(date -u -d '30 days' +%Y-%m-%dT%H:%M:%SZ) for date calculations.
Risk scoring
| Level | Criteria | Color |
|---|---|---|
| Critical | Active exposure, immediate risk of breach | 🔴 |
| High | Significant gap, exploit requires minimal effort | 🟠 |
| Medium | Gap exists but requires specific conditions to exploit | 🟡 |
| Low | Best practice deviation, minimal immediate risk | 🟢 |
Accepted exceptions (optional)
If the user provides a list of accepted exceptions, do not flag those items. Instead, note them in the report as Accepted Exception with the reason provided.
Example format the user may provide:
| Check | Reason |
|---|---|
| 3.1 Defender for DNS | DNS plan deprecated by Microsoft, not applicable |
| 5.2 Public blob access on storage | Public read access required for CDN-served static assets |
| 5.4 Key Vault public access | Key Vault is accessed from hybrid on-prem workloads |
When exceptions are provided:
- Skip the flagged checks in scoring
- List them in a separate "Accepted Exceptions" section at the end of the report
- Recalculate the overall score excluding excepted checks
Expected output
Report header (mandatory — use this exact format)
Defender Secure Score Report
| Field | Value |
|---|---|
| Subscription | (name + ID) |
| Assessment Date | YYYY-MM-DD |
| Overall Score | XX.XX / YY (ZZ%) |
Then present the gap summary:
The subscription is earning only ZZ% of its maximum Secure Score. There is a N-point gap to close.
Score improvement plan
| Priority | Action | Points | Effort | Resources affected |
|---|---|---|---|---|
| 1 | Enable HTTPS-only on 4 App Services | +4 | 5 min | app-web-, app-api- |
| 2 | Enable Defender for Storage | +3 | 2 min | subscription-level |
| 3 | Restrict SQL public access on 2 servers | +3 | 30 min | sql-prod-, sql-staging- |
| 4 | Add NSG to 2 subnets without rules | +2 | 15 min | subnet-data-, subnet-mgmt- |
| Total achievable | +12 | ~1 hour |
Critical findings (fix now)
Items that represent immediate exploitable risk.
Remediation commands
Pre-formatted az cli commands for each quick win, ready to approve and execute.
Use GetAzCliHelp to validate the command syntax before suggesting.
# Enable HTTPS-only on App Service
az webapp update --name <app> --resource-group <rg> --set httpsOnly=true
# Enable Defender for Storage
az security pricing create --name StorageAccounts --tier Standard
References
- Defender for Cloud: https://learn.microsoft.com/en-us/azure/defender-for-cloud/secure-score-security-controls
- Security Recommendations: https://learn.microsoft.com/en-us/azure/defender-for-cloud/recommendations-reference
- Defender Plans: https://learn.microsoft.com/en-us/azure/defender-for-cloud/defender-for-cloud-introduction
Recommended cadence
Schedule this skill as a weekly task via SRE Agent scheduled tasks for continuous score improvement tracking.
Sample output
The following is a redacted example of what the report looks like when run against a subscription.
Defender Secure Score Report
| Field | Value |
|---|---|
| Subscription | contoso-prod-001 (a1b2c3d4-e5f6-7890-abcd-ef1234567890) |
| Assessment Date | 2026-07-15 |
| Overall Score | 38.50 / 56 (69%) |
The subscription is earning only 69% of its maximum Secure Score. There is a 17.5-point gap to close.
Score improvement plan
| Priority | Action | Points | Effort | Resources affected |
|---|---|---|---|---|
| 1 | Enable HTTPS-only on 3 App Services | +4 | 5 min | app-web-prod, app-api-prod, app-admin-prod |
| 2 | Enable Defender for Storage | +3 | 2 min | subscription-level |
| 3 | Restrict SQL public access on 2 servers | +3 | 30 min | sql-contoso-prod, sql-contoso-staging |
| 4 | Add NSG to 2 subnets without rules | +2 | 15 min | subnet-data-prod, subnet-mgmt-prod |
| 5 | Rotate 3 expiring Key Vault secrets | +1.5 | 20 min | kv-contoso-prod |
| Total achievable | +13.5 | ~1.5 hours |
Critical findings (sample)
| # | Finding | Severity | Resources |
|---|---|---|---|
| 1 | SSH port 22 open to internet on NSG nsg-mgmt-prod |
🔴 Critical | 2 VMs exposed |
| 2 | Storage account stcontosoprod allows public blob access |
🔴 Critical | 1 account |
| 3 | Defender for Containers disabled | 🟠 High | AKS cluster aks-app-prod |
Defender plan coverage
| Plan | Status | Impact |
|---|---|---|
| Cloud Posture (CSPM) | ✅ Standard | — |
| Servers | ✅ P2 | — |
| Storage | ❌ Free | +3 points if enabled |
| Containers | ❌ Free | +2 points if enabled |
| Key Vault | ✅ Standard | — |
| App Service | ✅ Standard | — |
Remediation commands (sample)
# Enable HTTPS-only on App Service
az webapp update --name app-web-prod --resource-group rg-app-prod --set httpsOnly=true
# Enable Defender for Storage
az security pricing create --name StorageAccounts --tier Standard
# Restrict SQL Server public access
az sql server update --name sql-contoso-prod --resource-group rg-data-prod --set publicNetworkAccess="Disabled"