CIS Microsoft Azure Foundations Benchmark

Secure configuration guidelines for Microsoft Azure cloud platform

v3.0.0 March 2025

Overview

▶

This CIS Benchmark provides prescriptive guidance for establishing a secure configuration posture for Microsoft Azure. The benchmark covers identity and access management, security center settings, storage, databases, logging, networking, virtual machines, key vault, and app services.

~200Recommendations
9Sections
2Profile Levels
Scope: This benchmark targets the Azure control plane and key PaaS/IaaS services. It does not cover application-level security within workloads deployed on Azure.
SectionAreaFocus
1Identity and Access ManagementEntra ID, Conditional Access, MFA, PIM, Guest Access
2Microsoft Defender for CloudDefender Plans, Security Contacts, Auto-provisioning
3Storage AccountsSecure Transfer, Encryption, Access Keys, SAS Tokens
4Database ServicesAzure SQL, PostgreSQL, MySQL, Cosmos DB
5Logging and MonitoringDiagnostic Settings, Activity Logs, Alerts
6NetworkingNSGs, Network Watcher, VPN, Public IP
7Virtual MachinesOS Disk Encryption, Extensions, Managed Disks
8Key VaultKey Management, Secrets, Certificates, RBAC
9AppServiceAuthentication, TLS, Managed Identity, HTTPS

Profile Definitions

▶
ProfileDescriptionIntended Use
L1Level 1Practical baseline security settings for all Azure subscriptions. Minimal performance impact.
L2Level 2Defense-in-depth settings for security-sensitive environments. May affect usability or cost.

1 — Identity and Access Management

▶

Microsoft Entra ID configuration including conditional access, multi-factor authentication, guest access, and privileged identity management.

1.1 Security Defaults & Conditional Access

▶
1.1.1 Ensure Security Defaults are Disabled When Using Conditional Access (Manual)
L1
Description

Security Defaults provide basic identity security. However, organizations implementing Conditional Access must disable Security Defaults — the two features are mutually exclusive. Conditional Access provides more granular control.

Rationale

Security Defaults cannot coexist with Conditional Access policies. Organizations with Conditional Access gain more fine-grained control over authentication flows and can enforce MFA selectively.

Audit
az rest --method GET \
  --url 'https://graph.microsoft.com/v1.0/policies/identitySecurityDefaultsEnforcementPolicy' \
  --query 'isEnabled'
# If Conditional Access policies exist, this should be false
Remediation

Navigate to Microsoft Entra admin center > Identity > Overview > Properties > Manage security defaults. Set to Disabled only after Conditional Access policies are in place.

1.2 MFA & Authentication

▶
1.2.1 Ensure Multi-Factor Authentication is Required for All Users in All Applications (Manual)
L1
Description

A Conditional Access policy should exist that requires MFA for all users accessing all cloud applications, excluding emergency access accounts.

Rationale

Failure to multi-Factor Authentication is Required for All Users in All Applications may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
# Review Conditional Access policies in Entra admin center:
# Protection > Conditional Access > Policies
# Look for policy: Users=All, Apps=All Cloud Apps, Grant=Require MFA, State=On
Remediation

Create a Conditional Access policy targeting All Users (excluding break-glass accounts), All Cloud Apps, with Grant control set to Require multifactor authentication.

CIS Controls
ControlDescriptionIG
6.3Require MFA for Externally-Exposed ApplicationsIG1
6.5Require MFA for Administrative AccessIG1
1.2.2 Ensure That Microsoft Authenticator is Configured to Protect Against MFA Fatigue (Manual)
L1
Description

Configure Microsoft Authenticator with number matching and additional context (application name and geographic location) to prevent MFA fatigue attacks where users are bombarded with push notifications until they approve.

Rationale

Failure to that Microsoft Authenticator is Configured to Protect Against MFA Fatigue may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit

Navigate to Entra admin center > Protection > Authentication methods > Microsoft Authenticator. Verify: Number matching = Enabled, Show application name = Enabled, Show geographic location = Enabled.

Remediation

Enable number matching and additional context in the Microsoft Authenticator authentication method settings.

1.3 Guest & External Access

▶
1.3.1 Ensure that Guest Users Have Restricted Access to Azure AD Directory (Automated)
L1 Auto
Description

Restrict guest users so they can only see their own user profile. By default, guests can enumerate users, groups, and other directory objects. Set guest user access to most restrictive.

Rationale

Failure to that Guest Users Have Restricted Access to Azure AD Directory may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az rest --method GET \
  --url 'https://graph.microsoft.com/v1.0/policies/authorizationPolicy' \
  --query 'guestUserRoleId'
# Should be: 2af84b1e-32c8-42b7-82bc-daa82404023b (most restrictive)
Remediation

Navigate to External Identities > External collaboration settings. Set Guest user access restrictions to the most restrictive option.

1.4 Custom & Privileged Roles

▶
1.4.1 Ensure Access Reviews for High-Privileged Azure AD Roles are Configured (Manual)
L2
Description

Configure recurring access reviews for high-privileged roles (Global Administrator, Privileged Role Administrator, etc.) through Entra ID Governance. Reviews should be at least quarterly.

Rationale

Misconfiguration of Access Reviews for High-Privileged Azure AD Roles can lead to security gaps that may be exploited by attackers. A properly configured Azure cloud platform reduces exposure to both known vulnerabilities and configuration drift.

Audit

Navigate to Identity Governance > Access Reviews. Verify reviews exist for: Global Administrator, Security Administrator, Privileged Role Administrator, Exchange Administrator, SharePoint Administrator.

Remediation

Create access reviews for each high-privileged role with quarterly frequency and auto-remove on non-response.

1.4.2 Ensure Custom Role Assignments Are Reviewed and Minimized (Manual)
L1
Description

Custom roles in Azure can have overly broad permissions. Review all custom role definitions to ensure they follow the principle of least privilege and do not grant wildcard (*) actions unnecessarily.

Rationale

Failure to custom Role Assignments Are Reviewed and Minimized may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az role definition list --custom-role-only true \
  --query '[].{Name:roleName, Actions:permissions[0].actions}' -o table
Remediation

Review each custom role and remove unnecessary permissions. Replace wildcard actions with specific action strings.

2 — Microsoft Defender for Cloud

▶

Microsoft Defender for Cloud provides unified security management and advanced threat protection across Azure workloads.

2.1 Defender Plans

▶
2.1.1 Ensure Microsoft Defender for Servers is Set to On (Automated)
L2 Auto
Description

Enable Microsoft Defender for Servers to gain threat detection for VMs including file integrity monitoring, adaptive application controls, and just-in-time VM access.

Rationale

An improperly configured value for Microsoft Defender for Servers could weaken security controls or allow unintended behavior. Setting this to On ensures the Azure cloud platform operates within a well-defined security boundary.

Audit
az security pricing show -n VirtualMachines --query 'pricingTier'
# Should be "Standard"
Remediation
az security pricing create -n VirtualMachines --tier Standard
2.1.2 Ensure Microsoft Defender for Key Vault is Set to On (Automated)
L2 Auto
Description

Enable Defender for Key Vault to detect unusual access patterns, potentially harmful operations, and suspicious activities related to key vault resources.

Rationale

An improperly configured value for Microsoft Defender for Key Vault could weaken security controls or allow unintended behavior. Setting this to On ensures the Azure cloud platform operates within a well-defined security boundary.

Audit
az security pricing show -n KeyVaults --query 'pricingTier'
# Should be "Standard"
Remediation
az security pricing create -n KeyVaults --tier Standard
2.1.3 Ensure Microsoft Defender for Storage is Set to On (Automated)
L2 Auto
Description

Enable Defender for Storage to detect malicious file uploads, data exfiltration, and suspicious access patterns to storage accounts.

Rationale

An improperly configured value for Microsoft Defender for Storage could weaken security controls or allow unintended behavior. Setting this to On ensures the Azure cloud platform operates within a well-defined security boundary.

Audit
az security pricing show -n StorageAccounts --query 'pricingTier'
# Should be "Standard"
Remediation
az security pricing create -n StorageAccounts --tier Standard

2.2 Security Contacts

▶
2.2.1 Ensure Security Alert Emails are Configured to Relevant Contacts (Automated)
L1 Auto
Description

Configure security contact email addresses in Defender for Cloud to receive alert notifications. Include subscription owners and security team distribution lists.

Rationale

Failure to security Alert Emails are Configured to Relevant Contacts may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az security contact list --query '[].{Email:email, AlertNotifications:alertNotifications}'
# Verify email addresses are set and alertNotifications is "On"
Remediation
az security contact create -n default \
  --email security@yourdomain.com \
  --alert-notifications On \
  --alerts-to-admins On

3 — Storage Accounts

▶

Configuration of Azure Storage accounts covering encryption, access control, network restrictions, and secure transfer requirements.

3.1 Secure Transfer & Encryption

▶
3.1.1 Ensure Secure Transfer Required is Set to Enabled (Automated)
L1 Auto
Description

Require secure transfer (HTTPS) for all storage account operations. This prevents data from being transmitted in clear text over HTTP.

Rationale

An improperly configured value for Secure Transfer Required could weaken security controls or allow unintended behavior. Setting this to Enabled ensures the Azure cloud platform operates within a well-defined security boundary.

Audit
az storage account list --query '[].{Name:name, SecureTransfer:enableHttpsTrafficOnly}' -o table
# All should show True
Remediation
az storage account update -n <storageAccountName> -g <resourceGroup> \
  --https-only true
3.1.2 Ensure Storage Account Infrastructure Encryption is Enabled (Automated)
L2 Auto
Description

Enable infrastructure encryption (double encryption) for storage accounts. This adds a second layer of encryption at the infrastructure level using a platform-managed key, in addition to service-level encryption.

Rationale

Without Storage Account Infrastructure Encryption enabled, the Azure cloud platform may lack critical protections against known attack vectors. Enabling this control mitigates risk and aligns the deployment with industry-accepted security baselines.

Audit
az storage account show -n <account> -g <rg> \
  --query 'encryption.requireInfrastructureEncryption'
# Should be true
Remediation

Infrastructure encryption can only be enabled at storage account creation. Create a new storage account with --require-infrastructure-encryption true and migrate data.

3.1.3 Ensure Minimum TLS Version is Set to 1.2 (Automated)
L1 Auto
Description

Set the minimum TLS version to 1.2 for all storage accounts. TLS 1.0 and 1.1 have known vulnerabilities and should be disabled.

Rationale

An improperly configured value for Minimum TLS Version could weaken security controls or allow unintended behavior. Setting this to 1.2 ensures the Azure cloud platform operates within a well-defined security boundary.

Audit
az storage account list --query '[].{Name:name, MinTLS:minimumTlsVersion}' -o table
# All should show TLS1_2
Remediation
az storage account update -n <account> -g <rg> --min-tls-version TLS1_2

3.2 Access Keys & SAS

▶
3.2.1 Ensure Storage Account Access Keys are Periodically Regenerated (Manual)
L1
Description

Storage account access keys should be regenerated periodically. When keys are regenerated, applications using those keys must be updated. Consider using Entra ID authentication or managed identities instead of access keys.

Rationale

Failure to storage Account Access Keys are Periodically Regenerated may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az monitor activity-log list --resource-group <rg> \
  --query "[?operationName.value=='Microsoft.Storage/storageAccounts/regenerateKey/action']" \
  --start-time $(date -d '90 days ago' '+%Y-%m-%dT%H:%M:%SZ')
Remediation

Rotate keys every 90 days or migrate to Entra ID authentication with RBAC instead of shared keys.

3.2.2 Ensure Public Network Access to Storage Accounts is Disabled (Manual)
L2
Description

Disable public network access to storage accounts and use private endpoints instead. This ensures storage traffic traverses the Microsoft backbone network rather than the public internet.

Rationale

Leaving Public Network Access to Storage Accounts enabled when it is not required unnecessarily expands the attack surface. An attacker could leverage this feature to gain unauthorized access or escalate privileges on the Azure cloud platform.

Audit
az storage account list --query '[].{Name:name, PublicAccess:publicNetworkAccess}' -o table
# Should show "Disabled"
Remediation
az storage account update -n <account> -g <rg> --public-network-access Disabled
# Then create private endpoints for required connectivity

4 — Database Services

▶

Security configuration for Azure managed database services including SQL Database, PostgreSQL, and MySQL.

4.1 Azure SQL

▶
4.1.1 Ensure Auditing is Set to On for SQL Servers (Automated)
L1 Auto
Description

Enable auditing for all Azure SQL servers. Audit logs capture database events and write them to a storage account, Log Analytics workspace, or Event Hub.

Rationale

An improperly configured value for Auditing could weaken security controls or allow unintended behavior. Setting this to On for SQL Servers ensures the Azure cloud platform operates within a well-defined security boundary.

Audit
az sql server audit-policy show --resource-group <rg> --server <server> \
  --query 'state'
# Should be "Enabled"
Remediation
az sql server audit-policy update --resource-group <rg> --server <server> \
  --state Enabled \
  --storage-account <storageAccountId> \
  --retention-days 90
4.1.2 Ensure Transparent Data Encryption (TDE) is Enabled on SQL Databases (Automated)
L1 Auto
Description

Transparent Data Encryption encrypts data at rest for Azure SQL databases. TDE is enabled by default for new databases but should be verified for all existing databases.

Rationale

Failure to transparent Data Encryption (TDE) is Enabled on SQL Databases may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az sql db tde show --resource-group <rg> --server <server> --database <db> \
  --query 'state'
# Should be "Enabled"
Remediation
az sql db tde set --resource-group <rg> --server <server> --database <db> \
  --status Enabled
4.1.3 Ensure No Azure SQL Databases Allow Ingress from 0.0.0.0/0 (Automated)
L1 Auto
Description

Ensure SQL server firewall rules do not allow access from all Azure services (0.0.0.0) or all IP addresses. Each SQL server should have specific IP ranges or use private endpoints.

Rationale

Failure to no Azure SQL Databases Allow Ingress from 0.0.0.0/0 may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az sql server firewall-rule list --resource-group <rg> --server <server> \
  --query '[].{Name:name, Start:startIpAddress, End:endIpAddress}' -o table
# Should NOT have 0.0.0.0 - 255.255.255.255 or 0.0.0.0 - 0.0.0.0
Remediation
az sql server firewall-rule delete --resource-group <rg> --server <server> \
  --name AllowAllWindowsAzureIps

4.2 PostgreSQL / MySQL

▶
4.2.1 Ensure Enforce SSL Connection is Set to ENABLED for PostgreSQL (Automated)
L1 Auto
Description

Enable SSL enforcement on Azure Database for PostgreSQL flexible servers to ensure all client connections use TLS encryption.

Rationale

An improperly configured value for Enforce SSL Connection could weaken security controls or allow unintended behavior. Setting this to ENABLED for PostgreSQL ensures the Azure cloud platform operates within a well-defined security boundary.

Audit
az postgres flexible-server parameter show --resource-group <rg> \
  --server-name <server> --name require_secure_transport \
  --query 'value'
# Should be "ON"
Remediation
az postgres flexible-server parameter set --resource-group <rg> \
  --server-name <server> --name require_secure_transport --value ON
4.2.2 Ensure Server Parameter log_checkpoints is Set to ON for PostgreSQL (Automated)
L1 Auto
Description

Enable log_checkpoints to log each checkpoint in the PostgreSQL server log. This provides useful diagnostic information for performance tuning and security investigations.

Rationale

An improperly configured value for Server Parameter log_checkpoints could weaken security controls or allow unintended behavior. Setting this to ON for PostgreSQL ensures the Azure cloud platform operates within a well-defined security boundary.

Audit
az postgres flexible-server parameter show --resource-group <rg> \
  --server-name <server> --name log_checkpoints --query 'value'
# Should be "ON"
Remediation
az postgres flexible-server parameter set --resource-group <rg> \
  --server-name <server> --name log_checkpoints --value ON

5 — Logging and Monitoring

▶

Azure diagnostic settings, activity log configuration, and alert policies for security monitoring.

5.1 Diagnostic Settings

▶
5.1.1 Ensure Diagnostic Settings Capture All Activity Log Categories (Automated)
L1 Auto
Description

Configure diagnostic settings for the subscription to capture all activity log categories: Administrative, Security, ServiceHealth, Alert, Recommendation, Policy, Autoscale, and ResourceHealth.

Rationale

Failure to diagnostic Settings Capture All Activity Log Categories may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az monitor diagnostic-settings subscription list \
  --query '[].logs[].{Category:category, Enabled:enabled}' -o table
Remediation

Navigate to Monitor > Activity log > Export Activity Logs and create a diagnostic setting that sends all categories to a Log Analytics workspace or storage account.

5.2 Activity Log Alerts

▶
5.2.1 Ensure Activity Log Alert Exists for Create/Update Network Security Group (Automated)
L1 Auto
Description

Create an activity log alert to fire when a Network Security Group is created or updated. This detects unauthorized network configuration changes.

Rationale

Failure to activity Log Alert Exists for Create/Update Network Security Group may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az monitor activity-log alert list --query "[?condition.allOf[?field=='operationName' && equals=='Microsoft.Network/networkSecurityGroups/write']]"
Remediation
az monitor activity-log alert create -n "NSG-Create-Update" -g <rg> \
  --condition category=Administrative \
    and operationName=Microsoft.Network/networkSecurityGroups/write \
  --action-group <actionGroupId>
5.2.2 Ensure Activity Log Alert Exists for Create/Update/Delete Security Policy (Automated)
L1 Auto
Description

Create activity log alerts for security policy changes to detect when Azure Policy assignments are created, modified, or deleted.

Rationale

Failure to activity Log Alert Exists for Create/Update/Delete Security Policy may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az monitor activity-log alert list \
  --query "[?condition.allOf[?field=='operationName' && equals=='Microsoft.Authorization/policyAssignments/write']]"
Remediation

Create activity log alerts for Microsoft.Authorization/policyAssignments/write and Microsoft.Authorization/policyAssignments/delete operations.

6 — Networking

▶

Network security configuration including Network Security Groups, Network Watcher, and public IP management.

6.1 Network Security Groups

▶
6.1.1 Ensure No NSG Allows Inbound from 0.0.0.0/0 to Port 22 (Automated)
L1 Auto
Description

Ensure no Network Security Group allows unrestricted inbound SSH access (port 22) from the internet (0.0.0.0/0 or Any). SSH access should be restricted to specific IP ranges or through Azure Bastion.

Rationale

Failure to no NSG Allows Inbound from 0.0.0.0/0 to Port 22 may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az network nsg list --query '[].{Name:name, RG:resourceGroup}' -o table
# Then for each NSG:
az network nsg rule list -g <rg> --nsg-name <nsg> \
  --query "[?destinationPortRange=='22' && sourceAddressPrefix=='*' && access=='Allow' && direction=='Inbound']"
Remediation

Restrict SSH rules to specific source IP addresses or CIDR ranges. Consider using Azure Bastion for SSH access to eliminate public SSH exposure entirely.

6.1.2 Ensure No NSG Allows Inbound from 0.0.0.0/0 to Port 3389 (Automated)
L1 Auto
Description

Ensure no NSG allows unrestricted inbound RDP access (port 3389) from the internet. RDP is a frequent target of brute force attacks and should be restricted or replaced by Azure Bastion.

Rationale

Failure to no NSG Allows Inbound from 0.0.0.0/0 to Port 3389 may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az network nsg rule list -g <rg> --nsg-name <nsg> \
  --query "[?destinationPortRange=='3389' && sourceAddressPrefix=='*' && access=='Allow' && direction=='Inbound']"
Remediation

Remove or restrict RDP rules. Use Azure Bastion or JIT VM access for remote management.

6.2 Network Watcher

▶
6.2.1 Ensure Network Watcher is Enabled for All Regions in Use (Automated)
L1 Auto
Description

Network Watcher provides network diagnostic and visualization tools. It should be enabled for every Azure region where network resources are deployed.

Rationale

Failure to network Watcher is Enabled for All Regions in Use may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az network watcher list --query '[].{Location:location, State:provisioningState}' -o table
Remediation
az network watcher configure --resource-group NetworkWatcherRG \
  --locations <region> --enabled true
6.2.2 Ensure NSG Flow Logs are Enabled and Sent to Log Analytics (Automated)
L2 Auto
Description

Enable NSG flow logs for all Network Security Groups and configure traffic analytics with a Log Analytics workspace. Flow logs provide visibility into network traffic patterns for security analysis.

Rationale

Failure to nSG Flow Logs are Enabled and Sent to Log Analytics may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az network watcher flow-log list --location <region> \
  --query '[].{NSG:targetResourceId, Enabled:enabled, Analytics:flowAnalyticsConfiguration.networkWatcherFlowAnalyticsConfiguration.enabled}'
Remediation
az network watcher flow-log create --location <region> --name <name> \
  --nsg <nsgId> --storage-account <storageId> --enabled true \
  --log-version 2 --retention 90 \
  --traffic-analytics true --workspace <workspaceId>

7 — Virtual Machines

▶

7.1 OS & Disk Encryption

▶
7.1.1 Ensure Virtual Machines Utilize Managed Disks (Automated)
L1 Auto
Description

Ensure all VM disks are managed disks rather than unmanaged (VHD). Managed disks provide built-in encryption, higher reliability, and better security integration.

Rationale

Failure to virtual Machines Utilize Managed Disks may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az vm list --query '[?storageProfile.osDisk.managedDisk==null].{Name:name, RG:resourceGroup}' -o table
# Should return empty (no VMs with unmanaged disks)
Remediation

Migrate VMs from unmanaged to managed disks using az vm convert.

7.1.2 Ensure OS and Data Disks are Encrypted with CMK (Automated)
L2 Auto
Description

Use customer-managed keys (CMK) stored in Azure Key Vault for disk encryption. This provides full control over the encryption keys and enables key rotation and revocation.

Rationale

Without encryption, OS and Data Disks may transmit or store sensitive information in cleartext, exposing it to interception, eavesdropping, or tampering. Encryption is a critical control for data confidentiality and integrity.

Audit
az vm encryption show --resource-group <rg> --name <vm> \
  --query '{OSDisk:disks[0].statuses[0].code, DataDisks:disks[1:]}'
Remediation

Enable Azure Disk Encryption with a CMK from Key Vault, or use disk encryption sets with customer-managed keys.

7.1.3 Ensure Only Approved VM Extensions are Installed (Manual)
L1
Description

Review installed VM extensions and ensure only approved, necessary extensions are installed. Unnecessary extensions increase the attack surface.

Rationale

Failure to only Approved VM Extensions are Installed may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az vm extension list --resource-group <rg> --vm-name <vm> \
  --query '[].{Name:name, Publisher:publisher, Type:typePropertiesType}' -o table
Remediation

Remove unnecessary extensions: az vm extension delete -g <rg> --vm-name <vm> -n <extension>

8 — Key Vault

▶

8.1 Key & Secret Management

▶
8.1.1 Ensure Key Vault is Recoverable (Soft Delete + Purge Protection) (Automated)
L1 Auto
Description

Ensure soft delete and purge protection are enabled on all Key Vaults. Soft delete retains deleted vaults/secrets for a retention period. Purge protection prevents permanent deletion during the retention period.

Rationale

Failure to key Vault is Recoverable (Soft Delete + Purge Protection) may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az keyvault list --query '[].{Name:name, SoftDelete:properties.enableSoftDelete, PurgeProtection:properties.enablePurgeProtection}' -o table
# Both should be true
Remediation
az keyvault update --name <vault> --enable-soft-delete true --enable-purge-protection true
8.1.2 Ensure Key Vault Uses RBAC Instead of Access Policies (Manual)
L1
Description

Configure Key Vaults to use Azure RBAC for access control instead of vault access policies. RBAC provides more granular permissions and integrates with Azure's unified access management.

Rationale

Failure to key Vault Uses RBAC Instead of Access Policies may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az keyvault list --query '[].{Name:name, RBAC:properties.enableRbacAuthorization}' -o table
# Should be true for all vaults
Remediation
az keyvault update --name <vault> --enable-rbac-authorization true
8.1.3 Ensure Key Vault Keys Have an Expiration Date Set (Automated)
L1 Auto
Description

All keys stored in Key Vault should have an expiration date configured to enforce key rotation and prevent indefinite use of cryptographic keys.

Rationale

Failure to key Vault Keys Have an Expiration Date Set may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az keyvault key list --vault-name <vault> \
  --query '[].{Name:name, Expires:attributes.expires, Enabled:attributes.enabled}' -o table
Remediation
az keyvault key set-attributes --vault-name <vault> --name <key> \
  --expires '2025-12-31T23:59:59Z'

9 — AppService

▶

9.1 Authentication & TLS

▶
9.1.1 Ensure App Service Authentication is Set on Azure App Service (Automated)
L1 Auto
Description

Enable App Service Authentication (EasyAuth) for web apps that require authentication. This built-in feature handles authentication without requiring code changes.

Rationale

Failure to app Service Authentication is Set on Azure App Service may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az webapp auth show --resource-group <rg> --name <app> --query 'enabled'
# Should be true
Remediation
az webapp auth update --resource-group <rg> --name <app> --enabled true \
  --action LoginWithAzureActiveDirectory
9.1.2 Ensure Web App Redirects All HTTP Traffic to HTTPS (Automated)
L1 Auto
Description

Enable HTTPS-only mode on web applications to redirect all HTTP traffic to HTTPS, ensuring all data in transit is encrypted.

Rationale

Failure to web App Redirects All HTTP Traffic to HTTPS may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az webapp show --resource-group <rg> --name <app> --query 'httpsOnly'
# Should be true
Remediation
az webapp update --resource-group <rg> --name <app> --set httpsOnly=true
9.1.3 Ensure Web App Uses the Latest TLS Version (Automated)
L1 Auto
Description

Set the minimum TLS version for web apps to 1.2 or higher to protect against known vulnerabilities in TLS 1.0 and 1.1.

Rationale

Failure to web App Uses the Latest TLS Version may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az webapp config show --resource-group <rg> --name <app> --query 'minTlsVersion'
# Should be "1.2" or "1.3"
Remediation
az webapp config set --resource-group <rg> --name <app> --min-tls-version 1.2
9.1.4 Ensure Web App Uses Managed Identity (Automated)
L1 Auto
Description

Use managed identities for web apps instead of storing credentials in app settings. Managed identities eliminate the need for developers to manage credentials and provide automatic token management.

Rationale

Failure to web App Uses Managed Identity may leave the Azure cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
az webapp identity show --resource-group <rg> --name <app> --query 'type'
# Should be "SystemAssigned" or "SystemAssigned, UserAssigned"
Remediation
az webapp identity assign --resource-group <rg> --name <app>