Microsoft Defender for Cloud provides built-in compliance assessment against the CIS Azure Foundations Benchmark, helping organizations measure and improve their security posture. This guide covers enabling CIS compliance monitoring, understanding controls, implementing remediation, and tracking progress through Secure Score.
This article is part of our comprehensive guide on Cloud Security Tips for 2026, which covers essential security practices across all major cloud platforms.
Overview
The CIS Azure Foundations Benchmark in Defender for Cloud includes:
- 100+ controls across Azure services
- Automated assessment of configuration compliance
- Remediation guidance for each control
- Compliance scoring with trend tracking
- Export capabilities for audit reports
Prerequisites
Before implementing CIS compliance, ensure you have:
- Microsoft Defender for Cloud enabled on your subscription
- Security Reader or Security Admin role
- Azure CLI installed (version 2.50 or later)
- Understanding of your compliance requirements
- Remediation permissions (Contributor role for fixes)
Step 1: Enable CIS Benchmark Assessment
Enable via Azure Portal
- Sign in to the Azure Portal
- Navigate to Microsoft Defender for Cloud
- Go to Environment settings
- Select your subscription
- Click Security policy
- Under Regulatory compliance, click Add more standards
- Find CIS Microsoft Azure Foundations Benchmark and click Add
- Select the benchmark version (latest recommended)
- Click Save
Enable via Azure CLI
# Get subscription ID
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
# List available regulatory compliance standards
az security regulatory-compliance-standards list \
--query "[].{Name:name, State:state}" \
-o table
# Enable CIS Azure Foundations Benchmark v2.0
az security regulatory-compliance-standards update \
--name "CIS-Azure-2.0.0" \
--state Enabled
# Verify enablement
az security regulatory-compliance-standards show \
--name "CIS-Azure-2.0.0" \
--query "{Name:name, State:state}"
Enable via REST API
# Enable CIS benchmark using REST API
az rest \
--method PUT \
--url "https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/providers/Microsoft.Security/regulatoryComplianceStandards/CIS-Azure-2.0.0?api-version=2019-01-01-preview" \
--body '{"properties": {"state": "Enabled"}}'
Step 2: Review Compliance Dashboard
Access Regulatory Compliance Dashboard
- In Defender for Cloud, go to Regulatory compliance
- View compliance overview:
- Overall compliance percentage
- Passed vs. failed controls
- Controls by severity
- Select CIS Microsoft Azure Foundations Benchmark
- Review control families:
- Identity and Access Management
- Security Center
- Storage Accounts
- Database Services
- Logging and Monitoring
- Networking
- Virtual Machines
- Other Security Considerations
Query Compliance via Azure CLI
# Get overall compliance state for CIS benchmark
az security regulatory-compliance-standards show \
--name "CIS-Azure-2.0.0" \
--query "{Standard:name, Passed:passedControls, Failed:failedControls, Skipped:skippedControls}"
# List all CIS controls and their state
az security regulatory-compliance-controls list \
--standard-name "CIS-Azure-2.0.0" \
--query "[].{Control:name, Description:description, State:state}" \
-o table
# Get failed controls only
az security regulatory-compliance-controls list \
--standard-name "CIS-Azure-2.0.0" \
--query "[?state=='Failed'].{Control:name, Description:description}" \
-o table
Query with Azure Resource Graph
# Comprehensive CIS compliance query
az graph query -q "
securityresources
| where type == 'microsoft.security/regulatorycompliancestandards/regulatorycompliancecontrols'
| where name contains 'CIS'
| extend controlState = tostring(properties.state)
| extend controlName = tostring(properties.description)
| summarize Passed=countif(controlState == 'Passed'),
Failed=countif(controlState == 'Failed'),
Skipped=countif(controlState == 'Skipped')
"
# Get non-compliant assessments with affected resources
az graph query -q "
securityresources
| where type == 'microsoft.security/regulatorycompliancestandards/regulatorycompliancecontrols/regulatorycomplianceassessments'
| where properties.state == 'Failed'
| extend control = tostring(split(id, '/')[10])
| extend assessment = tostring(properties.description)
| extend resourceCount = toint(properties.failedResources)
| project control, assessment, resourceCount
| order by resourceCount desc
"
Step 3: Understand CIS Control Categories
Identity and Access Management (Section 1)
| Control ID | Description | Level |
|---|---|---|
| 1.1 | Ensure Security Defaults is enabled | L1 |
| 1.2 | Ensure MFA is enabled for all users | L1 |
| 1.3 | Ensure guest users are reviewed monthly | L1 |
| 1.4 | Ensure no custom subscription owner roles | L1 |
| 1.5 | Enable Conditional Access policies | L2 |
Defender for Cloud (Section 2)
| Control ID | Description | Level |
|---|---|---|
| 2.1 | Enable enhanced security features | L2 |
| 2.2 | Enable auto provisioning of agents | L1 |
| 2.3 | Configure email notifications | L1 |
| 2.4 | Enable integration with Microsoft Defender | L2 |
Storage Accounts (Section 3)
| Control ID | Description | Level |
|---|---|---|
| 3.1 | Require secure transfer | L1 |
| 3.2 | Enable infrastructure encryption | L2 |
| 3.3 | Enable soft delete for blob data | L1 |
| 3.4 | Use private endpoints | L2 |
| 3.5 | Disable public blob access | L1 |
Database Services (Section 4)
| Control ID | Description | Level |
|---|---|---|
| 4.1 | Enable auditing on SQL servers | L1 |
| 4.2 | Enable threat detection on SQL | L2 |
| 4.3 | Enable TDE with customer-managed keys | L2 |
| 4.4 | Configure SQL firewall rules | L1 |
Logging and Monitoring (Section 5)
| Control ID | Description | Level |
|---|---|---|
| 5.1 | Enable diagnostic settings | L1 |
| 5.2 | Configure activity log alerts | L1 |
| 5.3 | Enable Network Watcher | L2 |
| 5.4 | Configure log retention | L1 |
Step 4: Remediate Non-Compliant Controls
View Control Details
- In Regulatory compliance, click on a failed control
- Review:
- Control description and requirements
- Affected assessments
- Non-compliant resources
- Remediation guidance
Remediation Examples
Enable Secure Transfer for Storage (3.1):
# List storage accounts without secure transfer
az storage account list \
--query "[?enableHttpsTrafficOnly==\`false\`].{Name:name, ResourceGroup:resourceGroup}" \
-o table
# Enable secure transfer
az storage account update \
--name "mystorageaccount" \
--resource-group "rg-production" \
--https-only true
Configure SQL Auditing (4.1):
# Enable auditing on SQL server
STORAGE_ACCOUNT="stauditlogs"
SQL_SERVER="sql-production"
az sql server audit-policy update \
--resource-group "rg-databases" \
--server $SQL_SERVER \
--state Enabled \
--storage-account $STORAGE_ACCOUNT \
--retention-days 90
# Enable blob auditing for database
az sql db audit-policy update \
--resource-group "rg-databases" \
--server $SQL_SERVER \
--database "mydb" \
--state Enabled
Enable Activity Log Alerts (5.2):
# Create action group
az monitor action-group create \
--resource-group "rg-monitoring" \
--name "ag-security-alerts" \
--short-name "SecAlerts" \
--email-receivers name="SecurityTeam" email-address="[email protected]"
# Create activity log alert for security policy changes
az monitor activity-log alert create \
--resource-group "rg-monitoring" \
--name "alert-policy-changes" \
--action-group "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/rg-monitoring/providers/Microsoft.Insights/actionGroups/ag-security-alerts" \
--condition category=Security \
--scope "/subscriptions/$SUBSCRIPTION_ID"
# Create alert for network security group changes
az monitor activity-log alert create \
--resource-group "rg-monitoring" \
--name "alert-nsg-changes" \
--action-group "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/rg-monitoring/providers/Microsoft.Insights/actionGroups/ag-security-alerts" \
--condition category=Administrative and operationName="Microsoft.Network/networkSecurityGroups/write" \
--scope "/subscriptions/$SUBSCRIPTION_ID"
Enable MFA for Users (1.2):
# Check Conditional Access MFA policy status
az rest \
--method GET \
--url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" \
--query "value[?contains(displayName, 'MFA')].{Name:displayName, State:state}"
# Note: Creating Conditional Access policies requires Microsoft Graph PowerShell
# Use the Entra admin center for GUI-based configuration
Bulk Remediation with PowerShell
# Remediate all storage accounts without secure transfer
$subscriptionId = (Get-AzContext).Subscription.Id
$storageAccounts = Get-AzStorageAccount | Where-Object { $_.EnableHttpsTrafficOnly -eq $false }
foreach ($sa in $storageAccounts) {
Write-Host "Enabling secure transfer on $($sa.StorageAccountName)..."
Set-AzStorageAccount -ResourceGroupName $sa.ResourceGroupName `
-Name $sa.StorageAccountName `
-EnableHttpsTrafficOnly $true
}
# Remediate all SQL servers without auditing
$sqlServers = Get-AzSqlServer
foreach ($server in $sqlServers) {
$auditPolicy = Get-AzSqlServerAudit -ResourceGroupName $server.ResourceGroupName `
-ServerName $server.ServerName
if ($auditPolicy.BlobStorageTargetState -ne "Enabled") {
Write-Host "Enabling auditing on $($server.ServerName)..."
Set-AzSqlServerAudit -ResourceGroupName $server.ResourceGroupName `
-ServerName $server.ServerName `
-State Enabled `
-StorageAccountName "stauditlogs"
}
}
Step 5: Track Secure Score
View Score Impact
- In Defender for Cloud, click Secure Score
- Review score breakdown:
- Overall score percentage
- Points by security control
- Score trend over time
- Click Regulatory compliance to see CIS-specific impact
Query Secure Score via CLI
# Get current Secure Score
az security secure-score list \
--query "[].{Control:displayName, Current:score.current, Max:score.max, Percentage:score.percentage}" \
-o table
# Get score by control
az security secure-score-controls list \
--query "[].{Control:displayName, Current:score.current, Max:score.max, Unhealthy:unhealthyResourceCount}" \
-o table | head -20
# Calculate improvement from CIS remediation
az security secure-score-controls show \
--name "Remediate vulnerabilities" \
--query '{Control:displayName, CurrentScore:score.current, MaxScore:score.max, PotentialGain:to_string(score.max - score.current)}'
Set Compliance Targets
# Export compliance baseline for tracking
az security regulatory-compliance-assessments list \
--standard-name "CIS-Azure-2.0.0" \
--control-name "1.1" \
--query "[].{Assessment:name, State:state, SkippedResources:skippedResources}" \
-o json > cis-baseline-$(date +%Y%m%d).json
# Create compliance report
az graph query -q "
securityresources
| where type == 'microsoft.security/regulatorycompliancestandards/regulatorycompliancecontrols'
| where name startswith 'CIS'
| extend standard = tostring(split(id, '/')[8])
| extend control = tostring(properties.description)
| extend state = tostring(properties.state)
| project standard, control, state
" -o json > cis-compliance-report.json
Step 6: Generate Compliance Reports
Export from Portal
- In Regulatory compliance, select CIS benchmark
- Click Download report
- Choose format:
- PDF for executive summary
- CSV for detailed analysis
Generate Reports via PowerShell
# Generate CIS compliance report
$standards = Get-AzSecurityRegulatoryComplianceStandard
foreach ($standard in $standards | Where-Object { $_.Name -like "*CIS*" }) {
$controls = Get-AzSecurityRegulatoryComplianceControl -StandardName $standard.Name
$report = $controls | Select-Object @{
Name = 'ControlId'
Expression = { $_.Name }
}, @{
Name = 'Description'
Expression = { $_.Description }
}, @{
Name = 'State'
Expression = { $_.State }
}, @{
Name = 'PassedAssessments'
Expression = { $_.PassedAssessments }
}, @{
Name = 'FailedAssessments'
Expression = { $_.FailedAssessments }
}
$report | Export-Csv -Path "CIS-Compliance-$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation
}
Continuous Export to Log Analytics
# Configure continuous export for compliance data
WORKSPACE_ID=$(az monitor log-analytics workspace show \
--resource-group "rg-security" \
--workspace-name "law-security" \
--query id -o tsv)
az security automation create \
--name "export-compliance-data" \
--resource-group "rg-security" \
--scopes "/subscriptions/$SUBSCRIPTION_ID" \
--sources '[{"eventSource": "RegulatoryComplianceAssessment"}]' \
--actions "[{\"actionType\": \"LogAnalytics\", \"workspaceResourceId\": \"$WORKSPACE_ID\"}]"
Best Practices
- Prioritize Level 1 controls: Implement all L1 before L2
- Address high-impact first: Focus on controls affecting most resources
- Document exceptions: Use policy exemptions with justification
- Track trends: Monitor compliance percentage weekly
- Automate remediation: Use Azure Policy for continuous enforcement
- Review monthly: Reassess exemptions and new resources
- Integrate with ITSM: Create tickets for non-compliance
- Train teams: Ensure developers understand CIS requirements
Troubleshooting
Controls showing stale data:
- Allow 4-24 hours for assessment refresh
- Trigger manual evaluation:
az security assessment - Verify resource is in scope of subscription
Remediation not reflecting in compliance:
- Confirm remediation completed successfully
- Check for dependent controls that also need remediation
- Wait for next evaluation cycle (up to 24 hours)
Missing controls or assessments:
- Verify Defender for Cloud is enabled
- Check required Defender plans are active
- Confirm resource types match control requirements
Exemptions not working:
- Verify exemption scope matches resource
- Check exemption expiration date
- Confirm exemption category is appropriate
Next Steps
After implementing CIS compliance, enhance your security posture:
- Enable additional standards (PCI DSS, HIPAA, SOC 2)
- Implement Azure Policy for preventive controls
- Configure Microsoft Sentinel for security monitoring
- Review Cloud Security Tips for 2026 for comprehensive cloud security guidance