Virtual Machines


The presented resiliency recommendations in this guidance include Virtual Machines and dependent resources and settings.

Summary of Recommendations

Recommendations Details

VM-1 - Run production workloads on two or more VMs using VMSS Flex

Category: Availability

Impact: High

Guidance

Production VM workloads should be deployed on multiple VMs and grouped together in a VMSS Flex instance. VMSS Flex intelligently distributes VMs across the platform to minimize the impact of platform faults and platform updates on a workload. A workload running on single instance VMs, even when those instances are spread across availability zones, cannot receive the same protection because the platform has no way of knowing the VMs are related to each other.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VMs that are not associated with a VMSS Flex instance
resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| where isnull(properties.virtualMachineScaleSet.id)
| project recommendationId="vm-1", name, id, tags



VM-2 - Deploy VMs across Availability Zones

Category: Availability

Impact: High

Guidance

Azure Availability Zones are physically separate locations within each Azure region that are tolerant to local failures. Use availability zones to protect your applications and data against unlikely datacenter failures.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VMs that are not assigned to a Zone
Resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| where isnull(zones)
| project recommendationId="vm-2", name, id, tags, param1="No Zone"



VM-3 - Migrate VMs using availability sets to VMSS Flex

Category: Availability

Impact: High

Guidance

Availability sets will be retired in the near future. Modernize your workloads by migrating them from VMs to VMSS Flex. With VMSS Flex, you can deploy your VMs in one of two ways:

. Across zones . In the same zone, but across fault domains (FDs) and update domains (UD) automatically.

In an N-tier application, it’s recommended that you place each application tier into its own VMSS Flex.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VMs using Availability Sets
resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| where isnotnull(properties.availabilitySet)
| project recommendationId = "vm-3", name, id, tags, param1=strcat("availabilitySet: ",properties.availabilitySet.id)



VM-4 - Replicate VMs using Azure Site Recovery

Category: Disaster Recovery

Impact: Medium

Guidance

When you replicate Azure VMs using Site Recovery, all the VM disks are continuously replicated to the target region asynchronously. The recovery points are created every few minutes. This gives you a Recovery Point Objective (RPO) in the order of minutes. You can conduct disaster recovery drills as many times as you want, without affecting the production application or the ongoing replication.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VMs that do NOT have replication with ASR enabled
// Run query to see results.
resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| project name, id, tags
| join kind=leftouter (
    recoveryservicesresources
    | where type =~ 'Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectedItems'
    | where properties.providerSpecificDetails.dataSourceInfo.datasourceType =~ 'AzureVm'
    | project id=properties.providerSpecificDetails.dataSourceInfo.resourceId
    | extend name=strcat_array(array_slice(split(id, '/'), 8, -1), '/')
) on name
| where isnull(id1)
| project-away id1
| project-away name1
| project recommendationId = "vm-4", name, id, tags
| order by id asc



VM-5 - Use Managed Disks for VM disks

Category: Availability

Impact: High

Guidance

Azure unmanaged disks will be fully retired on September 30, 2025. If you use unmanaged disks, start planning the migration now.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VMs that are not using Managed Disks
Resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| where isnull(properties.storageProfile.osDisk.managedDisk)
| project recommendationId = "vm-5", name, id, tags



VM-6 - Host database data on a data disk

Category: System Efficiency

Impact: Low

Guidance

Host database data on a data disk instead of OS disk. A data disk is a managed disk that is attached to a virtual machine to store data you need to keep. Data disks are registered as SCSI drives and are labeled with a letter that you choose. Hosting your data on a data disk helps with flexibility when backuping or restoring data, as well as migrating the disk without having to migrate the entire Virtual Machine and Operating System. You will be able to select a different disk sku, with different type, size, and performance that meet your requirements.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VMs that only have OS Disk
Resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| where array_length(properties.storageProfile.dataDisks) < 1
| project recommendationId = "vm-6", name, id, tags



VM-7 - Backup VMs with Azure Backup service

Category: Disaster Recovery

Impact: Medium

Guidance

Enable backups for your virtual machines to secure and quickly recover your data. The Azure Backup service provides simple, secure, and cost-effective solutions to back up your data and recover it from the Microsoft Azure cloud.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VMs that do NOT have Backup enabled
// Run query to see results.
resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| project name, id, tags
| join kind=leftouter (
    recoveryservicesresources
    | where type =~ 'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems'
    | where properties.dataSourceInfo.datasourceType =~ 'Microsoft.Compute/virtualMachines'
    | project idBackupEnabled=properties.sourceResourceId
    | extend name=strcat_array(array_slice(split(idBackupEnabled, '/'), 8, -1), '/')
) on name
| where isnull(idBackupEnabled)
| project-away idBackupEnabled
| project-away name1
| project recommendationId = "vm-7", name, id, tags
| order by id asc



VM-8 - Production VMs should be using SSD disks

Category: System Efficiency

Impact: High

Guidance

Premium SSD disks offer high-performance, low-latency disk support for I/O-intensive applications and production workloads. Standard SSD Disks are a cost-effective storage option optimized for workloads that need consistent performance at lower IOPS levels.

It is recommended that you:

  • Use Standard HDD disks for Dev/Test scenarios and less critical workloads at lowest cost.
  • Use Premium SSD disks instead of Standard HDD disks with your premium-capable VMs. For any Single Instance VM using premium storage for all Operating System Disks and Data Disks, Azure guarantees VM connectivity of at least 99.9%.

If you want to upgrade from Standard HDD to Premium SSD disks, consider the following issues:

  • Upgrading requires a VM reboot and this process takes 3-5 minutes to complete.
  • If VMs are mission-critical production VMs, evaluate the improved availability against the cost of premium disks.

This does not apply to ephemeral disks Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all disks with StandardHDD sku attached to VMs
Resources
| where type =~ 'Microsoft.Compute/disks'
| where sku.name == 'Standard_LRS' and sku.tier == 'Standard'
| where managedBy != ""
| project recommendationId = "vm-8", name, id, tags, param1=strcat("managedBy: ", managedBy)



VM-9 - Review VMs in stopped state

Category: Governance

Impact: Low

Guidance

Azure Virtual Machines (VM) instances go through different states. There are provisioning and power states. If a Virtual Machine is not running that indicates the Virtual Machine might facing an issue or is no longer necessary and could be removed helping to reduce costs.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VMs that are NOT running
Resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| where properties.extended.instanceView.powerState.displayStatus != 'VM running'
| project recommendationId = "vm-9", name, id, tags



VM-10 - Enable Accelerated Networking (AccelNet)

Category: System Efficiency

Impact: Medium

Guidance

Accelerated networking enables single root I/O virtualization (SR-IOV) to a VM, greatly improving its networking performance. This high-performance path bypasses the host from the data path, which reduces latency, jitter, and CPU utilization for the most demanding network workloads on supported VM types.

This configuration is not always required, evaluate this option according to the workload requirements.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VM NICs that do not have Accelerated Networking enabled
resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| mv-expand nic = properties.networkProfile.networkInterfaces
| project name, id, tags, lowerCaseNicId = tolower(nic.id), vmSize = tostring(properties.hardwareProfile.vmSize)
| join kind = inner (
    resources
    | where type =~ 'Microsoft.Network/networkInterfaces'
    | where properties.enableAcceleratedNetworking == false
    | project nicName = split(id, "/")[8], lowerCaseNicId = tolower(id)
    )
    on lowerCaseNicId
| summarize nicNames = make_set(nicName) by name, id, tostring(tags), vmSize
| extend param1 = strcat("NicName: ", strcat_array(nicNames, ", ")), param2 = strcat("VMSize: ", vmSize)
| project recommendationId = "vm-10", name, id, tags, param1, param2
| order by id asc



VM-11 - When AccelNet is enabled, you must manually update the GuestOS NIC driver

Category: Governance

Impact: Low

Guidance

When Accelerated Networking is enabled the default Azure Virtual Network interface in the GuestOS is replaced for a Mellanox and consecutively its driver is provided from a 3rd party vendor. Marketplace images maintained by Microsoft are offered with the latest version of Mellanox drivers, however, once the Virtual Machine is deployed, the customer is responsible for maintaining the driver up to date.

Resources

Resource Graph Query

// cannot-be-validated-with-arg



VM-12 - VMs should not have a Public IP directly associated

Category: Access & Security

Impact: Medium

Guidance

If a Virtual Machine requires outbound internet connectivity we recommend the use of NAT Gateway or Azure Firewall, this will help to increase security and resiliency of the service as both services have much higher availability and SNAT ports. For inbound internet connectivity we recommend using a load balancing solution such as Azure Load Balancer and Application Gateway.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VMs with PublicIPs directly associated with them
Resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| where isnotnull(properties.networkProfile.networkInterfaces)
| mv-expand nic=properties.networkProfile.networkInterfaces
| project name, id, tags, nicId = nic.id
| extend nicId = tostring(nicId)
| join kind=inner (
    Resources
    | where type =~ 'Microsoft.Network/networkInterfaces'
    | where isnotnull(properties.ipConfigurations)
    | mv-expand ipconfig=properties.ipConfigurations
    | extend publicIp = tostring(ipconfig.properties.publicIPAddress.id)
    | where publicIp != ""
    | project name, nicId = tostring(id), publicIp
) on nicId
| project recommendationId = "vm-12", name, id, tags
| order by id asc



VM-13 - VM network interfaces and associated subnets both have a Network Security Group (NSG) associated

Category: Access & Security

Impact: Low

Guidance

Unless you have a specific reason to, we recommend that you associate a network security group to a subnet, or a network interface, but not both. Since rules in a network security group associated to a subnet can conflict with rules in a network security group associated to a network interface, you can have unexpected communication problems that require troubleshooting.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Provides a list of virtual machines and associated NICs that do have an NSG associated to them and also an NSG associated to the subnet.
Resources
| where type =~ 'Microsoft.Network/networkInterfaces'
| where isnotnull(properties.networkSecurityGroup)
| mv-expand ipConfigurations = properties.ipConfigurations, nsg = properties.networkSecurityGroup
| project nicId = tostring(id), subnetId = tostring(ipConfigurations.properties.subnet.id), nsgName=split(nsg.id, '/')[8]
| parse kind=regex subnetId with '/virtualNetworks/' virtualNetwork '/subnets/' subnet
    | join kind=inner (
        Resources
        | where type =~ 'Microsoft.Network/NetworkSecurityGroups' and isnotnull(properties.subnets)
        | project name, resourceGroup, subnet=properties.subnets
        | mv-expand subnet
        | project subnetId=tostring(subnet.id)
    ) on subnetId
    | project nicId
| join kind=leftouter (
    Resources
    | where type =~ 'Microsoft.Compute/virtualMachines'
    | where isnotnull(properties.networkProfile.networkInterfaces)
    | mv-expand nic=properties.networkProfile.networkInterfaces
    | project vmName = name, vmId = id, tags, nicId = nic.id, nicName=split(nic.id, '/')[8]
    | extend nicId = tostring(nicId)
) on nicId
| project recommendationId = "vm-13", name=vmName, id = vmId, tags, param1 = strcat("nic-name=", nicName)



VM-14 - IP Forwarding should only be enabled for Network Virtual Appliances

Category: Access & Security

Impact: Medium

Guidance

IP forwarding enables the virtual machine network interface to:

Receive network traffic not destined for one of the IP addresses assigned to any of the IP configurations assigned to the network interface.

Send network traffic with a different source IP address than the one assigned to one of a network interface’s IP configurations.

The setting must be enabled for every network interface that is attached to the virtual machine that receives traffic that the virtual machine needs to forward. A virtual machine can forward traffic whether it has multiple network interfaces or a single network interface attached to it. While IP forwarding is an Azure setting, the virtual machine must also run an application able to forward the traffic, such as firewall, WAN optimization, and load balancing applications.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VM NICs that have IPForwarding enabled. This feature is usually only required for Network Virtual Appliances
Resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| where isnotnull(properties.networkProfile.networkInterfaces)
| mv-expand nic=properties.networkProfile.networkInterfaces
| project name, id, tags, nicId = nic.id
| extend nicId = tostring(nicId)
| join kind=inner (
    Resources
    | where type =~ 'Microsoft.Network/networkInterfaces'
    | where properties.enableIPForwarding == true
    | project nicId = tostring(id)
) on nicId
| project recommendationId = "vm-14", name, id, tags
| order by id asc



VM-15 - Customer DNS Servers should be configured in the Virtual Network level

Category: Storage

Impact: Low

Guidance

Configure the DNS Server in the Virtual Network to avoid inconsistency across the environment.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VM NICs that have DNS Server settings configured in any of the NICs
Resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| where isnotnull(properties.networkProfile.networkInterfaces)
| mv-expand nic=properties.networkProfile.networkInterfaces
| project name, id, tags, nicId = nic.id
| extend nicId = tostring(nicId)
| join kind=inner (
    Resources
    | where type =~ 'Microsoft.Network/networkInterfaces'
    | project name, id, dnsServers = properties.dnsSettings.dnsServers
    | extend hasDns = array_length(dnsServers) >= 1
    | where hasDns != 0
    | project name, nicId = tostring(id)
) on nicId
| project recommendationId = "vm-15", name, id, tags
| order by id asc



VM-16 - Shared disks should only be enabled in clustered servers

Category: Storage

Impact: Medium

Guidance

Azure shared disks is a feature for Azure managed disks that enables you to attach a managed disk to multiple virtual machines (VMs) simultaneously. Attaching a managed disk to multiple VMs allows you to either deploy new or migrate existing clustered applications to Azure, and should only be used in those situations where the disk will be assigned to more than one Virtual Machine member of a Cluster.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all Disks configured to be Shared. This is not an indication of an issue, but if a disk with this configuration is assigned to two or more VMs without a proper disk control mechanism (like a WSFC) it can lead to data loss
resources
| where type =~ 'Microsoft.Compute/disks'
| where isnotnull(properties.maxShares)
| project id, name, tags, lowerCaseDiskId = tolower(id), diskState = tostring(properties.diskState)
| join kind = leftouter (
    resources
    | where type =~ 'Microsoft.Compute/virtualMachines'
    | project osDiskVmName = name, lowerCaseOsDiskId = tolower(properties.storageProfile.osDisk.managedDisk.id)
    | join kind = fullouter (
        resources
        | where type =~ 'Microsoft.Compute/virtualMachines'
        | mv-expand dataDisks = properties.storageProfile.dataDisks
        | project dataDiskVmName = name, lowerCaseDataDiskId = tolower(dataDisks.managedDisk.id)
        )
        on $left.lowerCaseOsDiskId == $right.lowerCaseDataDiskId
    | project lowerCaseDiskId = coalesce(lowerCaseOsDiskId, lowerCaseDataDiskId), vmName = coalesce(osDiskVmName, dataDiskVmName)
    )
    on lowerCaseDiskId
| summarize vmNames = make_set(vmName) by name, id, tostring(tags), diskState
| extend param1 = strcat("DiskState: ", diskState), param2 = iif(isempty(vmNames[0]), "VMName: n/a", strcat("VMName: ", strcat_array(vmNames, ", ")))
| project recommendationId = "vm-16", name, id, tags, param1, param2
| order by id asc



VM-17 - Network access to the VM disk should be set to Disable public access and enable private access

Category: Access & Security

Impact: Low

Guidance

Recommended changing to “Disable public access and enable private access” and creating a Private Endpoint.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all Disks with "Enable public access from all networks" enabled
resources
| where type =~ 'Microsoft.Compute/disks'
| where properties.publicNetworkAccess == "Enabled"
| project id, name, tags, lowerCaseDiskId = tolower(id)
| join kind = leftouter (
    resources
    | where type =~ 'Microsoft.Compute/virtualMachines'
    | project osDiskVmName = name, lowerCaseOsDiskId = tolower(properties.storageProfile.osDisk.managedDisk.id)
    | join kind = fullouter (
        resources
        | where type =~ 'Microsoft.Compute/virtualMachines'
        | mv-expand dataDisks = properties.storageProfile.dataDisks
        | project dataDiskVmName = name, lowerCaseDataDiskId = tolower(dataDisks.managedDisk.id)
        )
        on $left.lowerCaseOsDiskId == $right.lowerCaseDataDiskId
    | project lowerCaseDiskId = coalesce(lowerCaseOsDiskId, lowerCaseDataDiskId), vmName = coalesce(osDiskVmName, dataDiskVmName)
    )
    on lowerCaseDiskId
| summarize vmNames = make_set(vmName) by name, id, tostring(tags)
| extend param1 = iif(isempty(vmNames[0]), "VMName: n/a", strcat("VMName: ", strcat_array(vmNames, ", ")))
| project recommendationId = "vm-17", name, id, tags, param1
| order by id asc



VM-18 - Ensure that your VMs are compliant with Azure Policies

Category: Governance

Impact: Low

Guidance

It’s important to keep your virtual machine (VM) secure for the applications that you run. Securing your VMs can include one or more Azure services and features that cover secure access to your VMs and secure storage of your data. This article provides information that enables you to keep your VM and applications secure.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VMs in "NonCompliant" state with Azure Policies
PolicyResources
| where type =~ "Microsoft.PolicyInsights/policyStates" and properties.resourceType =~ "Microsoft.Compute/virtualMachines" and properties.complianceState =~ "NonCompliant"
| project
    policyAssignmentName = properties.policyAssignmentName,
    policyDefinitionName = properties.policyDefinitionName,
    lowerCasePolicyDefinitionIdOfPolicyState = tolower(properties.policyDefinitionId),
    lowerCaseVmIdOfPolicyState = tolower(properties.resourceId)
| join kind = leftouter (
    PolicyResources
    | where type =~ "Microsoft.Authorization/policyDefinitions"
    | project lowerCasePolicyDefinitionId = tolower(id), policyDefinitionDisplayName = properties.displayName
    )
    on $left.lowerCasePolicyDefinitionIdOfPolicyState == $right.lowerCasePolicyDefinitionId
| project policyAssignmentName, policyDefinitionName, policyDefinitionDisplayName, lowerCaseVmIdOfPolicyState
| join kind = leftouter (
    Resources
    | where type =~ "Microsoft.Compute/virtualMachines"
    | project vmName = name, vmId = id, vmTags = tags, lowerCaseVmId = tolower(id)
    )
    on $left.lowerCaseVmIdOfPolicyState == $right.lowerCaseVmId
| extend
    param1 = strcat("AssignmentName: ", policyAssignmentName),
    param2 = strcat("DefinitionName: ", policyDefinitionDisplayName),  // Align to Azure portal's term.
    param3 = strcat("DefinitionID: ", policyDefinitionName)            // Align to Azure portal's term.
| project recommendationId = "vm-18", name = vmName, id = vmId, tags = vmTags, param1, param2, param3



VM-19 - Enable advanced encryption options for your managed disks

Category: Access & Security

Impact: Medium

Guidance

Azure Disk Storage Server-Side Encryption (also referred to as encryption-at-rest or Azure Storage encryption) automatically encrypts data stored on Azure managed disks (OS and data disks) when persisting on the Storage Clusters. There are several types of advanced encryption options available for your managed disks, including Azure Disk Encryption (ADE), Encryption at host and Confidential disk encryption.

  • ADE encrypts the disks of Azure virtual machines (VMs) inside your VMs by using the DM-Crypt feature of Linux or the BitLocker feature of Windows.
  • Encryption at host ensures that data stored on the VM host hosting your VM is encrypted at rest and flows encrypted to the Storage clusters.
  • Confidential disk encryption binds disk encryption keys to the virtual machine’s TPM and makes the protected disk content accessible only to the VM.

Resources

Resource Graph Query

// under-development



VM-20 - Enable VM Insights

Category: Monitoring

Impact: Low

Guidance

VM insights monitors the performance and health of your virtual machines and virtual machine scale sets. It monitors their running processes and dependencies on other resources. VM insights can help deliver predictable performance and availability of vital applications by identifying performance bottlenecks and network issues. It can also help you understand whether an issue is related to other dependencies.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Check for VMs without Azure Monitoring Agent extension installed, missing Data Collection Rule or Data Collection Rule without performance enabled.
Resources
| where type == 'microsoft.compute/virtualmachines'
| project idVm = tolower(id), name, tags
| join kind=leftouter (
    InsightsResources
    | where type =~ "Microsoft.Insights/dataCollectionRuleAssociations" and id has "Microsoft.Compute/virtualMachines"
    | project idDcr = tolower(properties.dataCollectionRuleId), idVmDcr = tolower(substring(id, 0, indexof(id, "/providers/Microsoft.Insights/dataCollectionRuleAssociations/"))))
on $left.idVm == $right.idVmDcr
| join kind=leftouter (
    Resources
    | where type =~ "Microsoft.Insights/dataCollectionRules"
    | extend
        isPerformanceEnabled = iif(properties.dataSources.performanceCounters contains "Microsoft-InsightsMetrics" and properties.dataFlows contains "Microsoft-InsightsMetrics", true, false),
        isMapEnabled = iif(properties.dataSources.extensions contains "Microsoft-ServiceMap" and properties.dataSources.extensions contains "DependencyAgent" and properties.dataFlows contains "Microsoft-ServiceMap", true, false)//,
    | where isPerformanceEnabled or isMapEnabled
    | project dcrName = name, isPerformanceEnabled, isMapEnabled, idDcr = tolower(id))
on $left.idDcr == $right.idDcr
| join kind=leftouter (
    Resources
        | where type == 'microsoft.compute/virtualmachines/extensions' and (name contains 'AzureMonitorWindowsAgent' or name contains 'AzureMonitorLinuxAgent')
        | extend idVmExtension = tolower(substring(id, 0, indexof(id, '/extensions'))), extensionName = name)
on $left.idVm == $right.idVmExtension
| where isPerformanceEnabled != 1 or (extensionName != 'AzureMonitorWindowsAgent' and extensionName != 'AzureMonitorLinuxAgent')
| project recommendationId = "vm-20", name, id = idVm, tags, param1 = strcat('MonitoringExtension:', extensionName), param2 = strcat('DataCollectionRuleId:', idDcr), param3 = strcat('isPerformanceEnabled:', isPerformanceEnabled)



VM-21 - Configure diagnostic settings for all Azure Virtual Machines

Category: Monitoring

Impact: Low

Guidance

Platform metrics are sent automatically to Azure Monitor Metrics by default and without configuration. Platform logs provide detailed diagnostic and auditing information for Azure resources and the Azure platform they depend on:

  • Resource logs aren’t collected until they’re routed to a destination.
  • Activity logs exist on their own but can be routed to other locations.

Each Azure resource requires its own diagnostic setting, which defines the following criteria:

  • Sources: The type of metric and log data to send to the destinations defined in the setting. The available types vary by resource type.
  • Destinations: One or more destinations to send to.

A single diagnostic setting can define no more than one of each of the destinations. If you want to send data to more than one of a particular destination type (for example, two different Log Analytics workspaces), create multiple settings. Each resource can have up to five diagnostic settings.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all Virtual Machines without diagnostic settings enabled/with diagnostic settings enabled but not configured both performance counters and event logs/syslogs.
resources
| where type =~ "microsoft.compute/virtualmachines"
| project name, id, tags, lowerCaseVmId = tolower(id)
| join kind = leftouter (
    resources
    | where type =~ "Microsoft.Compute/virtualMachines/extensions" and properties.publisher =~ "Microsoft.Azure.Diagnostics"
    | project
        lowerCaseVmIdOfExtension = tolower(substring(id, 0, indexof(id, "/extensions/"))),
        extensionType = properties.type,
        provisioningState = properties.provisioningState,
        storageAccount = properties.settings.StorageAccount,
        // Windows
        wadPerfCounters = properties.settings.WadCfg.DiagnosticMonitorConfiguration.PerformanceCounters.PerformanceCounterConfiguration,
        wadEventLogs = properties.settings.WadCfg.DiagnosticMonitorConfiguration.WindowsEventLog,
        // Linux
        ladPerfCounters = properties.settings.ladCfg.diagnosticMonitorConfiguration.performanceCounters.performanceCounterConfiguration,
        ladSyslog = properties.settings.ladCfg.diagnosticMonitorConfiguration.syslogEvents
    | extend
        // Windows
        isWadPerfCountersConfigured = iif(array_length(wadPerfCounters) > 0, true, false),
        isWadEventLogsConfigured = iif(isnotnull(wadEventLogs) and array_length(wadEventLogs.DataSource) > 0, true, false),
        // Linux
        isLadPerfCountersConfigured = iif(array_length(ladPerfCounters) > 0, true, false),
        isLadSyslogConfigured = isnotnull(ladSyslog)
    | project
        lowerCaseVmIdOfExtension,
        extensionType,
        provisioningState,
        storageAccount,
        isPerfCountersConfigured = case(extensionType =~ "IaaSDiagnostics", isWadPerfCountersConfigured, extensionType =~ "LinuxDiagnostic", isLadPerfCountersConfigured, false),
        isEventLogsConfigured = case(extensionType =~ "IaaSDiagnostics", isWadEventLogsConfigured, extensionType =~ "LinuxDiagnostic", isLadSyslogConfigured, false)
    )
    on $left.lowerCaseVmId == $right.lowerCaseVmIdOfExtension
| where isempty(lowerCaseVmIdOfExtension) or provisioningState !~ "Succeeded" or not(isPerfCountersConfigured and isEventLogsConfigured)
| extend
    param1 = strcat("DiagnosticSetting: ", iif(isnotnull(extensionType), strcat("Enabled, partially configured (", extensionType, ")"), "Not enabled")),
    param2 = strcat("ProvisioningState: ", iif(isnotnull(provisioningState), provisioningState, "n/a")),
    param3 = strcat("storageAccount: ", iif(isnotnull(storageAccount), storageAccount, "n/a")),
    param4 = strcat("PerformanceCounters: ", case(isnull(isPerfCountersConfigured), "n/a", isPerfCountersConfigured, "Configured", "Not configured")),
    param5 = strcat("EventLogs/Syslogs: ", case(isnull(isEventLogsConfigured), "n/a", isEventLogsConfigured, "Configured", "Not configured"))
| project recommendationId = "vm-21", name, id, tags, param1, param2, param3, param4, param5



VM-22 - Use maintenance configurations for the VMs

Category: Governance

Impact: High

Guidance

The maintenance configuration settings allows user to schedule and manage updates, ensuring the VM updates/interruptions are done in planned timeframe.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find VMS that do not have maintenance configuration assigned
Resources
| extend resourceId = tolower(id)
| project name, location, type, id, tags, resourceId, properties
| where type =~ 'Microsoft.Compute/virtualMachines'
| join kind=leftouter (
maintenanceresources
| where type =~ "microsoft.maintenance/configurationassignments"
| project planName = name, type, maintenanceProps = properties
| extend resourceId = tostring(maintenanceProps.resourceId)
) on resourceId
| where isnull(maintenanceProps)
| project recommendationId = "vm-22",name, id, tags
| order by id asc



VM-23 - Avoid using A or B-Series VM Sku for production VMs that need the full performance of the CPU continuously

Category: System Efficiency

Impact: High

Guidance

A-series VMs have CPU performance and memory configurations best suited for entry level workloads like development and test. Some example use cases include development and test servers, low traffic web servers, small to medium databases, proof-of-concepts, and code repositories.

B-series VMs are ideal for workloads that do not need the full performance of the CPU continuously, like web servers, proof of concepts, small databases and development build environments. These workloads typically have burstable performance requirements. To determine the physical hardware on which this size is deployed, query the virtual hardware from within the virtual machine. The B-series provides you with the ability to purchase a VM size with baseline performance that can build up credits when it is using less than its baseline. When the VM has accumulated credits, the VM can burst above the baseline using up to 100% of the vCPU when your application requires higher CPU performance. Upon consuming all the CPU credits, a B-series virtual machine is throttled back to its base CPU performance until it accumulates the credits to CPU burst again.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VMs using A or B series families
resources
| where type == 'microsoft.compute/virtualmachines'
| where properties.hardwareProfile.vmSize contains "Standard_B" or properties.hardwareProfile.vmSize contains "Standard_A"
| project recommendationId = "vm-23", name, id, tags, param1=strcat("vmSku: " , properties.hardwareProfile.vmSize)



VM-24 - Mission Critical Workloads should be using Premium or Ultra Disks

Category: System Efficiency

Impact: High

Guidance

Azure Premium SSDs deliver high-performance and low-latency disk support for virtual machines (VMs) with input/output (IO)-intensive workloads.

Premium SSD v2 offers higher performance than Premium SSDs while also generally being less costly. You can individually tweak the performance (capacity, throughput, and IOPS) of Premium SSD v2 disks at any time, allowing workloads to be cost efficient while meeting shifting performance needs. You should use Premium solid-state drives (SSDs) as operating system (OS) disks as the V2 is not supported as OS Disk.

Azure ultra disks are the highest-performing storage option for Azure virtual machines (VMs). You can change the performance parameters of an ultra disk without having to restart your VMs. Ultra disks are suited for data-intensive workloads such as SAP HANA, top-tier databases, and transaction-heavy workloads. Ultra disks must be used as data disks and can only be created as empty disks. You should use Premium solid-state drives (SSDs) as operating system (OS) disks.

Resources

Resource Graph Query

// Azure Resource Graph Query
// Find all VMs that have an attached disk that is not in the Premium or Ultra sku tier.

resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| extend lname = tolower(name)
| join kind=leftouter(resources
    | where type =~ 'Microsoft.Compute/disks'
    | where not(sku.tier =~ 'Premium') and not(sku.tier =~ 'Ultra')
    | extend lname = tolower(tostring(split(managedBy, '/')[8]))
    | project lname, name
    | summarize disks = make_list(name) by lname) on lname
| where isnotnull(disks)
| project recommendationId = "vm-24", name, id, tags, param1=strcat("AffectedDisks: ", disks)



VM-27 - Use Azure Boost VMs for Maintenance sensitive workload

Category: Availability

Impact: Medium

Guidance

If the workload is Maintenance sensitive, please consider using Azure Boost compatible VMs. Azure Boost is designed to lessen the impact on customers when Azure maintenance activities occur.

Resources

Resource Graph Query

// under-development



VM-28 - Enable Scheduled Events for Maintenance sensitive workload VMs

Category: Availability

Impact: Medium

Guidance

If the workload is Maintenance sensitive, please enable Scheduled Events. Scheduled Events is an Azure Metadata Service that gives your application time to prepare for virtual machine maintenance. It provides information about upcoming maintenance events (for example, reboot) so that your application can prepare for them and limit disruption. It’s available for all Azure Virtual Machines types, including PaaS and IaaS on both Windows and Linux.

Resources

Resource Graph Query

// under-development