diff --git a/coverage.md b/coverage.md
new file mode 100644
index 0000000..99048ed
--- /dev/null
+++ b/coverage.md
@@ -0,0 +1,8 @@
+# Code Coverage Report - Scope.psm1
+| Metric | Value |
+|-----------------|-------------|
+| Result | ✅ Passed |
+| Passed Count | ✅ 3 |
+| Failed Count | ✅ 0 |
+| Coverage (%) | ❌ 42.4242424242424 |
+| Target Coverage (%) | 75 |
diff --git a/coverage.xml b/coverage.xml
new file mode 100644
index 0000000..bfd6129
--- /dev/null
+++ b/coverage.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/WARA/collector/collector.psm1 b/src/modules/WARA/collector/collector.psm1
index 139597f..84942fc 100644
--- a/src/modules/WARA/collector/collector.psm1
+++ b/src/modules/WARA/collector/collector.psm1
@@ -1,2 +1,158 @@
+Function Get-WAFAllAzGraphResource {
+ param (
+ [string[]]$subscriptionIds,
+ [string]$query = 'Resources | project id, resourceGroup, subscriptionId, name, type, location'
+ )
+ if ($Debugging) {
+ Write-Host
+ Write-Host "[-Debugging]: Running resource graph query..." -ForegroundColor Magenta
+ Write-Host
+ Write-Host "$query" -ForegroundColor Magenta
+ Write-Host
+ }
+ $result = $subscriptionIds ? (Search-AzGraph -Query $query -first 1000 -Subscription $subscriptionIds) : (Search-AzGraph -Query $query -first 1000 -usetenantscope) # -first 1000 returns the first 1000 results and subsequently reduces the amount of queries required to get data.
+
+ # Collection to store all resources
+ $allResources = @($result)
+
+ # Loop to paginate through the results using the skip token
+ $result = while ($result.SkipToken) {
+ # Retrieve the next set of results using the skip token
+ $result = $subscriptionId ? (Search-AzGraph -Query $query -SkipToken $result.SkipToken -Subscription $subscriptionIds -First 1000) : (Search-AzGraph -query $query -SkipToken $result.SkipToken -First 1000 -UseTenantScope)
+ # Add the results to the collection
+ write-output $result
+ }
+
+ $allResources += $result
+
+ # Output all resources
+ return $allResources
+ }
+
+ function Get-WAFResourceGroup {
+ param (
+ [string[]]$SubscriptionIds
+ )
+
+ # Query to get all resource groups in the tenant
+ $q = "resourcecontainers
+ | where type == 'microsoft.resources/subscriptions'
+ | project subscriptionId, subscriptionName = name
+ | join (resourcecontainers
+ | where type == 'microsoft.resources/subscriptions/resourcegroups')
+ on subscriptionId
+ | project subscriptionName, subscriptionId, resourceGroup, id=tolower(id)"
+
+ $r = $SubscriptionIds ? (Get-WAFAllAzGraphResource -query $q -subscriptionIds $SubscriptionIds -usetenantscope) : (Get-WAFAllAzGraphResource -query $q -usetenantscope)
+
+ # Returns the resource groups
+ return $r
+ }
+
+#This function grabs all resources inside of resource groups with matching tags.
+
+#This function grabs all resources that have matching tags and returns them.
+Function Get-WAFTaggedResources {
+ param(
+ [String[]]$tagArray,
+ [String[]]$SubscriptionIds
+ )
+
+ $queryTemplate = "| where (tags[''] =~ '')"
+
+ $queryobj = @()
+ foreach($tag in $tagArray){
+ $tagName, $tagValue = $tag.Split('==').Trim()
+ $queryobj += $queryTemplate -replace "", $tagName -replace "", $tagValue
+ }
+
+ $queryobj = $queryobj -join "`r`n"
+
+$q = "resources
+
+| project id, name, type, location, resourceGroup, subscriptionId" -replace "", $queryobj
+
+
+ Write-host The Query is: `r`n $q
+
+$r = $SubscriptionIds ? (Get-WAFAllAzGraphResource -query $q -subscriptionIds $SubscriptionIds) : (Get-WAFAllAzGraphResource -query $q -usetenantscope)
+return $r
+}
+#This function grabs all resources that have matching tags and returns them.
+Function Get-WAFTaggedRGResources {
+ param(
+ [String[]]$tagKeys,
+ [String[]]$tagValues,
+ [String[]]$SubscriptionIds
+ )
+
+ $tagValuesString = "'" + ($tagValues -join "','").toLower() + "'"
+ $tagKeysString = "'" + ($tagKeys -join "','").toLower() + "'"
+
+$q = "Resources
+ | mv-expand bagexpansion=array tags
+ | where isnotempty(tags)
+ | where tolower(tags[0]) in ($tagValuesString) // Specify your tag names here
+ | where tolower(tags[1]) in ($tagKeysString) // Specify your tag values here
+ | project name,id,type,resourceGroup,location,subscriptionId"
+
+ Write-host The Query is $q
+
+$r = $SubscriptionIds ? (Get-WAFAllAzGraphResource -query $q -subscriptionIds $SubscriptionIds) : (Get-WAFAllAzGraphResource -query $q -usetenantscope)
+return $r
+}
+
+Function Invoke-WAFQueryLoop {
+ param(
+ $RecommendationObject,
+ [string[]]$subscriptionIds
+ )
+
+ $Types = Get-WAFResourceType -SubscriptionIds $subscriptionIds
+
+ $QueryObject = Get-WAFQueryByResourceType -ObjectList $RecommendationObject -FilterList $Types.type -KeyColumn "recommendationResourceType"
+
+ $return = $QueryObject | Where-Object{$_.automationavailable -eq "arg"} | ForEach-Object {
+ Write-Progress -Activity "Running Queries" -Status "Running Query for $($_.recommendationResourceType)" -PercentComplete (($QueryObject.IndexOf($_) / $QueryObject.Count) * 100)
+ Get-WAFAllAzGraphResource -query $_.query -subscriptionIds $subscriptionIds
+ }
+
+ return $return
+}
+
+Function Get-WAFResourceType {
+ param(
+ [String[]]$SubscriptionIds
+ )
+
+ $q = "Resources
+ | summarize count() by type
+ | project type"
+
+ $r = $SubscriptionIds ? (Get-WAFAllAzGraphResource -query $q -subscriptionIds $SubscriptionIds) : (Get-WAFAllAzGraphResource -query $q -usetenantscope)
+
+ return $r
+}
+
+function Get-WAFQueryByResourceType {
+ param (
+ [Parameter(Mandatory = $true)]
+ [array]$ObjectList,
+
+ [Parameter(Mandatory = $true)]
+ [array]$FilterList,
+
+ [Parameter(Mandatory = $true)]
+ [string]$KeyColumn
+ )
+
+ $matchingObjects = foreach ($obj in $ObjectList) {
+ if ($obj.$KeyColumn -in $FilterList) {
+ $obj
+ }
+ }
+
+ return $matchingObjects
+}
diff --git a/src/modules/WARA/runbook/runbook.psm1 b/src/modules/WARA/runbook/runbook.psm1
index e69de29..3501cf0 100644
--- a/src/modules/WARA/runbook/runbook.psm1
+++ b/src/modules/WARA/runbook/runbook.psm1
@@ -0,0 +1,57 @@
+<#
+.SYNOPSIS
+WARA Runbook module
+
+.DESCRIPTION
+Enables developers to consume WARA runbook files
+#>
+
+function Read-Runbook {
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$RunbookPath
+ )
+
+ # First, check to make sure the runbook actually exists...
+ if (!(Test-Path $RunbookPath -PathType Leaf)) {
+
+ # If not, fail early.
+ Write-Error "[-RunbookPath]: No runbook found at [$RunbookPath]."
+ $null
+
+ } else {
+
+ # If so, let's read this runbook!
+ $runbook = @{
+ Parameters = @{}
+ Selectors = @{}
+ Checks = @{}
+ QueryOverrides = @()
+ }
+
+ # Read the runbook JSON
+ $runbookJson = Get-Content -Raw $RunbookPath | ConvertFrom-Json
+
+ # Try to load parameters
+ $runbookJson.parameters.PSObject.Properties | ForEach-Object {
+ $runbook.Parameters[$_.Name] = $_.Value
+ }
+
+ # Try to load selectors
+ $runbookJson.selectors.PSObject.Properties | ForEach-Object {
+ $runbook.Selectors[$_.Name.ToLower()] = $_.Value
+ }
+
+ # Try to load checks
+ $runbookJson.checks.PSObject.Properties | ForEach-Object {
+ $runbook.Checks[$_.Name.ToLower()] = $_.Value
+ }
+
+ # Try to load query overrides
+ $runbookJson.query_overrides | ForEach-Object {
+ $runbook.QueryOverrides += [string]$_
+ }
+
+ return [pscustomobject]$runbook
+ }
+}
diff --git a/src/modules/WARA/scope/Get-WAFFilteredResourceList.md b/src/modules/WARA/scope/Get-WAFFilteredResourceList.md
new file mode 100644
index 0000000..836fcbb
--- /dev/null
+++ b/src/modules/WARA/scope/Get-WAFFilteredResourceList.md
@@ -0,0 +1,105 @@
+---
+external help file: scope-help.xml
+Module Name: scope
+online version:
+schema: 2.0.0
+---
+
+# Get-WAFFilteredResourceList
+
+## SYNOPSIS
+Retrieves a filtered list of Azure resources based on subscription, resource group, and resource filters.
+
+## SYNTAX
+
+```
+Get-WAFFilteredResourceList [[-SubscriptionFilters] ] [[-ResourceGroupFilters] ]
+ [[-ResourceFilters] ] [[-UnfilteredResources] ]
+```
+
+## DESCRIPTION
+The Get-WAFFilteredResourceList function filters Azure resources by combining subscription, resource group, and resource filters.
+It generates a list of implicit subscription IDs from the provided filters, retrieves unfiltered resources, and then applies the filters to return the matching resources.
+
+## EXAMPLES
+
+### EXAMPLE 1
+```
+$subscriptionFilters = @("/subscriptions/12345")
+$resourceGroupFilters = @("/subscriptions/12345/resourceGroups/myResourceGroup")
+$resourceFilters = @("/subscriptions/12345/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM")
+```
+
+$filteredResources = Get-WAFFilteredResourceList -SubscriptionFilters $subscriptionFilters -ResourceGroupFilters $resourceGroupFilters -ResourceFilters $resourceFilters
+
+## PARAMETERS
+
+### -SubscriptionFilters
+An array of subscription identifiers to filter the resources.
+
+```yaml
+Type: String[]
+Parameter Sets: (All)
+Aliases:
+
+Required: False
+Position: 1
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### -ResourceGroupFilters
+An array of resource group identifiers to filter the resources.
+
+```yaml
+Type: String[]
+Parameter Sets: (All)
+Aliases:
+
+Required: False
+Position: 2
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### -ResourceFilters
+An array of resource identifiers to filter the resources.
+
+```yaml
+Type: String[]
+Parameter Sets: (All)
+Aliases:
+
+Required: False
+Position: 3
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### -UnfilteredResources
+{{ Fill UnfilteredResources Description }}
+
+```yaml
+Type: String[]
+Parameter Sets: (All)
+Aliases:
+
+Required: False
+Position: 4
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+## INPUTS
+
+## OUTPUTS
+
+## NOTES
+Author: Your Name
+Date: 2024-08-07
+
+## RELATED LINKS
diff --git a/src/modules/WARA/scope/Get-WAFResourceGroupsByList.md b/src/modules/WARA/scope/Get-WAFResourceGroupsByList.md
new file mode 100644
index 0000000..17a44c5
--- /dev/null
+++ b/src/modules/WARA/scope/Get-WAFResourceGroupsByList.md
@@ -0,0 +1,110 @@
+---
+external help file: scope-help.xml
+Module Name: scope
+online version:
+schema: 2.0.0
+---
+
+# Get-WAFResourceGroupsByList
+
+## SYNOPSIS
+Filters a list of objects based on resource group identifiers.
+
+## SYNTAX
+
+```
+Get-WAFResourceGroupsByList [-ObjectList] [-FilterList] [-KeyColumn]
+ [-ProgressAction ] []
+```
+
+## DESCRIPTION
+The Get-WAFResourceGroupsByList function takes a list of objects and filters them based on the specified resource group identifiers.
+It compares the first five segments of the KeyColumn property of each object with the provided filter list.
+
+## EXAMPLES
+
+### EXAMPLE 1
+```
+$objectList = @(
+ @{ Id = "/subscriptions/12345/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM" },
+ @{ Id = "/subscriptions/12345/resourceGroups/anotherResourceGroup/providers/Microsoft.Compute/virtualMachines/anotherVM" }
+)
+$filterList = @("/subscriptions/12345/resourceGroups/myResourceGroup")
+```
+
+$filteredObjects = Get-WAFResourceGroupsByList -ObjectList $objectList -FilterList $filterList -KeyColumn "Id"
+
+## PARAMETERS
+
+### -ObjectList
+An array of objects to be filtered.
+
+```yaml
+Type: Array
+Parameter Sets: (All)
+Aliases:
+
+Required: True
+Position: 1
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### -FilterList
+An array of resource group identifiers to filter the objects.
+
+```yaml
+Type: Array
+Parameter Sets: (All)
+Aliases:
+
+Required: True
+Position: 2
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### -KeyColumn
+The name of the property in the objects that contains the resource group identifier.
+
+```yaml
+Type: String
+Parameter Sets: (All)
+Aliases:
+
+Required: True
+Position: 3
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### -ProgressAction
+{{ Fill ProgressAction Description }}
+
+```yaml
+Type: ActionPreference
+Parameter Sets: (All)
+Aliases: proga
+
+Required: False
+Position: Named
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### CommonParameters
+This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
+
+## INPUTS
+
+## OUTPUTS
+
+## NOTES
+Author: Kyle Poineal
+Date: 2024-08-07
+
+## RELATED LINKS
diff --git a/src/modules/WARA/scope/Get-WAFResourcesByList.md b/src/modules/WARA/scope/Get-WAFResourcesByList.md
new file mode 100644
index 0000000..7108d71
--- /dev/null
+++ b/src/modules/WARA/scope/Get-WAFResourcesByList.md
@@ -0,0 +1,110 @@
+---
+external help file: scope-help.xml
+Module Name: scope
+online version:
+schema: 2.0.0
+---
+
+# Get-WAFResourcesByList
+
+## SYNOPSIS
+Filters a list of objects based on resource identifiers.
+
+## SYNTAX
+
+```
+Get-WAFResourcesByList [-ObjectList] [-FilterList] [-KeyColumn]
+ [-ProgressAction ] []
+```
+
+## DESCRIPTION
+The Get-WAFResourcesByList function takes a list of objects and filters them based on the specified resource identifiers.
+It compares the KeyColumn property of each object with the provided filter list.
+
+## EXAMPLES
+
+### EXAMPLE 1
+```
+$objectList = @(
+ @{ Id = "/subscriptions/12345/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM" },
+ @{ Id = "/subscriptions/12345/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/anotherVM" }
+)
+$filterList = @("/subscriptions/12345/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM")
+```
+
+$filteredObjects = Get-WAFResourcesByList -ObjectList $objectList -FilterList $filterList -KeyColumn "Id"
+
+## PARAMETERS
+
+### -ObjectList
+An array of objects to be filtered.
+
+```yaml
+Type: Array
+Parameter Sets: (All)
+Aliases:
+
+Required: True
+Position: 1
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### -FilterList
+An array of resource identifiers to filter the objects.
+
+```yaml
+Type: Array
+Parameter Sets: (All)
+Aliases:
+
+Required: True
+Position: 2
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### -KeyColumn
+The name of the property in the objects that contains the resource identifier.
+
+```yaml
+Type: String
+Parameter Sets: (All)
+Aliases:
+
+Required: True
+Position: 3
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### -ProgressAction
+{{ Fill ProgressAction Description }}
+
+```yaml
+Type: ActionPreference
+Parameter Sets: (All)
+Aliases: proga
+
+Required: False
+Position: Named
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### CommonParameters
+This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
+
+## INPUTS
+
+## OUTPUTS
+
+## NOTES
+Author: Your Name
+Date: 2024-08-07
+
+## RELATED LINKS
diff --git a/src/modules/WARA/scope/Get-WAFSubscriptionsByList.md b/src/modules/WARA/scope/Get-WAFSubscriptionsByList.md
new file mode 100644
index 0000000..79b4bb5
--- /dev/null
+++ b/src/modules/WARA/scope/Get-WAFSubscriptionsByList.md
@@ -0,0 +1,110 @@
+---
+external help file: scope-help.xml
+Module Name: scope
+online version:
+schema: 2.0.0
+---
+
+# Get-WAFSubscriptionsByList
+
+## SYNOPSIS
+Filters a list of objects based on subscription identifiers.
+
+## SYNTAX
+
+```
+Get-WAFSubscriptionsByList [-ObjectList] [-FilterList] [-KeyColumn]
+ [-ProgressAction ] []
+```
+
+## DESCRIPTION
+The Get-WAFSubscriptionsByList function takes a list of objects and filters them based on the specified subscription identifiers.
+It compares the first three segments of the KeyColumn property of each object with the provided filter list.
+
+## EXAMPLES
+
+### EXAMPLE 1
+```
+$objectList = @(
+ @{ Id = "/subscriptions/12345/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM" },
+ @{ Id = "/subscriptions/67890/resourceGroups/anotherResourceGroup/providers/Microsoft.Compute/virtualMachines/anotherVM" }
+)
+$filterList = @("/subscriptions/12345")
+```
+
+$filteredObjects = Get-WAFSubscriptionsByList -ObjectList $objectList -FilterList $filterList -KeyColumn "Id"
+
+## PARAMETERS
+
+### -ObjectList
+An array of objects to be filtered.
+
+```yaml
+Type: Array
+Parameter Sets: (All)
+Aliases:
+
+Required: True
+Position: 1
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### -FilterList
+An array of subscription identifiers to filter the objects.
+
+```yaml
+Type: Array
+Parameter Sets: (All)
+Aliases:
+
+Required: True
+Position: 2
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### -KeyColumn
+The name of the property in the objects that contains the subscription identifier.
+
+```yaml
+Type: String
+Parameter Sets: (All)
+Aliases:
+
+Required: True
+Position: 3
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### -ProgressAction
+{{ Fill ProgressAction Description }}
+
+```yaml
+Type: ActionPreference
+Parameter Sets: (All)
+Aliases: proga
+
+Required: False
+Position: Named
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### CommonParameters
+This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
+
+## INPUTS
+
+## OUTPUTS
+
+## NOTES
+Author: Kyle Poineal
+Date: 2024-08-07
+
+## RELATED LINKS
diff --git a/src/modules/WARA/scope/Get-WAFUnfilteredResourceList.md b/src/modules/WARA/scope/Get-WAFUnfilteredResourceList.md
new file mode 100644
index 0000000..7c10111
--- /dev/null
+++ b/src/modules/WARA/scope/Get-WAFUnfilteredResourceList.md
@@ -0,0 +1,93 @@
+---
+external help file: scope-help.xml
+Module Name: scope
+online version:
+schema: 2.0.0
+---
+
+# Get-WAFUnfilteredResourceList
+
+## SYNOPSIS
+Retrieves unfiltered resources from Azure based on provided subscription, resource group, and resource filters.
+
+## SYNTAX
+
+```
+Get-WAFUnfilteredResourceList [[-SubscriptionFilters] ] [[-ResourceGroupFilters] ]
+ [[-ResourceFilters] ]
+```
+
+## DESCRIPTION
+The Get-WAFUnfilteredResource function takes arrays of subscription filters, resource group filters, and resource filters.
+It creates a list of unique subscription IDs based on these filters and retrieves unfiltered resources from Azure using these subscription IDs.
+
+## EXAMPLES
+
+### EXAMPLE 1
+```
+$subscriptionFilters = @('/subscriptions/11111111-1111-1111-1111-111111111111')
+$resourceGroupFilters = @('/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/test1')
+$resourceFilters = @('/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/test1/providers/Microsoft.Compute/virtualMachines/TestVM1')
+$unfilteredResources = Get-WAFUnfilteredResource -SubscriptionFilters $subscriptionFilters -ResourceGroupFilters $resourceGroupFilters -ResourceFilters $resourceFilters
+```
+
+## PARAMETERS
+
+### -SubscriptionFilters
+An array of strings representing the subscription filters.
+Each string should be a subscription ID or a part of a subscription ID.
+
+```yaml
+Type: String[]
+Parameter Sets: (All)
+Aliases:
+
+Required: False
+Position: 1
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### -ResourceGroupFilters
+An array of strings representing the resource group filters.
+Each string should be a resource group ID or a part of a resource group ID.
+
+```yaml
+Type: String[]
+Parameter Sets: (All)
+Aliases:
+
+Required: False
+Position: 2
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+### -ResourceFilters
+An array of strings representing the resource filters.
+Each string should be a resource ID or a part of a resource ID.
+
+```yaml
+Type: String[]
+Parameter Sets: (All)
+Aliases:
+
+Required: False
+Position: 3
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
+## INPUTS
+
+## OUTPUTS
+
+### Returns an array of unfiltered resources from Azure.
+## NOTES
+This function assumes that the Get-WAFAllAzGraphResource function is defined and available in the current context.
+It also assumes that Azure authentication has been set up.
+
+## RELATED LINKS
diff --git a/src/modules/WARA/scope/scope.psm1 b/src/modules/WARA/scope/scope.psm1
index e69de29..b9bef3a 100644
--- a/src/modules/WARA/scope/scope.psm1
+++ b/src/modules/WARA/scope/scope.psm1
@@ -0,0 +1,294 @@
+<#
+.SYNOPSIS
+ Retrieves filtered lists of Azure resources based on provided subscription, resource group, and resource filters.
+
+.DESCRIPTION
+ This module contains functions to filter Azure resources by subscription, resource group, and resource IDs.
+ It includes the following functions:
+ - Get-WAFResourceGroupsByList
+ - Get-WAFSubscriptionsByList
+ - Get-WAFResourcesByList
+ - Get-WAFFilteredResourceList
+
+.EXAMPLE
+ $subscriptionFilters = @("/subscriptions/12345")
+ $resourceGroupFilters = @("/subscriptions/12345/resourceGroups/myResourceGroup")
+ $resourceFilters = @("/subscriptions/12345/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM")
+
+ $filteredResources = Get-WAFFilteredResourceList -SubscriptionFilters $subscriptionFilters -ResourceGroupFilters $resourceGroupFilters -ResourceFilters $resourceFilters
+
+.NOTES
+ Author: Kyle Poineal
+ Date: 2024-08-07
+#>
+
+<#
+.SYNOPSIS
+Short description
+
+.DESCRIPTION
+Long description
+
+.PARAMETER ObjectList
+Parameter description
+
+.PARAMETER FilterList
+Parameter description
+
+.PARAMETER KeyColumn
+Parameter description
+
+.EXAMPLE
+An example
+
+.NOTES
+General notes
+#>
+
+<#
+.SYNOPSIS
+ Filters a list of objects based on resource group identifiers.
+
+.DESCRIPTION
+ The Get-WAFResourceGroupsByList function takes a list of objects and filters them based on the specified resource group identifiers.
+ It compares the first five segments of the KeyColumn property of each object with the provided filter list.
+
+.PARAMETER ObjectList
+ An array of objects to be filtered.
+
+.PARAMETER FilterList
+ An array of resource group identifiers to filter the objects.
+
+.PARAMETER KeyColumn
+ The name of the property in the objects that contains the resource group identifier.
+
+.EXAMPLE
+ $objectList = @(
+ @{ Id = "/subscriptions/12345/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM" },
+ @{ Id = "/subscriptions/12345/resourceGroups/anotherResourceGroup/providers/Microsoft.Compute/virtualMachines/anotherVM" }
+ )
+ $filterList = @("/subscriptions/12345/resourceGroups/myResourceGroup")
+
+ $filteredObjects = Get-WAFResourceGroupsByList -ObjectList $objectList -FilterList $filterList -KeyColumn "Id"
+
+.NOTES
+ Author: Kyle Poineal
+ Date: 2024-08-07
+#>
+function Get-WAFResourceGroupsByList {
+ param (
+ [Parameter(Mandatory = $true)]
+ [array]$ObjectList,
+
+ [Parameter(Mandatory = $true)]
+ [array]$FilterList,
+
+ [Parameter(Mandatory = $true)]
+ [string]$KeyColumn
+ )
+
+ $matchingObjects = foreach ($obj in $ObjectList) {
+ if (($obj.$KeyColumn.split('/')[0..4] -join '/') -in $FilterList) {
+ $obj
+ }
+ }
+
+ return $matchingObjects
+ }
+
+ <#
+.SYNOPSIS
+ Filters a list of objects based on subscription identifiers.
+
+.DESCRIPTION
+ The Get-WAFSubscriptionsByList function takes a list of objects and filters them based on the specified subscription identifiers.
+ It compares the first three segments of the KeyColumn property of each object with the provided filter list.
+
+.PARAMETER ObjectList
+ An array of objects to be filtered.
+
+.PARAMETER FilterList
+ An array of subscription identifiers to filter the objects.
+
+.PARAMETER KeyColumn
+ The name of the property in the objects that contains the subscription identifier.
+
+.EXAMPLE
+ $objectList = @(
+ @{ Id = "/subscriptions/12345/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM" },
+ @{ Id = "/subscriptions/67890/resourceGroups/anotherResourceGroup/providers/Microsoft.Compute/virtualMachines/anotherVM" }
+ )
+ $filterList = @("/subscriptions/12345")
+
+ $filteredObjects = Get-WAFSubscriptionsByList -ObjectList $objectList -FilterList $filterList -KeyColumn "Id"
+
+.NOTES
+ Author: Kyle Poineal
+ Date: 2024-08-07
+#>
+ function Get-WAFSubscriptionsByList {
+ param (
+ [Parameter(Mandatory = $true)]
+ [array]$ObjectList,
+
+ [Parameter(Mandatory = $true)]
+ [array]$FilterList,
+
+ [Parameter(Mandatory = $true)]
+ [string]$KeyColumn
+ )
+
+ $matchingObjects = foreach ($obj in $ObjectList) {
+ if (($obj.$KeyColumn.split('/')[0..2] -join '/') -in $FilterList) {
+ $obj
+ }
+ }
+
+ return $matchingObjects
+ }
+
+ <#
+.SYNOPSIS
+ Filters a list of objects based on resource identifiers.
+
+.DESCRIPTION
+ The Get-WAFResourcesByList function takes a list of objects and filters them based on the specified resource identifiers.
+ It compares the KeyColumn property of each object with the provided filter list.
+
+.PARAMETER ObjectList
+ An array of objects to be filtered.
+
+.PARAMETER FilterList
+ An array of resource identifiers to filter the objects.
+
+.PARAMETER KeyColumn
+ The name of the property in the objects that contains the resource identifier.
+
+.EXAMPLE
+ $objectList = @(
+ @{ Id = "/subscriptions/12345/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM" },
+ @{ Id = "/subscriptions/12345/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/anotherVM" }
+ )
+ $filterList = @("/subscriptions/12345/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM")
+
+ $filteredObjects = Get-WAFResourcesByList -ObjectList $objectList -FilterList $filterList -KeyColumn "Id"
+
+.NOTES
+ Author: Your Name
+ Date: 2024-08-07
+#>
+ function Get-WAFResourcesByList {
+ param (
+ [Parameter(Mandatory = $true)]
+ [array]$ObjectList,
+
+ [Parameter(Mandatory = $true)]
+ [array]$FilterList,
+
+ [Parameter(Mandatory = $true)]
+ [string]$KeyColumn
+ )
+
+
+
+ $matchingObjects = foreach ($obj in $ObjectList) {
+ if ($obj.$KeyColumn -in $FilterList) {
+ $obj
+ }
+ }
+
+ return $matchingObjects
+ }
+
+<#
+.SYNOPSIS
+Retrieves unfiltered resources from Azure based on provided subscription, resource group, and resource filters.
+
+.DESCRIPTION
+The Get-WAFUnfilteredResource function takes arrays of subscription filters, resource group filters, and resource filters.
+It creates a list of unique subscription IDs based on these filters and retrieves unfiltered resources from Azure using these subscription IDs.
+
+.PARAMETER SubscriptionFilters
+An array of strings representing the subscription filters. Each string should be a subscription ID or a part of a subscription ID.
+
+.PARAMETER ResourceGroupFilters
+An array of strings representing the resource group filters. Each string should be a resource group ID or a part of a resource group ID.
+
+.PARAMETER ResourceFilters
+An array of strings representing the resource filters. Each string should be a resource ID or a part of a resource ID.
+
+.OUTPUTS
+Returns an array of unfiltered resources from Azure.
+
+.EXAMPLE
+$subscriptionFilters = @('/subscriptions/11111111-1111-1111-1111-111111111111')
+$resourceGroupFilters = @('/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/test1')
+$resourceFilters = @('/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/test1/providers/Microsoft.Compute/virtualMachines/TestVM1')
+$unfilteredResources = Get-WAFUnfilteredResource -SubscriptionFilters $subscriptionFilters -ResourceGroupFilters $resourceGroupFilters -ResourceFilters $resourceFilters
+
+.NOTES
+This function assumes that the Get-WAFAllAzGraphResource function is defined and available in the current context. It also assumes that Azure authentication has been set up.
+#>
+function Get-WAFUnfilteredResourceList {
+ param(
+ [String[]]$SubscriptionFilters,
+ [String[]]$ResourceGroupFilters,
+ [String[]]$ResourceFilters
+ )
+ #Create a list of subscription ids based on the filters. Adds all the filters together then splits them into subscription Ids. Groups them to remove duplicates and returns a string array.
+ $ImplicitSubscriptionIds = (($SubscriptionFilters + $ResourceGroupFilters + $ResourceFilters) | ForEach-Object {$_.split("/")[0..2] -join "/"} | Group-Object | Select-Object Name).Name
+ $UnfilteredResources = Get-WAFAllAzGraphResource -subscriptionId ($ImplicitSubscriptionIds -replace ("/subscriptions/",""))
+ return $UnfilteredResources
+}
+
+<#
+.SYNOPSIS
+ Retrieves a filtered list of Azure resources based on subscription, resource group, and resource filters.
+
+.DESCRIPTION
+ The Get-WAFFilteredResourceList function filters Azure resources by combining subscription, resource group, and resource filters.
+ It generates a list of implicit subscription IDs from the provided filters, retrieves unfiltered resources, and then applies the filters to return the matching resources.
+
+.PARAMETER SubscriptionFilters
+ An array of subscription identifiers to filter the resources.
+
+.PARAMETER ResourceGroupFilters
+ An array of resource group identifiers to filter the resources.
+
+.PARAMETER ResourceFilters
+ An array of resource identifiers to filter the resources.
+
+.EXAMPLE
+ $subscriptionFilters = @("/subscriptions/12345")
+ $resourceGroupFilters = @("/subscriptions/12345/resourceGroups/myResourceGroup")
+ $resourceFilters = @("/subscriptions/12345/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM")
+
+ $filteredResources = Get-WAFFilteredResourceList -SubscriptionFilters $subscriptionFilters -ResourceGroupFilters $resourceGroupFilters -ResourceFilters $resourceFilters
+
+.NOTES
+ Author: Your Name
+ Date: 2024-08-07
+#>
+function Get-WAFFilteredResourceList {
+ param(
+ [String[]]$SubscriptionFilters,
+ [String[]]$ResourceGroupFilters,
+ [String[]]$ResourceFilters,
+ [String[]]$UnfilteredResources
+ )
+
+ # TODO: ADD FILTERS FOR TAGS
+
+ $SubscriptionFilters ? [void]($SubscriptionFilteredResources = Get-WAFSubscriptionsByList -ObjectList $UnfilteredResources -FilterList $SubscriptionFilters -KeyColumn "Id") : "Subscription Filters not provided."
+
+ $ResourceGroupFilters ? [void]($ResourceGroupFilteredResources = Get-WAFResourceGroupsByList -ObjectList $UnfilteredResources -FilterList $ResourceGroupFilters -KeyColumn "Id") : "Resource Group Filters not provided."
+
+ $ResourceFilters ? [void]($ResourceFilteredResources = Get-WAFResourcesByList -ObjectList $UnfilteredResources -FilterList $ResourceFilters -KeyColumn "Id") : "Resource Filters not provided."
+
+ #Originally used to remove duplicates but there was some weird interaction with the return object that caused it to duplicate the entire array.
+ #This just needs to be sorted outside of this function using | Sort-Object -Property Id,RecommendationId -Unique
+ $FilteredResources = $SubscriptionFilteredResources + $ResourceGroupFilteredResources + $ResourceFilteredResources
+
+ return $FilteredResources
+
+}
diff --git a/src/modules/WARA/support/support.psm1 b/src/modules/WARA/support/support.psm1
deleted file mode 100644
index e69de29..0000000
diff --git a/src/modules/WARA/utils/utils.psm1 b/src/modules/WARA/utils/utils.psm1
index e69de29..7b7ce66 100644
--- a/src/modules/WARA/utils/utils.psm1
+++ b/src/modules/WARA/utils/utils.psm1
@@ -0,0 +1,103 @@
+function Import-WAFConfigFileData($file){
+ # Read the file content and store it in a variable
+ $filecontent = (Get-content $file).trim().tolower()
+
+ # Create an array to store the line number of each section
+ $linetable = @()
+ $objarray = @{}
+
+ # Iterate through the file content and store the line number of each section
+ Foreach($line in $filecontent){
+ if (-not [string]::IsNullOrWhiteSpace($line) -and -not $line.startswith("#")){
+ # If the line is a section, store the line number
+ if ($line -match "^\[([^\]]+)\]$") {
+ # Store the section name and line number. Remove the brackets from the section name
+ $linetable += $filecontent.indexof($line)
+
+ }
+ }
+ }
+
+ # Iterate through the line numbers and extract the section content
+ $count = 0
+ foreach($entry in $linetable){
+
+ # Get the section name
+ $name = $filecontent[$entry]
+ # Remove the brackets from the section name
+ $name = $name.replace("[","").replace("]","")
+
+ # Get the start and stop line numbers for the section content
+ # If the section is the last one, set the stop line number to the end of the file
+ $start = $entry + 1
+ if($count -eq ($linetable.length-1)){
+ $stop = $filecontent.length - 1
+ }
+ else{
+ $stop = $linetable[$count+1] - 2
+ }
+
+ # Extract the section content
+ $configsection = $filecontent[$start..$stop]
+
+ # Add the section content to the object array
+ $objarray += @{$Name=$configsection}
+
+ # Increment the count
+ $count++
+ }
+
+ # Return the object array and cast to pscustomobject
+ return [pscustomobject]$objarray
+
+ }
+
+ function Connect-WAFAzure
+{
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [string]$TenantID,
+ [Parameter(Mandatory = $true)]
+ [string[]]$SubscriptionIds,
+ [ValidateSet('AzureCloud', 'AzureChinaCloud', 'AzureGermanCloud', 'AzureUSGovernment')]
+ [string]$AzureEnvironment = 'AzureCloud'
+ )
+
+ # Connect To Azure Tenant
+ If(-not (Get-AzContext))
+ {
+ Connect-AzAccount -Tenant $TenantID -WarningAction SilentlyContinue -Environment $AzureEnvironment
+ }
+}
+
+function Import-WAFAPRLJSON
+{
+<#
+.SYNOPSIS
+This module contains utility functions for the Well-Architected Reliability Assessment.
+
+.DESCRIPTION
+The utils.psm1 module provides various utility functions that can be used in the Well-Architected Reliability Assessment project.
+
+.NOTES
+File Path: /c:/dev/repos/Well-Architected-Reliability-Assessment/src/modules/wara/utils/utils.psm1
+
+.LINK
+GitHub Repository: https://github.com/your-username/Well-Architected-Reliability-Assessment
+
+.EXAMPLE
+# Example usage of the utility function
+PS> Invoke-UtilityFunction -Parameter1 "Value1" -Parameter2 "Value2"
+#>
+param(
+ [Parameter(Mandatory = $true)]
+ [string]$file
+)
+ # Validate file path, read content and convert to JSON
+ $return = (test-path $file) ? (get-content $file -raw | convertfrom-json -depth 10) : ("Path does not exist")
+
+ # Return the converted JSON object
+ return $return
+}
+
diff --git a/src/modules/WARA/wara.psd1 b/src/modules/WARA/wara.psd1
index c665168..41a81d1 100644
--- a/src/modules/WARA/wara.psd1
+++ b/src/modules/WARA/wara.psd1
@@ -21,7 +21,7 @@ ModuleVersion = '0.0.1'
GUID = 'a6fbf19e-af5d-4c70-92f6-f96432aba2dd'
# Author of this module
-Author = 'kylepoineal'
+Author = 'Microsoft'
# Company or vendor of this module
CompanyName = 'Microsoft'
diff --git a/src/tests/coverage.md b/src/tests/coverage.md
new file mode 100644
index 0000000..3d5484c
--- /dev/null
+++ b/src/tests/coverage.md
@@ -0,0 +1,8 @@
+# Code Coverage Report - Scope.psm1
+| Metric | Value |
+|-----------------|-------------|
+| Result | ✅ Passed |
+| Passed Count | ✅ 3 |
+| Failed Count | ✅ 0 |
+| Coverage (%) | ❌ 43.75 |
+| Target Coverage (%) | 75 |
diff --git a/src/tests/coverage.xml b/src/tests/coverage.xml
new file mode 100644
index 0000000..c90c7af
--- /dev/null
+++ b/src/tests/coverage.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tests/data/aprl.json b/src/tests/data/aprl.json
new file mode 100644
index 0000000..ee44aba
--- /dev/null
+++ b/src/tests/data/aprl.json
@@ -0,0 +1,7704 @@
+[
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "baf3bfc0-32a2-4c0c-926d-c9bf0b49808e",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Change your API Management service tier",
+ "url": "https://learn.microsoft.com/en-us/azure/api-management/upgrade-and-scale#change-your-api-management-service-tier"
+ },
+ {
+ "name": "Migrate Azure API Management to availability zone support",
+ "url": "https://learn.microsoft.com/en-us/azure/reliability/migrate-api-mgt"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Upgrading the API Management instance to the Premium SKU adds support for Availability Zones, enhancing availability and resilience by distributing services across physically separate locations within Azure regions.\n",
+ "pgVerified": false,
+ "description": "Migrate API Management services to Premium SKU to support Availability Zones",
+ "potentialBenefits": "Enhanced availability and resilience",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ApiManagement/service",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all API Management instances that aren't Premium\r\nresources\r\n| where type =~ 'Microsoft.ApiManagement/service'\r\n| extend skuName = sku.name\r\n| where tolower(skuName) != tolower('premium')\r\n| project recommendationId = \"baf3bfc0-32a2-4c0c-926d-c9bf0b49808e\", name, id, tags, param1=strcat(\"SKU: \", skuName)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "740f2c1c-8857-4648-80eb-47d2c56d5a50",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Ensure API Management availability and reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/api-management/high-availability#availability-zones"
+ },
+ {
+ "name": "Migrate Azure API Management to availability zone support",
+ "url": "https://learn.microsoft.com/en-us/azure/reliability/migrate-api-mgt"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Zone redundancy for APIM instances ensures the gateway and control plane (Management API, developer portal, Git configuration) are replicated across datacenters in physically separated zones, boosting resilience to zone failures.\n",
+ "pgVerified": false,
+ "description": "Enable Availability Zones on Premium API Management instances",
+ "potentialBenefits": "Improved resilience to zone failures",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ApiManagement/service",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Premium API Management instances that aren't zone redundant\r\nresources\r\n| where type =~ 'Microsoft.ApiManagement/service'\r\n| extend skuName = sku.name\r\n| where tolower(skuName) == tolower('premium')\r\n| where isnull(zones) or array_length(zones) < 2\r\n| extend zoneValue = iff((isnull(zones)), \"null\", zones)\r\n| project recommendationId = \"740f2c1c-8857-4648-80eb-47d2c56d5a50\", name, id, tags, param1=\"Zones: No Zone or Zonal\", param2=strcat(\"Zones value: \", zoneValue )\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e35cf148-8eee-49d1-a1c9-956160f99e0b",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure API Management - stv1 platform retirement (August 2024)",
+ "url": "https://learn.microsoft.com/en-us/azure/api-management/breaking-changes/stv1-platform-retirement-august-2024"
+ },
+ {
+ "name": "Azure API Management compute platform",
+ "url": "https://learn.microsoft.com/en-us/azure/api-management/compute-infrastructure"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Upgrading to API Management stv2 is required as stv1 retires on 31 Aug 2024, offering enhanced capabilities with the new platform version.\n",
+ "pgVerified": false,
+ "description": "Upgrade to platform version stv2",
+ "potentialBenefits": "Ensures service continuity",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ApiManagement/service",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all API Management instances that aren't upgraded to platform version stv2\r\nresources\r\n| where type =~ 'Microsoft.ApiManagement/service'\r\n| extend plat_version = properties.platformVersion\r\n| extend skuName = sku.name\r\n| where tolower(plat_version) != tolower('stv2')\r\n| project recommendationId = \"e35cf148-8eee-49d1-a1c9-956160f99e0b\", name, id, tags, param1=strcat(\"Platform Version: \", plat_version) , param2=strcat(\"SKU: \", skuName)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "c79680ea-de85-44fa-a596-f31fa17a952f",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Setting up auto-scale for Azure API Management",
+ "url": "https://learn.microsoft.com/azure/api-management/api-management-howto-autoscale"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Use API Management with auto-scale for high availability in workloads that experience variable traffic patterns. There are several limitations with auto-scale, so review the documentation to ensure it meets your requirements.\n",
+ "pgVerified": false,
+ "description": "Enable auto-scale for production workloads on API Management services",
+ "potentialBenefits": "Enhanced availability and resilience",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ApiManagement/service",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "8dbcd94b-0948-4df3-b608-1946726c3abf",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Health probes for Azure Container Apps",
+ "url": "https://learn.microsoft.com/azure/container-apps/health-probes?tabs=arm-template"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Enable container health probes to monitor the health of your container apps and ensure that unhealthy containers are restarted automatically.\n",
+ "pgVerified": false,
+ "description": "Enable container health probes",
+ "potentialBenefits": "Enhanced availability and resilience",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.App/containerApps",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "f4201965-a88d-449d-b3b4-021394719eb2",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Reliability in Azure Container Apps",
+ "url": "https://learn.microsoft.com/en-us/azure/reliability/reliability-azure-container-apps"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "To take advantage of availability zones, you must enable zone redundancy when you create a Container Apps environment. The environment must include a virtual network with an available subnet. To ensure proper distribution of replicas, set your app's minimum replica count to three.\n",
+ "pgVerified": false,
+ "description": "Deploy zone redundant Container app environments",
+ "potentialBenefits": "Enhances app resiliency and reliability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.App/managedenvironments",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// The query filters the qualified Container app environments that do not have Zone Redundancy enabled.\r\nresources\r\n| where type =~ \"microsoft.app/managedenvironments\"\r\n| where tobool(properties.zoneRedundant) == false\r\n| project recommendationId = \"f4201965-a88d-449d-b3b4-021394719eb2\", name, id, tags, param1 = \"AvailabilityZones: Single Zone\"\r\n| order by id asc\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "bb4c8db4-f821-475b-b1ea-16e95358665e",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Purge protection",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-app-configuration/concept-soft-delete#purge-protection"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "With Purge protection enabled, soft deleted stores can't be purged in the retention period. If disabled, the soft deleted store can be purged before the retention period expires.\n",
+ "pgVerified": false,
+ "description": "Enable Purge protection for Azure App Configuration",
+ "potentialBenefits": "Prevent accidental deletion of configuration stores.",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.AppConfiguration/configurationStores",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Purge protection should be enabled for App Configuration stores to prevent accidental deletion of configuration data.\r\nresources\r\n| where type =~ \"Microsoft.AppConfiguration/configurationStores\"\r\n| where sku.name <> \"free\"\r\n| where (properties.enablePurgeProtection <> true) or isnull(properties.enablePurgeProtection )\r\n| project recommendationId = \"bb4c8db4-f821-475b-b1ea-16e95358665e\", name, id, tags, param1 = \"Enable purge protection\"\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "2102a57a-a056-4d5e-afe5-9df9f92177ca",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Choose App Configuration tier",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-app-configuration/faq#which-app-configuration-tier-should-i-use"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "SLA is not available for Free tier. Upgrade to the Standard tier to get an SLA of 99.9%\n",
+ "pgVerified": false,
+ "description": "Upgrade to App Configuration Standard tier",
+ "potentialBenefits": "High availability, more storage, higher request quota.",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.AppConfiguration/configurationStores",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Upgrade to App Configuration Standard tier\r\nresources\r\n| where type =~ \"Microsoft.AppConfiguration/configurationStores\"\r\n| where sku.name == \"free\"\r\n| project recommendationId = \"2102a57a-a056-4d5e-afe5-9df9f92177ca\", name, id, tags, param1 = \"Upgrade to Standard SKU\"\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "67205887-0733-466e-b50e-b1cd7316c514",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Disaster recovery for Automation accounts",
+ "url": "https://learn.microsoft.com/en-us/azure/automation/automation-disaster-recovery?tabs=win-hrw%2Cps-script%2Coption-one"
+ },
+ {
+ "name": "Disaster recovery scenarios for cloud and hybrid jobs",
+ "url": "https://learn.microsoft.com/en-us/azure/automation/automation-disaster-recovery?tabs=win-hrw%2Cps-script%2Coption-one#scenarios-for-cloud-and-hybrid-jobs"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Set up disaster recovery for Automation accounts and resources like Modules, Connections, Credentials, Certificates, Variables, and Schedules to deal with region or zone failures. A replica Automation account should be ready in a secondary region for failover.\n",
+ "pgVerified": false,
+ "description": "Set up disaster recovery of Automation accounts and its dependent resources",
+ "potentialBenefits": "Ensures continuity during outages",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Automation/automationAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "74fcb9f2-9a25-49a6-8c42-d32851c4afb7",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/azure-vmware/eslz-management-and-monitoring#design-recommendations"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Ensure Azure Service Health notifications are set for Azure VMware Solution across all used regions and subscriptions. This communicates service/security issues and maintenance activities like host replacements and upgrades, reducing service request submissions.\n",
+ "pgVerified": true,
+ "description": "Configure Azure Service Health notifications and alerts for Azure VMware Solution",
+ "potentialBenefits": "Prompt mitigation of issues.",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.AVS/privateClouds",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of Azure VMware Solution resources that don't have one or more service health alerts covering AVS private clouds in the deployed subscription and region pairs.\r\n//full list of private clouds\r\n(resources\r\n| where ['type'] == \"microsoft.avs/privateclouds\"\r\n| extend locale = tolower(location)\r\n| extend subscriptionId = tolower(subscriptionId)\r\n| project id, name, tags, subscriptionId, locale)\r\n| join kind=leftouter\r\n//Alert ID's that include all incident types filtered by AVS Service Health alerts\r\n((resources\r\n| where type == \"microsoft.insights/activitylogalerts\"\r\n| extend alertproperties = todynamic(properties)\r\n| where alertproperties.condition.allOf[0].field == \"category\" and alertproperties.condition.allOf[0].equals == \"ServiceHealth\"\r\n| where alertproperties.condition.allOf[1].field == \"properties.impactedServices[*].ServiceName\" and set_has_element(alertproperties.condition.allOf[1].containsAny, \"Azure VMware Solution\")\r\n| extend locale = strcat_array(split(tolower(alertproperties.condition.allOf[2].containsAny),' '), '')\r\n| mv-expand todynamic(locale)\r\n| where locale != \"global\"\r\n| project subscriptionId, tostring(locale) )\r\n| union\r\n//Alert ID's that include only some of the incident types after filtering by service health alerts covering AVS private clouds.\r\n(resources\r\n| where type == \"microsoft.insights/activitylogalerts\"\r\n| extend subscriptionId = tolower(subscriptionId)\r\n| extend alertproperties = todynamic(properties)\r\n| where alertproperties.condition.allOf[0].field == \"category\" and alertproperties.condition.allOf[0].equals == \"ServiceHealth\"\r\n| where alertproperties.condition.allOf[2].field == \"properties.impactedServices[*].ServiceName\" and set_has_element(alertproperties.condition.allOf[2].containsAny, \"Azure VMware Solution\")\r\n| extend locale = strcat_array(split(tolower(alertproperties.condition.allOf[3].containsAny),' '), '')\r\n| mv-expand todynamic(locale)\r\n| mv-expand alertproperties.condition.allOf[1].anyOf\r\n| extend incidentType = alertproperties_condition_allOf_1_anyOf.equals\r\n| where locale != \"global\"\r\n| project id, subscriptionId, locale, incidentType\r\n| distinct subscriptionId, tostring(locale), tostring(incidentType)\r\n| summarize incidentTypes=count() by subscriptionId, locale\r\n| where incidentTypes == 5 //only include this subscription, region pair if it includes all the incident types.\r\n| project subscriptionId, locale)) on subscriptionId, locale\r\n| where subscriptionId1 == \"\" or locale1 == \"\" or isnull(subscriptionId1) or isnull(locale1)\r\n| project recommendationId = \"74fcb9f2-9a25-49a6-8c42-d32851c4afb7\", name, id, tags, param1 = \"avsServiceHealthAlertsAllIncidentTypesConfigured: False\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "29d7a115-dfb6-4df1-9205-04824109548f",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Configure and streamline alerts",
+ "url": "https://learn.microsoft.com/en-us/azure/well-architected/azure-vmware/monitoring#configure-and-streamline-alerts"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Set an alert for when the node count in Azure VMware Solution Private Cloud hits or exceeds 90 hosts, enabling timely planning for a new private cloud.\n",
+ "pgVerified": true,
+ "description": "Monitor when Azure VMware Solution Private Cloud is reaching the capacity limit",
+ "potentialBenefits": "Proactive capacity planning",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.AVS/privateClouds",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot be validated with ARG\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "f86355e3-de7c-4dad-8080-1b0b411e66c8",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/en-us/azure/well-architected/azure-vmware/monitoring#configure-and-streamline-alerts"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Alert when the cluster size reaches 14 hosts. Set up periodic alerts for planning new clusters or datastores due to growth, especially from storage needs. Beyond 14 hosts, trigger alerts for each new host addition for proactive resource monitoring.\n",
+ "pgVerified": true,
+ "description": "Monitor when Azure VMware Solution Cluster Size is approaching the host limit",
+ "potentialBenefits": "Proactive resource management",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.AVS/privateClouds",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot be validated with ARG\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "9ec5b4c8-3dd8-473a-86ee-3273290331b9",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/en-us/azure/well-architected/azure-vmware/infrastructure#implement-high-availability"
+ },
+ {
+ "name": "Stretched Clusters",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-vmware/deploy-vsan-stretched-clusters"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "For Azure VMware Solution, enabling Stretched Clusters offers 99.99% SLA, synchronous storage replication (RPO=0), and spreads vSAN datastore across two AZs. Must be done at initial setup, needing double quota due to extension across AZs.\n",
+ "pgVerified": true,
+ "description": "Enable Stretched Clusters for Multi-AZ Availability of the vSAN Datastore",
+ "potentialBenefits": "99.99% SLA, 0 RPO, Multi-AZ",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.AVS/privateClouds",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of Azure VMware Solution resources that aren't configured as stretched clusters and in supported regions.\r\nresources\r\n| where ['type'] == \"microsoft.avs/privateclouds\"\r\n| extend avsproperties = todynamic(properties)\r\n| where avsproperties.availability.strategy != \"DualZone\"\r\n| where location in (\"uksouth\", \"westeurope\", \"germanywestcentral\", \"australiaeast\")\r\n| project recommendationId = \"9ec5b4c8-3dd8-473a-86ee-3273290331b9\", name, id, tags, param1 = \"stretchClusters: Disabled\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "4232eb32-3241-4049-9e14-9b8005817b56",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-vmware/configure-alerts-for-azure-vmware-solution#supported-metrics-and-activities"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Ensure VMware vSAN datastore slack space is maintained for SLA by monitoring storage utilization and setting alerts at 70% and 75% utilization to allow for capacity planning. To expand, add hosts or external storage like Azure Elastic SAN, Azure NetApp Files, if CPU and RAM requirements are met.\n",
+ "pgVerified": true,
+ "description": "Configure Azure Monitor Alert warning thresholds for vSAN datastore utilization",
+ "potentialBenefits": "Optimized capacity planning for vSAN",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.AVS/privateClouds",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of Azure VMware Solution resources that don't have a vSAN capacity critical alert with a threshold of 75% or a warning capacity of 70%.\r\n(\r\nresources\r\n| where ['type'] == \"microsoft.avs/privateclouds\"\r\n| extend scopeId = tolower(tostring(id))\r\n| project ['scopeId'], name, id, tags\r\n| join kind=leftouter (\r\nresources\r\n| where type == \"microsoft.insights/metricalerts\"\r\n| extend alertProperties = todynamic(properties)\r\n| mv-expand alertProperties.scopes\r\n| mv-expand alertProperties.criteria.allOf\r\n| extend scopeId = tolower(tostring(alertProperties_scopes))\r\n| extend metric = alertProperties_criteria_allOf.metricName\r\n| extend threshold = alertProperties_criteria_allOf.threshold\r\n| project scopeId, tostring(metric), toint(['threshold'])\r\n| where metric == \"DiskUsedPercentage\"\r\n| where threshold == 75\r\n) on scopeId\r\n| where isnull(['threshold'])\r\n| project recommendationId = \"4232eb32-3241-4049-9e14-9b8005817b56\", name, id, tags, param1 = \"vsanCapacityCriticalAlert: isNull or threshold != 75\"\r\n)\r\n| union (\r\nresources\r\n| where ['type'] == \"microsoft.avs/privateclouds\"\r\n| extend scopeId = tolower(tostring(id))\r\n| project ['scopeId'], name, id, tags\r\n| join kind=leftouter (\r\nresources\r\n| where type == \"microsoft.insights/metricalerts\"\r\n| extend alertProperties = todynamic(properties)\r\n| mv-expand alertProperties.scopes\r\n| mv-expand alertProperties.criteria.allOf\r\n| extend scopeId = tolower(tostring(alertProperties_scopes))\r\n| extend metric = alertProperties_criteria_allOf.metricName\r\n| extend threshold = alertProperties_criteria_allOf.threshold\r\n| project scopeId, tostring(metric), toint(['threshold'])\r\n| where metric == \"DiskUsedPercentage\"\r\n| where threshold == 70\r\n) on scopeId\r\n| where isnull(['threshold'])\r\n| project recommendationId = \"4232eb32-3241-4049-9e14-9b8005817b56\", name, id, tags, param1 = \"vsanCapacityWarningAlert: isNull or threshold != 70\"\r\n)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "fa4ab927-bced-429a-971a-53350de7f14b",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/en-us/azure/well-architected/azure-vmware/monitoring#manage-logs-and-archives"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Ensure Diagnostic Settings are configured for each private cloud to send syslogs to external sources for analysis and/or archiving. Azure VMware Solution Syslogs contain data for troubleshooting and performance, aiding quicker issue resolution and early detection of issues.\n",
+ "pgVerified": true,
+ "description": "Configure Syslog in Diagnostic Settings for Azure VMware Solution",
+ "potentialBenefits": "Faster issue resolution, early detection",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.AVS/privateClouds",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot be validated with ARG\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "4ee5d535-c47b-470a-9557-4a3dd297d62f",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/en-us/azure/well-architected/azure-vmware/monitoring#configure-and-streamline-alerts"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Ensure sufficient compute resources to avoid host resource exhaustion in Azure VMware Solution, which utilizes vSphere DRS and HA for dynamic workload resource management. However, sustained CPU utilization over 95% may increase CPU Ready times, impacting workloads.\n",
+ "pgVerified": true,
+ "description": "Monitor CPU Utilization to ensure sufficient resources for workloads",
+ "potentialBenefits": "Avoids resource exhaustion, optimizes performance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.AVS/privateClouds",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of Azure VMware Solution resources that don't have a Cluster CPU capacity critical alert with a threshold of 95%.\r\nresources\r\n| where ['type'] == \"microsoft.avs/privateclouds\"\r\n| extend scopeId = tolower(tostring(id))\r\n| project ['scopeId'], name, id, tags\r\n| join kind=leftouter (\r\nresources\r\n| where type == \"microsoft.insights/metricalerts\"\r\n| extend alertProperties = todynamic(properties)\r\n| mv-expand alertProperties.scopes\r\n| mv-expand alertProperties.criteria.allOf\r\n| extend scopeId = tolower(tostring(alertProperties_scopes))\r\n| extend metric = alertProperties_criteria_allOf.metricName\r\n| extend threshold = alertProperties_criteria_allOf.threshold\r\n| project scopeId, tostring(metric), toint(['threshold'])\r\n| where metric == \"EffectiveCpuAverage\"\r\n| where threshold == 95\r\n) on scopeId\r\n| where isnull(['threshold'])\r\n| project recommendationId = \"4ee5d535-c47b-470a-9557-4a3dd297d62f\", name, id, tags, param1 = \"hostCpuCriticalAlert: isNull or threshold != 95\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "029208c8-5186-4a76-8ee8-6e3445fef4dd",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/en-us/azure/well-architected/azure-vmware/monitoring#configure-and-streamline-alerts"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Ensure sufficient memory resources to prevent host resource exhaustion in Azure VMware Solution. It uses vSphere DRS and vSphere HA for dynamic workload management. Yet, continuous memory use over 95% leads to disk swapping, affecting workloads.\n",
+ "pgVerified": true,
+ "description": "Monitor Memory Utilization to ensure sufficient resources for workloads",
+ "potentialBenefits": "Avoids host exhaustion and swapping",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.AVS/privateClouds",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of Azure VMware Solution resources that don't have a cluster host memory critical alert with a threshold of 95%.\r\nresources\r\n| where ['type'] == \"microsoft.avs/privateclouds\"\r\n| extend scopeId = tolower(tostring(id))\r\n| project ['scopeId'], name, id, tags\r\n| join kind=leftouter (\r\nresources\r\n| where type == \"microsoft.insights/metricalerts\"\r\n| extend alertProperties = todynamic(properties)\r\n| mv-expand alertProperties.scopes\r\n| mv-expand alertProperties.criteria.allOf\r\n| extend scopeId = tolower(tostring(alertProperties_scopes))\r\n| extend metric = alertProperties_criteria_allOf.metricName\r\n| extend threshold = alertProperties_criteria_allOf.threshold\r\n| project scopeId, tostring(metric), toint(['threshold'])\r\n| where metric == \"UsageAverage\"\r\n| where threshold == 95\r\n) on scopeId\r\n| where isnull(['threshold'])\r\n| project recommendationId = \"029208c8-5186-4a76-8ee8-6e3445fef4dd\", name, id, tags, param1 = \"hostMemoryCriticalAlert: isNull or threshold != 95\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "a5ef7c05-c611-4842-9af5-11efdc99123a",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Lock your resources to protect your infrastructure",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/lock-resources"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Applying a resource delete lock to the Azure VMware Solution Private Cloud resource group prevents unauthorized or accidental deletion by anyone with contributor access, ensuring the protection and reliability of the Azure VMware Solution Private Cloud.\n",
+ "pgVerified": true,
+ "description": "Apply Resource delete lock on the resource group hosting the private cloud",
+ "potentialBenefits": "Prevents accidental deletion",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.AVS/privateClouds",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot be validated with ARG\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e0ac2f57-c8c0-4b8c-a7c8-19e5797828b5",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Configure Customer Managed Keys",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-vmware/configure-customer-managed-keys?tabs=azure-portal"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "When using customer-managed keys for encrypting vSAN datastores, leveraging Azure Key Vault for central management and accessing them via a managed identity linked to the private cloud is advised. The expiration of these keys can render the vSAN datastore and its associated workloads inaccessible.\n",
+ "pgVerified": true,
+ "description": "Use key autorotation for vSAN datastore customer-managed keys",
+ "potentialBenefits": "Avoid outages with key auto-rotation",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.AVS/privateClouds",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot be validated with ARG\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "fcc2e257-23af-4c68-aac8-9cc03033c939",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Configure DNS forwarder",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-vmware/configure-dns-azure-vmware-solution#configure-dns-forwarder"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure VMware Solution private clouds support up to three DNS servers for a single FQDN, preventing a single DNS server from becoming a point of failure. It's crucial to use multiple DNS servers for on-premises FQDN resolution from each private cloud.\n",
+ "pgVerified": true,
+ "description": "Use multiple DNS servers per private FQDN zone",
+ "potentialBenefits": "Enhances reliability and avoids failure",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.AVS/privateClouds",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot be validated with ARG\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "3464854d-6f75-4922-95e4-a2a308b53ce6",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/azure/reliability/reliability-batch#cross-region-disaster-recovery-and-business-continuity"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "To ensure cross-region disaster recovery and business continuity, set the right quotas for all Batch accounts to allocate necessary core numbers upfront, preventing execution interruptions from reaching quota limits.\n",
+ "pgVerified": false,
+ "description": "Monitor Batch Account quota",
+ "potentialBenefits": "Ensures business continuity",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Batch/batchAccounts",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "71cfab8f-d588-4742-b175-b6e07ae48dbd",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/azure/batch/create-pool-availability-zones"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "When using Virtual Machine Configuration for Azure Batch pools, opting to distribute your pool across Availability Zones bolsters your compute nodes against Azure datacenter failures.\n",
+ "pgVerified": false,
+ "description": "Create an Azure Batch pool across Availability Zones",
+ "potentialBenefits": "Enhanced reliability and failure protection",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Batch/batchAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5a44bd30-ae6a-4b81-9b68-dc3a8ffca4d8",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Enable zone redundancy for Azure Cache for Redis",
+ "url": "https://learn.microsoft.com/azure/azure-cache-for-redis/cache-how-to-zone-redundancy"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure Cache for Redis offers zone redundancy in Premium and Enterprise tiers, using VMs across multiple Availability Zones to ensure greater resilience and availability.\n",
+ "pgVerified": false,
+ "description": "Enable zone redundancy for Azure Cache for Redis",
+ "potentialBenefits": "Higher resilience and availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cache/Redis",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find Cache for Redis instances with one or no Zones selected\r\nresources\r\n| where type == \"microsoft.cache/redis\"\r\n| where array_length(zones) <= 1 or isnull(zones)\r\n| project recommendationId = \"5a44bd30-ae6a-4b81-9b68-dc3a8ffca4d8\", name, id, tags, param1 = \"AvailabilityZones: Single Zone\"\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "cabc1f98-c8a7-44f7-ab24-977982ef3f70",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Schedule Redis Updates",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-administration#update-channel-and-schedule-updates"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure Cache for Redis allows for specifying maintenance windows. A maintenance window allows you to control the days and times of a week during which the VMs hosting your cache can be updated.\n",
+ "pgVerified": false,
+ "description": "Schedule updates by setting a maintenance window",
+ "potentialBenefits": "Higher resilience and availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cache/redis",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "9437634c-d69e-2747-b13e-631c13182150",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Load Balancing Options",
+ "url": "https://learn.microsoft.com/azure/architecture/guide/technology-choices/load-balancing-overview"
+ },
+ {
+ "name": "Azure Traffic Manager",
+ "url": "https://learn.microsoft.com/azure/traffic-manager/traffic-manager-overview"
+ },
+ {
+ "name": "Azure Front Door",
+ "url": "https://learn.microsoft.com/azure/frontdoor/front-door-overview"
+ },
+ {
+ "name": "Mission-critical global content delivery",
+ "url": "https://learn.microsoft.com/en-us/azure/architecture/guide/networking/global-web-applications/mission-critical-content-delivery"
+ }
+ ],
+ "recommendationControl": "Business Continuity",
+ "longDescription": "For most solutions, choose either Azure Front Door for content caching, CDN, TLS termination, and WAF, or Traffic Manager for simple global load balancing.\n",
+ "pgVerified": true,
+ "description": "Avoid combining Traffic Manager and Front Door",
+ "potentialBenefits": "Optimized network routing and security",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Avoid combining Traffic Manager and Front Door\r\nresources\r\n| where type == \"microsoft.network/trafficmanagerprofiles\"\r\n| mvexpand(properties.endpoints)\r\n| extend endpoint=tostring(properties_endpoints.properties.target)\r\n| project name, trafficmanager=id, matchname=endpoint, tags\r\n| join (\r\n resources\r\n | where type =~ \"microsoft.cdn/profiles/afdendpoints\"\r\n | extend matchname= tostring(properties.hostName)\r\n | extend splitid=split(id, \"/\")\r\n | extend frontdoorid=tolower(strcat_array(array_slice(splitid, 0, 8), \"/\"))\r\n | project name, id, matchname, frontdoorid, type\r\n | union\r\n (cdnresources\r\n | where type =~ \"Microsoft.Cdn/Profiles/CustomDomains\"\r\n | extend matchname= tostring(properties.hostName)\r\n | extend splitid=split(id, \"/\")\r\n | extend frontdoorid=tolower(strcat_array(array_slice(splitid, 0, 8), \"/\"))\r\n | project name, id, matchname, frontdoorid, type)\r\n )\r\n on matchname\r\n| project\r\n recommendationId = \"9437634c-d69e-2747-b13e-631c13182150\",\r\n name=split(trafficmanager, \"/\")[-1],\r\n id=trafficmanager,\r\n tags,\r\n param1=strcat(\"hostname:\", matchname),\r\n param2=strcat(\"frontdoorid:\", frontdoorid)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "6c40b7ae-2bea-5748-be1a-9e9e3b834649",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Secure traffic to Azure Front Door origins",
+ "url": "https://learn.microsoft.com/azure/frontdoor/origin-security?tabs=app-service-functions&pivots=front-door-standard-premium"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Front Door's features perform optimally when traffic exclusively comes through Front Door. It's advised to set up your origin to deny access to traffic that bypasses Front Door.\n",
+ "pgVerified": true,
+ "description": "Restrict traffic to your origins",
+ "potentialBenefits": "Enhances security and performance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "52bc9a7b-23c8-bc4c-9d2a-7bc43b50104a",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "REST API Reference",
+ "url": "https://learn.microsoft.com/rest/api/frontdoor/"
+ },
+ {
+ "name": "Client library for Java",
+ "url": "https://learn.microsoft.com/java/api/overview/azure/resourcemanager-frontdoor-readme?view=azure-java-preview"
+ },
+ {
+ "name": "SDK for Python",
+ "url": "https://learn.microsoft.com/python/api/overview/azure/front-door?view=azure-python"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "When working with Azure Front Door through APIs, ARM templates, Bicep, or SDKs, using the latest API or SDK version is crucial. Updates bring new functions, important security patches, and bug fixes.\n",
+ "pgVerified": true,
+ "description": "Use the latest API version and SDK version",
+ "potentialBenefits": "Enhanced security and features",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1ad74c3c-e3d7-0046-b83f-a2199974ef15",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Monitor metrics and logs in Azure Front Door",
+ "url": "https://learn.microsoft.com/azure/frontdoor/front-door-diagnostics?pivots=front-door-standard-premium"
+ },
+ {
+ "name": "WAF logs",
+ "url": "https://learn.microsoft.com/azure/web-application-firewall/afds/waf-front-door-monitor?pivots=front-door-standard-premium#waf-logs"
+ },
+ {
+ "name": "Configure Azure Front Door logs",
+ "url": "https://learn.microsoft.com/azure/frontdoor/standard-premium/how-to-logs"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Front Door logs offer comprehensive telemetry on each request, crucial for understanding your solution's performance and responses, especially when caching is enabled, as origin servers might not receive every request.\n",
+ "pgVerified": true,
+ "description": "Configure logs",
+ "potentialBenefits": "Enhanced insights and solution monitoring",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "d9bd6780-0d6f-cd4c-bc66-8ddcab12f3d1",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "End-to-end TLS with Azure Front Door",
+ "url": "https://learn.microsoft.com/azure/frontdoor/end-to-end-tls?pivots=front-door-standard-premium"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Front Door terminates TCP and TLS connections from clients and establishes new connections from each PoP to the origin. Securing these connections with TLS, even for Azure-hosted origins, ensures data is always encrypted during transit.\n",
+ "pgVerified": true,
+ "description": "Use end-to-end TLS",
+ "potentialBenefits": "Ensures data encryption in transit",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Use end-to-end TLS\r\ncdnresources\r\n| where type == \"microsoft.cdn/profiles/afdendpoints/routes\"\r\n| extend forwardingProtocol=tostring(properties.forwardingProtocol),supportedProtocols=properties.supportedProtocols\r\n| project id,name,forwardingProtocol,supportedProtocols,tags\r\n| where forwardingProtocol !~ \"httpsonly\" or supportedProtocols has \"http\"\r\n| project recommendationId= \"d9bd6780-0d6f-cd4c-bc66-8ddcab12f3d1\", name,id,tags,param1=strcat(\"forwardingProtocol:\",forwardingProtocol),param2=strcat(\"supportedProtocols:\",supportedProtocols)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "24ab9f11-a3e4-3043-a985-22cf94c4933a",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Create HTTP to HTTPS redirect rule",
+ "url": "https://learn.microsoft.com/azure/frontdoor/front-door-how-to-redirect-https#create-http-to-https-redirect-rule"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Using HTTPS is ideal for secure connections. However, for compatibility with older clients, HTTP requests may be necessary. Azure Front Door enables auto redirection of HTTP to HTTPS, enhancing security without sacrificing accessibility.\n",
+ "pgVerified": true,
+ "description": "Use HTTP to HTTPS redirection",
+ "potentialBenefits": "Enhances security and compliance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Use HTTP to HTTPS redirection\r\ncdnresources\r\n| where type == \"microsoft.cdn/profiles/afdendpoints/routes\"\r\n| extend httpsRedirect=tostring(properties.httpsRedirect)\r\n| project id,name,httpsRedirect,tags\r\n| where httpsRedirect !~ \"enabled\"\r\n| project recommendationId= \"24ab9f11-a3e4-3043-a985-22cf94c4933a\", name,id,tags,param1=strcat(\"httpsRedirect:\",httpsRedirect)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "29d65c41-2fad-d142-95eb-9eab95f6c0a5",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Configure HTTPS on an Azure Front Door custom domain using the Azure portal",
+ "url": "https://learn.microsoft.com/azure/frontdoor/standard-premium/how-to-configure-https-custom-domain?tabs=powershell"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "When Front Door manages your TLS certificates, it reduces your operational costs and helps you to avoid costly outages caused by forgetting to renew a certificate. Front Door automatically issues and rotates the managed TLS certificates.\n",
+ "pgVerified": true,
+ "description": "Use managed TLS certificates",
+ "potentialBenefits": "Lowers costs, avoids outages",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "4638c2c0-03de-6d42-9e09-82ee4478cbf3",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Select the certificate for Azure Front Door to deploy",
+ "url": "https://learn.microsoft.com/azure/frontdoor/standard-premium/how-to-configure-https-custom-domain?tabs=powershell#select-the-certificate-for-azure-front-door-to-deploy"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "If you use your own TLS certificates, set the Key Vault certificate version to 'Latest' to avoid reconfiguring Azure Front Door for new certificate versions and waiting for deployment across Front Door's environments.\n",
+ "pgVerified": true,
+ "description": "Use latest version for customer-managed certificates",
+ "potentialBenefits": "Saves time and automates TLS updates",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "cd6a32af-747a-e649-82a7-a98f528ca842",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Preserve the original HTTP host name between a reverse proxy and its back-end web application",
+ "url": "https://learn.microsoft.com/azure/architecture/best-practices/host-name-preservation"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Front Door can rewrite Host headers for custom domain names routing to a single origin, useful for avoiding custom domain configuration at both Front Door and the origin.\n",
+ "pgVerified": true,
+ "description": "Use the same domain name on Front Door and your origin",
+ "potentialBenefits": "Improves session/auth handling",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1bd2b7e8-400f-e64a-99a2-c572f7b08a62",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Web Application Firewall on Azure Front Door",
+ "url": "https://learn.microsoft.com/azure/frontdoor/web-application-firewall"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "For internet-facing applications, enabling the Front Door web application firewall (WAF) and configuring it to use managed rules is recommended for protection against a wide range of attacks using Microsoft-managed rules.\n",
+ "pgVerified": true,
+ "description": "Enable the WAF",
+ "potentialBenefits": "Enhances web app security",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Enable the WAF\r\n\r\nresources\r\n| where type =~ \"microsoft.cdn/profiles\" and sku has \"AzureFrontDoor\"\r\n| project name, cdnprofileid=tolower(id), tostring(tags), resourceGroup, subscriptionId,skuname=tostring(sku.name)\r\n| join kind= fullouter (\r\n cdnresources\r\n | where type == \"microsoft.cdn/profiles/securitypolicies\"\r\n | extend wafpolicyid=tostring(properties['parameters']['wafPolicy']['id'])\r\n | extend splitid=split(id, \"/\")\r\n | extend cdnprofileid=tolower(strcat_array(array_slice(splitid, 0, 8), \"/\"))\r\n | project secpolname=name, cdnprofileid, wafpolicyid\r\n )\r\n on cdnprofileid\r\n| project name, cdnprofileid, secpolname, wafpolicyid,skuname\r\n| join kind = fullouter (\r\n resources\r\n | where type == \"microsoft.network/frontdoorwebapplicationfirewallpolicies\"\r\n | extend\r\n managedrulesenabled=iff(tostring(properties.managedRules.managedRuleSets) != \"[]\", true, false),\r\n enabledState = tostring(properties.policySettings.enabledState)\r\n | project afdwafname=name, managedrulesenabled, wafpolicyid=id, enabledState, tostring(tags)\r\n )\r\n on wafpolicyid\r\n| where name != \"\"\r\n| summarize\r\n associatedsecuritypolicies=countif(secpolname != \"\"),\r\n wafswithmanagedrules=countif(managedrulesenabled == 1)\r\n by name, id=cdnprofileid, tags,skuname\r\n| where associatedsecuritypolicies == 0 or wafswithmanagedrules == 0\r\n| project\r\n recommendationId = \"1bd2b7e8-400f-e64a-99a2-c572f7b08a62\",\r\n name,\r\n id,\r\n todynamic(tags),\r\n param1 = strcat(\"associatedsecuritypolicies:\", associatedsecuritypolicies),\r\n param2 = strcat(\"wafswithmanagedrules:\", wafswithmanagedrules),\r\n param3 = strcat(\"skuname:\",skuname)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "38f3d542-6de6-a44b-86c6-97e3be690281",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Health probes",
+ "url": "https://learn.microsoft.com/azure/frontdoor/health-probes"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Front Door health probes help detect unavailable or unhealthy origins, directing traffic to alternate origins if needed.\n",
+ "pgVerified": true,
+ "description": "Disable health probes when there is only one origin in an origin group",
+ "potentialBenefits": "Reduces unnecessary origin traffic",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Disable health probes when there is only one origin in an origin group\r\ncdnresources\r\n| where type =~ \"microsoft.cdn/profiles/origingroups\"\r\n| extend healthprobe=tostring(properties.healthProbeSettings)\r\n| project origingroupname=name, id, tags, resourceGroup, subscriptionId, healthprobe\r\n| join (\r\n cdnresources\r\n | where type =~ \"microsoft.cdn/profiles/origingroups/Origins\"\r\n | extend origingroupname = tostring(properties.originGroupName)\r\n )\r\n on origingroupname\r\n| summarize origincount=count(), enabledhealthprobecount=countif(healthprobe != \"\") by origingroupname, id, tostring(tags), resourceGroup, subscriptionId\r\n| where origincount == 1 and enabledhealthprobecount != 0\r\n| project\r\n recommendationId = \"38f3d542-6de6-a44b-86c6-97e3be690281\",\r\n name=origingroupname,\r\n id,\r\n todynamic(tags),\r\n param1 = strcat(\"origincount:\", origincount),\r\n param2 = strcat(\"enabledhealthprobecount:\", enabledhealthprobecount)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5225bba3-28ec-1e43-8986-7eedfd466d65",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Health Endpoint Monitoring pattern",
+ "url": "https://learn.microsoft.com/azure/architecture/patterns/health-endpoint-monitoring"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Consider selecting a webpage or location specifically designed for health monitoring as the endpoint for Azure Front Door's health probes. This should encompass the status of critical components like application servers, databases, and caches to serve production traffic efficiently.\n",
+ "pgVerified": true,
+ "description": "Select good health probe endpoints",
+ "potentialBenefits": "Improves traffic routing and uptime",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5783defe-b49e-d947-84f7-d8677593f324",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Supported HTTP methods for health probes",
+ "url": "https://learn.microsoft.com/azure/frontdoor/health-probes#supported-http-methods-for-health-probes"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Health probes in Azure Front Door can use GET or HEAD HTTP methods. Using the HEAD method for health probes is a recommended practice because it reduces the traffic load on your origins, being less resource-intensive.\n",
+ "pgVerified": true,
+ "description": "Use HEAD health probes",
+ "potentialBenefits": "Reduces traffic load on origins",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b515690d-3bf9-3a49-8d38-188e0fd45896",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Geo filter WAF policy - GeoMatch",
+ "url": "https://learn.microsoft.com/azure/web-application-firewall/afds/waf-front-door-geo-filtering"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Azure Front Door's geo-filtering through WAF enables defining custom access rules by country/region to restrict or allow web app access.\n",
+ "pgVerified": true,
+ "description": "Use geo-filtering in Azure Front Door",
+ "potentialBenefits": "Enhanced regional access control",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1cfe7834-56ec-ff41-b11d-993734705dba",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Private link for Azure Front Door",
+ "url": "https://learn.microsoft.com/azure/frontdoor/private-link"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Azure Private Link enables secure access to Azure PaaS and services over a private endpoint in your virtual network, ensuring traffic goes over the Microsoft backbone network, not the public internet.\n",
+ "pgVerified": true,
+ "description": "Secure your Origin with Private Link in Azure Front Door",
+ "potentialBenefits": "Enhanced security and private connectivity",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Cdn/profiles",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b49a39fd-f431-4b61-9062-f2157849d845",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Compute Gallery best practices",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/azure-compute-gallery#best-practices"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Keeping a minimum of 3 replicas for production images in Azure's Compute Gallery ensures scalability and prevents throttling in multi-VM deployments by distributing VM deployments across different replicas. This reduces the risk of overloading a single replica.\n",
+ "pgVerified": true,
+ "description": "A minimum of three replicas should be kept for production image versions",
+ "potentialBenefits": "Enhances scalability and avoids throttling",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/galleries",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Query to list all image versions,its associated image name and version replica configurations per region in a compute gallery whose version replicas is less than 3\r\nresources\r\n| where type =~ \"microsoft.compute/galleries/images/versions\"\r\n| extend GalleryName = tostring(split(tostring(id), \"/\")[8]), ImageName = tostring(split(tostring(id), \"/\")[10])\r\n| mv-expand VersionReplicas = properties.publishingProfile.targetRegions\r\n| project RecommendationId=\"b49a39fd-f431-4b61-9062-f2157849d845\",name,id,tags,param1=strcat(\"GalleryName: \",GalleryName),param2=strcat(\"ImageName: \",ImageName),param3=strcat(\"VersionReplicaRegionName: \",VersionReplicas.name),param4=strcat(\"VersionReplicationCount: \",VersionReplicas.regionalReplicaCount),rc=toint(VersionReplicas.regionalReplicaCount)\r\n| where rc < 3\r\n| project-away rc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "488dcc8b-f2e3-40ce-bf95-73deb2db095f",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Compute Gallery best practices",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/azure-compute-gallery#best-practices"
+ },
+ {
+ "name": "Zone-redundant storage",
+ "url": "https://learn.microsoft.com/en-us/azure/storage/common/storage-redundancy#zone-redundant-storage"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Use ZRS for high availability when creating image/VM versions in Azure Compute Gallery, offering resilience against Availability Zone failures. ZRS accounts are advisable in regions with Availability Zones, with the choice of Standard_ZRS recommended over Standard_LRS for these regions.\n",
+ "pgVerified": true,
+ "description": "Zone redundant storage should be used for image versions",
+ "potentialBenefits": "Enhances image version availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/galleries",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Query to list all image versions and its associated image and gallery name whose Storage account type is not using ZRS\r\nresources\r\n| where type =~ \"microsoft.compute/galleries/images/versions\"\r\n| extend GalleryName = tostring(split(tostring(id), \"/\")[8]), ImageName = tostring(split(tostring(id), \"/\")[10])\r\n| extend StorageAccountType = tostring(properties.publishingProfile.storageAccountType)\r\n| where StorageAccountType !has \"ZRS\"\r\n| project RecommendationId=\"488dcc8b-f2e3-40ce-bf95-73deb2db095f\",name,id,tags,param1=strcat(\"GalleryName: \",GalleryName),param2=strcat(\"ImageName: \",ImageName),param3=strcat(\"StorageAccountType: \",StorageAccountType)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1c5e1e58-4e56-491c-8529-10f37af9d4ed",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Compute Gallery best practices",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/azure-compute-gallery#best-practices"
+ },
+ {
+ "name": "Generation 1 vs Generation 2 in Hyper-V",
+ "url": "https://learn.microsoft.com/en-us/windows-server/virtualization/hyper-v/plan/should-i-create-a-generation-1-or-2-virtual-machine-in-hyper-v"
+ },
+ {
+ "name": "Images in Compute gallery",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/shared-image-galleries?tabs=azure-cli"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "We recommend creating Trusted Launch Supported Images for benefits like Secure Boot, vTPM, trusted launch VMs, large boot volume. These are Gen 2 Images by default and you cannot change a VM's generation after creation, so review the considerations first.\n",
+ "pgVerified": true,
+ "description": "Consider creating TrustedLaunchSupported images where possible",
+ "potentialBenefits": "Enhances VM security and features",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/galleries",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Query to list all images whose Hyper-V generation is not V2\r\nresources\r\n| where type =~ \"microsoft.compute/galleries/images\"\r\n| extend VMGeneration = properties.hyperVGeneration\r\n| where VMGeneration <> 'V2'\r\n| project RecommendationId=\"1c5e1e58-4e56-491c-8529-10f37af9d4ed\",name,id,tags,param1=strcat(\"VMGeneration: \",VMGeneration)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "273f6b30-68e0-4241-85ea-acf15ffb60bf",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "What has changed with Flexible orchestration mode",
+ "url": "https://learn.microsoft.com/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-orchestration-modes#what-has-changed-with-flexible-orchestration-mode"
+ },
+ {
+ "name": "Attach or detach a Virtual Machine to or from a Virtual Machine Scale Set",
+ "url": "https://learn.microsoft.com/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-attach-detach-vm?branch=main&tabs=portal-1%2Cportal-2%2Cportal-3"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Production VM workloads should be deployed on multiple VMs and grouped in a VMSS Flex instance to intelligently distribute across the platform, minimizing the impact of platform faults and updates.\n",
+ "pgVerified": true,
+ "description": "Run production workloads on two or more VMs using VMSS Flex",
+ "potentialBenefits": "Enhanced fault/update resilience",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs that are not associated with a VMSS Flex instance\r\nresources\r\n| where type =~ 'Microsoft.Compute/virtualMachines'\r\n| where isnull(properties.virtualMachineScaleSet.id)\r\n| project recommendationId=\"273f6b30-68e0-4241-85ea-acf15ffb60bf\", name, id, tags\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "2bd0be95-a825-6f47-a8c6-3db1fb5eb387",
+ "recommendationTypeId": "066a047a-9ace-45f4-ac50-6325840a6b00",
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Create virtual machines in an availability zone using the Azure portal",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/create-portal-availability-zone?tabs=standard"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure Availability Zones, within each Azure region, are tolerant to local failures, protecting applications and data against unlikely Datacenter failures by being physically separate.\n",
+ "pgVerified": true,
+ "description": "Deploy VMs across Availability Zones",
+ "potentialBenefits": "Enhanced VM resilience to failures",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs that are not assigned to a Zone\r\nResources\r\n| where type =~ 'Microsoft.Compute/virtualMachines'\r\n| where isnull(zones)\r\n| project recommendationId=\"2bd0be95-a825-6f47-a8c6-3db1fb5eb387\", name, id, tags, param1=\"No Zone\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "a8d25876-7951-b646-b4e8-880c9031596b",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Resiliency checklist for Virtual Machines",
+ "url": "https://learn.microsoft.com/azure/architecture/checklist/resiliency-per-service#virtual-machines"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Availability sets will soon be retired. Migrate workloads from VMs to VMSS Flex for deployment across zones or within the same zone across different fault domains (FDs) and update domains (UDs) for better reliability.\n",
+ "pgVerified": true,
+ "description": "Migrate VMs using availability sets to VMSS Flex",
+ "potentialBenefits": "Enhances reliability and future-proofs VMs",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs using Availability Sets\r\nresources\r\n| where type =~ 'Microsoft.Compute/virtualMachines'\r\n| where isnotnull(properties.availabilitySet)\r\n| project recommendationId = \"a8d25876-7951-b646-b4e8-880c9031596b\", name, id, tags, param1=strcat(\"availabilitySet: \",properties.availabilitySet.id)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "cfe22a65-b1db-fd41-9e8e-d573922709ae",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Resiliency checklist for Virtual Machines",
+ "url": "https://learn.microsoft.com/azure/architecture/checklist/resiliency-per-service#virtual-machines"
+ },
+ {
+ "name": "Run a test failover (disaster recovery drill) to Azure",
+ "url": "https://learn.microsoft.com/azure/site-recovery/site-recovery-test-failover-to-azure"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Replicating Azure VMs via Site Recovery entails continuous, asynchronous disk replication to a target region. Recovery points are generated every few minutes, ensuring a Recovery Point Objective (RPO) in minutes.\n",
+ "pgVerified": true,
+ "description": "Replicate VMs using Azure Site Recovery",
+ "potentialBenefits": "Minimize downtime in disasters",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs that do NOT have replication with ASR enabled\r\nresources\r\n| where type =~ \"Microsoft.Compute/virtualMachines\"\r\n| extend securityType = iif(isnull(properties.securityProfile.securityType), \"Standard\", properties.securityProfile.securityType)\r\n| where securityType !in~ (\"TrustedLaunch\", \"ConfidentialVM\")\r\n| project id, vmIdForJoin = tolower(id), name, tags\r\n| join kind = leftouter (\r\n recoveryservicesresources\r\n | where type =~ \"Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectedItems\"\r\n and properties.providerSpecificDetails.dataSourceInfo.datasourceType =~ \"AzureVm\"\r\n | project vmResourceId = tolower(properties.providerSpecificDetails.dataSourceInfo.resourceId)\r\n )\r\n on $left.vmIdForJoin == $right.vmResourceId\r\n| where isempty(vmResourceId)\r\n| project recommendationId = \"cfe22a65-b1db-fd41-9e8e-d573922709ae\", name, id, tags\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "122d11d7-b91f-8747-a562-f56b79bcfbdc",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Migrate your Azure unmanaged disks by Sep 30, 2025",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/unmanaged-disks-deprecation"
+ },
+ {
+ "name": "Migrate Windows VM from unmanaged disks to managed disks",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/windows/convert-unmanaged-to-managed-disks"
+ },
+ {
+ "name": "Migrate Linux VM from unmanaged disks to managed disks",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/linux/convert-unmanaged-to-managed-disks"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure is retiring unmanaged disks on September 30, 2025. Users should plan the migration to avoid disruptions and maintain service reliability.\n",
+ "pgVerified": true,
+ "description": "Use Managed Disks for VM disks",
+ "potentialBenefits": "Avoid retirement disruption, enhance reliability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs that are not using Managed Disks\r\nResources\r\n| where type =~ 'Microsoft.Compute/virtualMachines'\r\n| where isnull(properties.storageProfile.osDisk.managedDisk)\r\n| project recommendationId = \"122d11d7-b91f-8747-a562-f56b79bcfbdc\", name, id, tags\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "4ea2878f-0d69-8d4a-b715-afc10d1e538e",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Introduction to Azure managed disks - Data disks",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/managed-disks-overview#data-disk"
+ },
+ {
+ "name": "Azure managed disk types",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/disks-types"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "A data disk is a managed disk attached to a virtual machine for storing database or other essential data. These disks are SCSI drives labeled as per choice.\n",
+ "pgVerified": true,
+ "description": "Host database data on a data disk",
+ "potentialBenefits": "Enhances performance, recovery, migration flexibility",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs that only have OS Disk\r\nResources\r\n| where type =~ 'Microsoft.Compute/virtualMachines'\r\n| where array_length(properties.storageProfile.dataDisks) < 1\r\n| project recommendationId = \"4ea2878f-0d69-8d4a-b715-afc10d1e538e\", name, id, tags\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1981f704-97b9-b645-9c57-33f8ded9261a",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "What is the Azure Backup service?",
+ "url": "https://learn.microsoft.com/azure/backup/backup-overview"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Enable backups for your virtual machines with Azure Backup to secure and quickly recover your data. This service offers simple, secure, and cost-effective solutions for backing up and recovering data from the Microsoft Azure cloud.\n",
+ "pgVerified": true,
+ "description": "Backup VMs with Azure Backup service",
+ "potentialBenefits": "Secure data recovery and backup",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs that do NOT have Backup enabled\r\n// Run query to see results.\r\nresources\r\n| where type =~ 'Microsoft.Compute/virtualMachines'\r\n| project name, id, tags\r\n| join kind=leftouter (\r\n recoveryservicesresources\r\n | where type =~ 'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems'\r\n | where properties.dataSourceInfo.datasourceType =~ 'Microsoft.Compute/virtualMachines'\r\n | project idBackupEnabled=properties.sourceResourceId\r\n | extend name=strcat_array(array_slice(split(idBackupEnabled, '/'), 8, -1), '/')\r\n) on name\r\n| where isnull(idBackupEnabled)\r\n| project-away idBackupEnabled\r\n| project-away name1\r\n| project recommendationId = \"1981f704-97b9-b645-9c57-33f8ded9261a\", name, id, tags\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "98b334c0-8578-6046-9e43-b6e8fce6318e",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "States and billing status of Azure Virtual Machines",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/states-billing?context=%2Ftroubleshoot%2Fazure%2Fvirtual-machines%2Fcontext%2Fcontext#power-states-and-billing"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Azure Virtual Machines (VM) instances have various states, like provisioning and power states. A non-running VM may indicate issues or it being unnecessary, suggesting removal could help cut costs.\n",
+ "pgVerified": true,
+ "description": "Review VMs in stopped state",
+ "potentialBenefits": "Reduce costs by removing unused VMs",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs that are NOT running\r\nResources\r\n| where type =~ 'Microsoft.Compute/virtualMachines'\r\n| where properties.extended.instanceView.powerState.displayStatus != 'VM running'\r\n| project recommendationId = \"98b334c0-8578-6046-9e43-b6e8fce6318e\", name, id, tags\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "dfedbeb1-1519-fc47-86a5-52f96cf07105",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Accelerated Networking (AccelNet) overview",
+ "url": "https://learn.microsoft.com/azure/virtual-network/accelerated-networking-overview"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Accelerated networking enables SR-IOV to a VM, greatly improving its networking performance by bypassing the host from the data path, which reduces latency, jitter, and CPU utilization for demanding network workloads on supported VM types.\n",
+ "pgVerified": true,
+ "description": "Enable Accelerated Networking (AccelNet)",
+ "potentialBenefits": "Reduces latency, jitter and CPU use",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VM NICs that do not have Accelerated Networking enabled\r\nresources\r\n| where type =~ 'Microsoft.Compute/virtualMachines'\r\n| mv-expand nic = properties.networkProfile.networkInterfaces\r\n| project name, id, tags, lowerCaseNicId = tolower(nic.id), vmSize = tostring(properties.hardwareProfile.vmSize)\r\n| join kind = inner (\r\n resources\r\n | where type =~ 'Microsoft.Network/networkInterfaces'\r\n | where properties.enableAcceleratedNetworking == false\r\n | project nicName = split(id, \"/\")[8], lowerCaseNicId = tolower(id)\r\n )\r\n on lowerCaseNicId\r\n| summarize nicNames = make_set(nicName) by name, id, tostring(tags), vmSize\r\n| extend param1 = strcat(\"NicName: \", strcat_array(nicNames, \", \")), param2 = strcat(\"VMSize: \", vmSize)\r\n| project recommendationId = \"dfedbeb1-1519-fc47-86a5-52f96cf07105\", name, id, tags, param1, param2\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "73d1bb04-7d3e-0d47-bc0d-63afe773b5fe",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Accelerated Networking (AccelNet) overview",
+ "url": "https://learn.microsoft.com/azure/virtual-network/accelerated-networking-overview"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "When Accelerated Networking is enabled, the default Azure VNet interface in GuestOS is swapped for a Mellanox, and its driver comes from a 3rd party. Marketplace images have the latest Mellanox drivers, but post-deployment, updating the driver is the user's responsibility.\n",
+ "pgVerified": true,
+ "description": "When AccelNet is enabled, you must manually update the GuestOS NIC driver",
+ "potentialBenefits": "Enhanced VM network efficiency",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1f629a30-c9d0-d241-82ee-6f2eb9d42cb4",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Use Source Network Address Translation (SNAT) for outbound connections",
+ "url": "https://learn.microsoft.com/azure/load-balancer/load-balancer-outbound-connections"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "For outbound internet connectivity of Virtual Machines, using NAT Gateway or Azure Firewall is recommended to enhance security and service resilience, thanks to their higher availability and SNAT ports.\n",
+ "pgVerified": true,
+ "description": "VMs should not have a Public IP directly associated",
+ "potentialBenefits": "Enhanced security and service resiliency",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs with PublicIPs directly associated with them\r\nResources\r\n| where type =~ 'Microsoft.Compute/virtualMachines'\r\n| where isnotnull(properties.networkProfile.networkInterfaces)\r\n| mv-expand nic=properties.networkProfile.networkInterfaces\r\n| project name, id, tags, nicId = nic.id\r\n| extend nicId = tostring(nicId)\r\n| join kind=inner (\r\n Resources\r\n | where type =~ 'Microsoft.Network/networkInterfaces'\r\n | where isnotnull(properties.ipConfigurations)\r\n | mv-expand ipconfig=properties.ipConfigurations\r\n | extend publicIp = tostring(ipconfig.properties.publicIPAddress.id)\r\n | where publicIp != \"\"\r\n | project name, nicId = tostring(id), publicIp\r\n) on nicId\r\n| project recommendationId = \"1f629a30-c9d0-d241-82ee-6f2eb9d42cb4\", name, id, tags\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "82b3cf6b-9ae2-2e44-b193-10793213f676",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "How network security groups filter network traffic",
+ "url": "https://learn.microsoft.com/azure/virtual-network/network-security-group-how-it-works#intra-subnet-traffic"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Unless you have a specific reason, it's advised to associate a network security group to a subnet or a network interface, but not both, to avoid unexpected communication issues and troubleshooting due to potential rule conflicts between the two associations.\n",
+ "pgVerified": true,
+ "description": "VM network interfaces and associated subnets both have a Network Security Group (NSG) associated",
+ "potentialBenefits": "Reduces communication problems",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// 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.\r\nResources\r\n| where type =~ 'Microsoft.Network/networkInterfaces'\r\n| where isnotnull(properties.networkSecurityGroup)\r\n| mv-expand ipConfigurations = properties.ipConfigurations, nsg = properties.networkSecurityGroup\r\n| project nicId = tostring(id), subnetId = tostring(ipConfigurations.properties.subnet.id), nsgName=split(nsg.id, '/')[8]\r\n| parse kind=regex subnetId with '/virtualNetworks/' virtualNetwork '/subnets/' subnet\r\n | join kind=inner (\r\n Resources\r\n | where type =~ 'Microsoft.Network/NetworkSecurityGroups' and isnotnull(properties.subnets)\r\n | project name, resourceGroup, subnet=properties.subnets\r\n | mv-expand subnet\r\n | project subnetId=tostring(subnet.id)\r\n ) on subnetId\r\n | project nicId\r\n| join kind=leftouter (\r\n Resources\r\n | where type =~ 'Microsoft.Compute/virtualMachines'\r\n | where isnotnull(properties.networkProfile.networkInterfaces)\r\n | mv-expand nic=properties.networkProfile.networkInterfaces\r\n | project vmName = name, vmId = id, tags, nicId = nic.id, nicName=split(nic.id, '/')[8]\r\n | extend nicId = tostring(nicId)\r\n) on nicId\r\n| project recommendationId = \"82b3cf6b-9ae2-2e44-b193-10793213f676\", name=vmName, id = vmId, tags, param1 = strcat(\"nic-name=\", nicName)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "41a22a5e-5e08-9647-92d0-2ffe9ef1bdad",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Enable or disable IP forwarding",
+ "url": "https://learn.microsoft.com/azure/virtual-network/virtual-network-network-interface?tabs=network-interface-portal#enable-or-disable-ip-forwarding"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "IP forwarding allows a virtual machine network interface to receive and send network traffic not destined for or originating from its assigned IP addresses.\n",
+ "pgVerified": true,
+ "description": "IP Forwarding should only be enabled for Network Virtual Appliances",
+ "potentialBenefits": "Enhances network appliance function",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VM NICs that have IPForwarding enabled. This feature is usually only required for Network Virtual Appliances\r\nResources\r\n| where type =~ 'Microsoft.Compute/virtualMachines'\r\n| where isnotnull(properties.networkProfile.networkInterfaces)\r\n| mv-expand nic=properties.networkProfile.networkInterfaces\r\n| project name, id, tags, nicId = nic.id\r\n| extend nicId = tostring(nicId)\r\n| join kind=inner (\r\n Resources\r\n | where type =~ 'Microsoft.Network/networkInterfaces'\r\n | where properties.enableIPForwarding == true\r\n | project nicId = tostring(id)\r\n) on nicId\r\n| project recommendationId = \"41a22a5e-5e08-9647-92d0-2ffe9ef1bdad\", name, id, tags\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1cf8fe21-9593-1e4e-966b-779a294c0d30",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Name resolution for resources in Azure virtual networks",
+ "url": "https://learn.microsoft.com/azure/virtual-network/virtual-networks-name-resolution-for-vms-and-role-instances"
+ }
+ ],
+ "recommendationControl": "Other Best Practices",
+ "longDescription": "Configure the DNS Server at the Virtual Network level to prevent any inconsistency across the environment.\n",
+ "pgVerified": true,
+ "description": "Customer DNS Servers should be configured in the Virtual Network level",
+ "potentialBenefits": "Ensures DNS consistency",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VM NICs that have DNS Server settings configured in any of the NICs\r\nResources\r\n| where type =~ 'Microsoft.Compute/virtualMachines'\r\n| where isnotnull(properties.networkProfile.networkInterfaces)\r\n| mv-expand nic=properties.networkProfile.networkInterfaces\r\n| project name, id, tags, nicId = nic.id\r\n| extend nicId = tostring(nicId)\r\n| join kind=inner (\r\n Resources\r\n | where type =~ 'Microsoft.Network/networkInterfaces'\r\n | project name, id, dnsServers = properties.dnsSettings.dnsServers\r\n | extend hasDns = array_length(dnsServers) >= 1\r\n | where hasDns != 0\r\n | project name, nicId = tostring(id)\r\n) on nicId\r\n| project recommendationId = \"1cf8fe21-9593-1e4e-966b-779a294c0d30\", name, id, tags\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "3263a64a-c256-de48-9818-afd3cbc55c2a",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Shared Disk Introduction",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/disks-shared"
+ },
+ {
+ "name": "Enable Shared Disks",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/disks-shared-enable?tabs=azure-portal"
+ }
+ ],
+ "recommendationControl": "Other Best Practices",
+ "longDescription": "Azure shared disks let you attach a disk to multiple VMs at once for deploying or migrating clustered applications, suitable only when a disk is shared among VM cluster members.\n",
+ "pgVerified": true,
+ "description": "Shared disks should only be enabled in clustered servers",
+ "potentialBenefits": "Enhances clustered server performance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// 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\r\nresources\r\n| where type =~ 'Microsoft.Compute/disks'\r\n| where isnotnull(properties.maxShares)\r\n| project id, name, tags, lowerCaseDiskId = tolower(id), diskState = tostring(properties.diskState)\r\n| join kind = leftouter (\r\n resources\r\n | where type =~ 'Microsoft.Compute/virtualMachines'\r\n | project osDiskVmName = name, lowerCaseOsDiskId = tolower(properties.storageProfile.osDisk.managedDisk.id)\r\n | join kind = fullouter (\r\n resources\r\n | where type =~ 'Microsoft.Compute/virtualMachines'\r\n | mv-expand dataDisks = properties.storageProfile.dataDisks\r\n | project dataDiskVmName = name, lowerCaseDataDiskId = tolower(dataDisks.managedDisk.id)\r\n )\r\n on $left.lowerCaseOsDiskId == $right.lowerCaseDataDiskId\r\n | project lowerCaseDiskId = coalesce(lowerCaseOsDiskId, lowerCaseDataDiskId), vmName = coalesce(osDiskVmName, dataDiskVmName)\r\n )\r\n on lowerCaseDiskId\r\n| summarize vmNames = make_set(vmName) by name, id, tostring(tags), diskState\r\n| extend param1 = strcat(\"DiskState: \", diskState), param2 = iif(isempty(vmNames[0]), \"VMName: n/a\", strcat(\"VMName: \", strcat_array(vmNames, \", \")))\r\n| project recommendationId = \"3263a64a-c256-de48-9818-afd3cbc55c2a\", name, id, tags, param1, param2\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "70b1d2be-e6c4-b54e-9959-b1b690f9e485",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Restrict import/export access for managed disks using Azure Private Link",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/disks-enable-private-links-for-import-export-portal"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Recommended changing to \"Disable public access and enable private access\" and creating a Private Endpoint to improve security by restricting direct public access and ensuring connections are made privately, enhancing data protection and minimizing potential external threats.\n",
+ "pgVerified": true,
+ "description": "Network access to the VM disk should be set to Disable public access and enable private access",
+ "potentialBenefits": "Enhances VM security and privacy",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Disks with \"Enable public access from all networks\" enabled\r\nresources\r\n| where type =~ 'Microsoft.Compute/disks'\r\n| where properties.publicNetworkAccess == \"Enabled\"\r\n| project id, name, tags, lowerCaseDiskId = tolower(id)\r\n| join kind = leftouter (\r\n resources\r\n | where type =~ 'Microsoft.Compute/virtualMachines'\r\n | project osDiskVmName = name, lowerCaseOsDiskId = tolower(properties.storageProfile.osDisk.managedDisk.id)\r\n | join kind = fullouter (\r\n resources\r\n | where type =~ 'Microsoft.Compute/virtualMachines'\r\n | mv-expand dataDisks = properties.storageProfile.dataDisks\r\n | project dataDiskVmName = name, lowerCaseDataDiskId = tolower(dataDisks.managedDisk.id)\r\n )\r\n on $left.lowerCaseOsDiskId == $right.lowerCaseDataDiskId\r\n | project lowerCaseDiskId = coalesce(lowerCaseOsDiskId, lowerCaseDataDiskId), vmName = coalesce(osDiskVmName, dataDiskVmName)\r\n )\r\n on lowerCaseDiskId\r\n| summarize vmNames = make_set(vmName) by name, id, tostring(tags)\r\n| extend param1 = iif(isempty(vmNames[0]), \"VMName: n/a\", strcat(\"VMName: \", strcat_array(vmNames, \", \")))\r\n| project recommendationId = \"70b1d2be-e6c4-b54e-9959-b1b690f9e485\", name, id, tags, param1\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "c42343ae-2712-2843-a285-3437eb0b28a1",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Policy-driven governance",
+ "url": "https://learn.microsoft.com/azure/cloud-adoption-framework/ready/landing-zone/design-principles#policy-driven-governance"
+ },
+ {
+ "name": "Azure Policy Regulatory Compliance controls for Azure Virtual Machines",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/security-policy"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Keeping your virtual machine (VM) secure is crucial for the applications you run. This involves using various Azure services and features to ensure secure access to your VMs and the secure storage of your data, aiming for overall security of your VM and applications.\n",
+ "pgVerified": true,
+ "description": "Ensure that your VMs are compliant with Azure Policies",
+ "potentialBenefits": "Secure VMs and applications",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs in \"Non-compliant\" state with Azure Policies\r\npolicyresources\r\n| where type =~ \"Microsoft.PolicyInsights/policyStates\" and properties.resourceType =~ \"Microsoft.Compute/virtualMachines\" and properties.complianceState =~ \"NonCompliant\"\r\n| project\r\n policyDefinitionId = tolower(properties.policyDefinitionId),\r\n policyAssignmentId = tolower(properties.policyAssignmentId),\r\n targetResourceId = tolower(properties.resourceId)\r\n// Join the policy definition details\r\n| join kind = leftouter (\r\n policyresources\r\n | where type =~ \"Microsoft.Authorization/policyDefinitions\"\r\n | project policyDefinitionId = tolower(id), policyDefinitionDisplayName = properties.displayName\r\n )\r\n on policyDefinitionId\r\n| project policyDefinitionId, policyDefinitionDisplayName, policyAssignmentId, targetResourceId\r\n// Join the policy assignment details\r\n| join kind = leftouter (\r\n policyresources\r\n | where type =~ \"Microsoft.Authorization/policyAssignments\"\r\n | project policyAssignmentId = tolower(id), policyAssignmentDisplayName = properties.displayName\r\n )\r\n on policyAssignmentId\r\n| project policyDefinitionId, policyDefinitionDisplayName, policyAssignmentId, policyAssignmentDisplayName, targetResourceId\r\n// Join the target resource details\r\n| join kind = leftouter (\r\n resources\r\n | where type =~ \"Microsoft.Compute/virtualMachines\"\r\n | project targetResourceId = tolower(id), targetResourceIdPreservedCase = id, targetResourceName = name, targetResourceTags = tags\r\n )\r\n on targetResourceId\r\n| project\r\n recommendationId = \"c42343ae-2712-2843-a285-3437eb0b28a1\",\r\n name = targetResourceName,\r\n id = targetResourceIdPreservedCase,\r\n tags = targetResourceTags,\r\n param1 = strcat(\"DefinitionName: \", policyDefinitionDisplayName),\r\n param2 = strcat(\"DefinitionID: \", policyDefinitionId),\r\n param3 = strcat(\"AssignmentName: \", policyAssignmentDisplayName),\r\n param4 = strcat(\"AssignmentID: \", policyAssignmentId)\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "f0a97179-133a-6e4f-8a49-8a44da73ffce",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Overview of managed disk encryption options",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/disk-encryption-overview"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Consider enabling Azure Disk Encryption (ADE) for encrypting Azure VM disks using DM-Crypt (Linux) or BitLocker (Windows). Additionally, consider Encryption at host and Confidential disk encryption for enhanced data security.\n",
+ "pgVerified": true,
+ "description": "Enable advanced encryption options for your managed disks",
+ "potentialBenefits": "Enhances data security and integrity",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b72214bb-e879-5f4b-b9cd-642db84f36f4",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Overview of VM insights",
+ "url": "https://learn.microsoft.com/azure/azure-monitor/vm/vminsights-overview"
+ },
+ {
+ "name": "Did the extension install properly?",
+ "url": "https://learn.microsoft.com/azure/azure-monitor/vm/vminsights-troubleshoot#did-the-extension-install-properly"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "VM Insights monitors VM and scale set performance, health, running processes, and dependencies. It enhances the predictability of application performance and availability by pinpointing performance bottlenecks and network issues, and it clarifies if problems are related to other dependencies.\n",
+ "pgVerified": true,
+ "description": "Enable VM Insights",
+ "potentialBenefits": "Improves VM performance and health",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Check for VMs without Azure Monitoring Agent extension installed, missing Data Collection Rule or Data Collection Rule without performance enabled.\r\nResources\r\n| where type == 'microsoft.compute/virtualmachines'\r\n| project idVm = tolower(id), name, tags\r\n| join kind=leftouter (\r\n InsightsResources\r\n | where type =~ \"Microsoft.Insights/dataCollectionRuleAssociations\" and id has \"Microsoft.Compute/virtualMachines\"\r\n | project idDcr = tolower(properties.dataCollectionRuleId), idVmDcr = tolower(substring(id, 0, indexof(id, \"/providers/Microsoft.Insights/dataCollectionRuleAssociations/\"))))\r\non $left.idVm == $right.idVmDcr\r\n| join kind=leftouter (\r\n Resources\r\n | where type =~ \"Microsoft.Insights/dataCollectionRules\"\r\n | extend\r\n isPerformanceEnabled = iif(properties.dataSources.performanceCounters contains \"Microsoft-InsightsMetrics\" and properties.dataFlows contains \"Microsoft-InsightsMetrics\", true, false),\r\n isMapEnabled = iif(properties.dataSources.extensions contains \"Microsoft-ServiceMap\" and properties.dataSources.extensions contains \"DependencyAgent\" and properties.dataFlows contains \"Microsoft-ServiceMap\", true, false)//,\r\n | where isPerformanceEnabled or isMapEnabled\r\n | project dcrName = name, isPerformanceEnabled, isMapEnabled, idDcr = tolower(id))\r\non $left.idDcr == $right.idDcr\r\n| join kind=leftouter (\r\n Resources\r\n | where type == 'microsoft.compute/virtualmachines/extensions' and (name contains 'AzureMonitorWindowsAgent' or name contains 'AzureMonitorLinuxAgent')\r\n | extend idVmExtension = tolower(substring(id, 0, indexof(id, '/extensions'))), extensionName = name)\r\non $left.idVm == $right.idVmExtension\r\n| where isPerformanceEnabled != 1 or (extensionName != 'AzureMonitorWindowsAgent' and extensionName != 'AzureMonitorLinuxAgent')\r\n| project recommendationId = \"b72214bb-e879-5f4b-b9cd-642db84f36f4\", name, id = idVm, tags, param1 = strcat('MonitoringExtension:', extensionName), param2 = strcat('DataCollectionRuleId:', idDcr), param3 = strcat('isPerformanceEnabled:', isPerformanceEnabled)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "4a9d8973-6dba-0042-b3aa-07924877ebd5",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Monitor Agent overview",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-monitor/agents/agents-overview"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Azure Monitor Metrics automatically receives platform metrics, but platform logs, which offer detailed diagnostics and auditing for resources and their Azure platform, need to be manually routed for collection.\n",
+ "pgVerified": true,
+ "description": "Configure monitoring for all Azure Virtual Machines",
+ "potentialBenefits": "Enhanced diagnostics and auditing capability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Virtual Machines without diagnostic settings enabled/with diagnostic settings enabled but not configured both performance counters and event logs/syslogs.\r\nresources\r\n| where type =~ \"microsoft.compute/virtualmachines\"\r\n| project name, id, tags, lowerCaseVmId = tolower(id)\r\n| join kind = leftouter (\r\n resources\r\n | where type =~ \"Microsoft.Compute/virtualMachines/extensions\" and properties.publisher =~ \"Microsoft.Azure.Diagnostics\"\r\n | project\r\n lowerCaseVmIdOfExtension = tolower(substring(id, 0, indexof(id, \"/extensions/\"))),\r\n extensionType = properties.type,\r\n provisioningState = properties.provisioningState,\r\n storageAccount = properties.settings.StorageAccount,\r\n // Windows\r\n wadPerfCounters = properties.settings.WadCfg.DiagnosticMonitorConfiguration.PerformanceCounters.PerformanceCounterConfiguration,\r\n wadEventLogs = properties.settings.WadCfg.DiagnosticMonitorConfiguration.WindowsEventLog,\r\n // Linux\r\n ladPerfCounters = properties.settings.ladCfg.diagnosticMonitorConfiguration.performanceCounters.performanceCounterConfiguration,\r\n ladSyslog = properties.settings.ladCfg.diagnosticMonitorConfiguration.syslogEvents\r\n | extend\r\n // Windows\r\n isWadPerfCountersConfigured = iif(array_length(wadPerfCounters) > 0, true, false),\r\n isWadEventLogsConfigured = iif(isnotnull(wadEventLogs) and array_length(wadEventLogs.DataSource) > 0, true, false),\r\n // Linux\r\n isLadPerfCountersConfigured = iif(array_length(ladPerfCounters) > 0, true, false),\r\n isLadSyslogConfigured = isnotnull(ladSyslog)\r\n | project\r\n lowerCaseVmIdOfExtension,\r\n extensionType,\r\n provisioningState,\r\n storageAccount,\r\n isPerfCountersConfigured = case(extensionType =~ \"IaaSDiagnostics\", isWadPerfCountersConfigured, extensionType =~ \"LinuxDiagnostic\", isLadPerfCountersConfigured, false),\r\n isEventLogsConfigured = case(extensionType =~ \"IaaSDiagnostics\", isWadEventLogsConfigured, extensionType =~ \"LinuxDiagnostic\", isLadSyslogConfigured, false)\r\n )\r\n on $left.lowerCaseVmId == $right.lowerCaseVmIdOfExtension\r\n| where isempty(lowerCaseVmIdOfExtension) or provisioningState !~ \"Succeeded\" or not(isPerfCountersConfigured and isEventLogsConfigured)\r\n| extend\r\n param1 = strcat(\"DiagnosticSetting: \", iif(isnotnull(extensionType), strcat(\"Enabled, partially configured (\", extensionType, \")\"), \"Not enabled\")),\r\n param2 = strcat(\"ProvisioningState: \", iif(isnotnull(provisioningState), provisioningState, \"n/a\")),\r\n param3 = strcat(\"storageAccount: \", iif(isnotnull(storageAccount), storageAccount, \"n/a\")),\r\n param4 = strcat(\"PerformanceCounters: \", case(isnull(isPerfCountersConfigured), \"n/a\", isPerfCountersConfigured, \"Configured\", \"Not configured\")),\r\n param5 = strcat(\"EventLogs/Syslogs: \", case(isnull(isEventLogsConfigured), \"n/a\", isEventLogsConfigured, \"Configured\", \"Not configured\"))\r\n| project recommendationId = \"4a9d8973-6dba-0042-b3aa-07924877ebd5\", name, id, tags, param1, param2, param3, param4, param5\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "52ab9e5c-eec0-3148-8bd7-b6dd9e1be870",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Use maintenance configurations to control and manage the VM updates",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/maintenance-configurations"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "The maintenance configuration settings let users schedule and manage updates, making sure the updates or interruptions on the VM are performed within a planned timeframe.\n",
+ "pgVerified": true,
+ "description": "Use maintenance configurations for the VMs",
+ "potentialBenefits": "Scheduled updates for VMs",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find VMS that do not have maintenance configuration assigned\r\nResources\r\n| extend resourceId = tolower(id)\r\n| project name, location, type, id, tags, resourceId, properties\r\n| where type =~ 'Microsoft.Compute/virtualMachines'\r\n| join kind=leftouter (\r\nmaintenanceresources\r\n| where type =~ \"microsoft.maintenance/configurationassignments\"\r\n| project planName = name, type, maintenanceProps = properties\r\n| extend resourceId = tostring(maintenanceProps.resourceId)\r\n) on resourceId\r\n| where isnull(maintenanceProps)\r\n| project recommendationId = \"52ab9e5c-eec0-3148-8bd7-b6dd9e1be870\",name, id, tags\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "3201dba8-d1da-4826-98a4-104066545170",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "B-series burstable virtual machine sizes",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/sizes-b-series-burstable"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "A-series VMs are tailored for entry-level workloads like development and testing, including use cases such as development and test servers, low traffic web servers, and small to medium databases.\n",
+ "pgVerified": true,
+ "description": "Don't use A or B-Series VMs for production needing constant full CPU performance",
+ "potentialBenefits": "Ensures full CPU usage for heavy tasks",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs using A or B series families\r\nresources\r\n| where type == 'microsoft.compute/virtualmachines'\r\n| where properties.hardwareProfile.vmSize contains \"Standard_B\" or properties.hardwareProfile.vmSize contains \"Standard_A\"\r\n| project recommendationId = \"3201dba8-d1da-4826-98a4-104066545170\", name, id, tags, param1=strcat(\"vmSku: \" , properties.hardwareProfile.vmSize)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "df0ff862-814d-45a3-95e4-4fad5a244ba6",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Disk type comparison and decision tree",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/disks-types#disk-type-comparison"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Compared to Standard HDD and SSD, Premium SSD, SSDv2, and Ultra SSDs offer improved performance, configurability, and higher single-instance Virtual Machine uptime SLAs. The lowest SLA of all disks on a Virtual Machine applies, so it is best to use Premium or Ultra Disks for the highest uptime SLA.\n",
+ "pgVerified": true,
+ "description": "Mission Critical Workloads should consider using Premium or Ultra Disks",
+ "potentialBenefits": "Enhanced performance, cost efficiency, and uptime SLA",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs that have an attached disk that is not in the Premium or Ultra sku tier.\r\n\r\nresources\r\n| where type =~ 'Microsoft.Compute/virtualMachines'\r\n| extend lname = tolower(name)\r\n| join kind=leftouter(resources\r\n | where type =~ 'Microsoft.Compute/disks'\r\n | where not(sku.tier =~ 'Premium') and not(sku.tier =~ 'Ultra')\r\n | extend lname = tolower(tostring(split(managedBy, '/')[8]))\r\n | project lname, name\r\n | summarize disks = make_list(name) by lname) on lname\r\n| where isnotnull(disks)\r\n| project recommendationId = \"df0ff862-814d-45a3-95e4-4fad5a244ba6\", name, id, tags, param1=strcat(\"AffectedDisks: \", disks)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "9ab499d8-8844-424d-a2d4-8f53690eb8f8",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Microsoft Azure Boost",
+ "url": "https://learn.microsoft.com/azure/azure-boost/overview"
+ },
+ {
+ "name": "Announcing the general availability of Azure Boost",
+ "url": "https://aka.ms/AzureBoostGABlog"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "If the workload is Maintenance sensitive, consider Azure Boost compatible VMs. Azure Boost is designed to lessen the impact on customers when Azure maintenance activities occur on the host, and the current list of compatible VM sizes are documented in the first link below.\n",
+ "pgVerified": true,
+ "description": "Use Azure Boost VMs for Maintenance sensitive workload",
+ "potentialBenefits": "Less maintenance impact",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "2de8fa5e-14f4-4c4c-857f-1520f87a629f",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Monitor scheduled events for your Azure VMs",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/windows/scheduled-event-service"
+ },
+ {
+ "name": "Azure Metadata Service Scheduled Events for Linux VMs",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/linux/scheduled-events"
+ },
+ {
+ "name": "Azure Metadata Service Scheduled Events for Windows VMs",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/windows/scheduled-events"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "If your workload is Maintenance sensitive, enable Scheduled Events. This Azure Metadata Service lets your app prepare for virtual machine maintenance by providing information on upcoming events like reboots, reducing disruptions.\n",
+ "pgVerified": true,
+ "description": "Enable Scheduled Events for Maintenance sensitive workload VMs",
+ "potentialBenefits": "Minimize downtime for VMs",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "fa0cf4f5-0b21-47b7-89a9-ee936f193ce1",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Redundancy options for managed disks",
+ "url": "https://aka.ms/zrsdisksdoc"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure disks offers a zone-redundant storage (ZRS) option for workloads that need to be resilient to an entire zone being down. Due to the cross-zone data replication, ZRS disks have higher write latency when compared to the locally-redundant option (LRS), so make sure to benchmark your disks.\n",
+ "pgVerified": true,
+ "description": "Use ZRS Disks or Protect LRS Disks from Availability Zone Failure",
+ "potentialBenefits": "Enhanced Disk resilience to failures",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find eligible Disks that are not zonal nor zone redundant\r\nresources\r\n| where type == 'microsoft.compute/disks'\r\n| where sku has \"Premium_LRS\" or sku has \"StandardSSD_LRS\"\r\n| where sku.name has_cs 'ZRS' or array_length(zones) > 0\r\n| project recommendationId=\"fa0cf4f5-0b21-47b7-89a9-ee936f193ce1\", name, id, tags, param1 = sku, param2 = sku.name\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e7495e1c-0c75-0946-b266-b429b5c7f3bf",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "When to use VMSS instead of VMs",
+ "url": "https://learn.microsoft.com/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-design-overview#when-to-use-scale-sets-instead-of-virtual-machines"
+ },
+ {
+ "name": "Azure Well-Architected Framework review - Virtual Machines and Scale Sets",
+ "url": "https://learn.microsoft.com/azure/well-architected/services/compute/virtual-machines/virtual-machines-review"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Deploying even single instance VMs into a scale set with Flexible orchestration mode future-proofs applications for scaling and availability. This mode guarantees high availability (up to 1000 VMs) by distributing VMs across fault domains in a region or within an Availability Zone.\n",
+ "pgVerified": true,
+ "description": "Deploy VMSS with Flex orchestration mode instead of Uniform",
+ "potentialBenefits": "Higher scalability and availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachineScaleSets",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all zonal VMs that are NOT deployed with Flex orchestration mode\r\nresources\r\n| where type == \"microsoft.compute/virtualmachinescalesets\"\r\n| where properties.orchestrationMode != \"Flexible\"\r\n| project recommendationId = \"e7495e1c-0c75-0946-b266-b429b5c7f3bf\", name, id, tags, param1 = strcat(\"orchestrationMode: \", tostring(properties.orchestrationMode))\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "94794d2a-eff0-2345-9b67-6f9349d0a627",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Using Application Health extension with Virtual Machine Scale Sets",
+ "url": "https://learn.microsoft.com/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-health-extension?tabs=rest-api"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Monitoring application health in Azure Virtual Machine Scale Sets is crucial for deployment management. It supports rolling upgrades such as automatic OS-image upgrades and VM guest patching, leveraging health monitoring for upgrading.\n",
+ "pgVerified": true,
+ "description": "Enable VMSS application health monitoring",
+ "potentialBenefits": "Enhances deployment management and upgrades",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachineScaleSets",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs that do NOT have health monitoring enabled\r\nresources\r\n| where type == \"microsoft.compute/virtualmachinescalesets\"\r\n| join kind=leftouter (\r\n resources\r\n | where type == \"microsoft.compute/virtualmachinescalesets\"\r\n | mv-expand extension=properties.virtualMachineProfile.extensionProfile.extensions\r\n | where extension.properties.type in ( \"ApplicationHealthWindows\", \"ApplicationHealthLinux\" )\r\n | project id\r\n) on id\r\n| where id1 == \"\"\r\n| project recommendationId = \"94794d2a-eff0-2345-9b67-6f9349d0a627\", name, id, tags, param1 = \"extension: null\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "820f4743-1f94-e946-ae0b-45efafd87962",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Automatic instance repairs for Azure Virtual Machine Scale Sets",
+ "url": "https://learn.microsoft.com/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-automatic-instance-repairs#requirements-for-using-automatic-instance-repairs"
+ }
+ ],
+ "recommendationControl": "Other Best Practices",
+ "longDescription": "Enabling automatic instance repairs in Azure Virtual Machine Scale Sets enhances application availability through a continuous health check and maintenance process.\n",
+ "pgVerified": true,
+ "description": "Enable Automatic Repair policy",
+ "potentialBenefits": "Boosts app availability by auto-repair",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachineScaleSets",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs that do NOT have automatic repair policy enabled\r\nresources\r\n| where type == \"microsoft.compute/virtualmachinescalesets\"\r\n| where properties.automaticRepairsPolicy.enabled == false\r\n| project recommendationId = \"820f4743-1f94-e946-ae0b-45efafd87962\", name, id, tags, param1 = \"automaticRepairsPolicy: Disabled\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "ee66ff65-9aa3-2345-93c1-25827cf79f44",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Get started with autoscale in Azure",
+ "url": "https://learn.microsoft.com/azure/azure-monitor/autoscale/autoscale-get-started?WT.mc_id=Portal-Microsoft_Azure_Monitoring"
+ },
+ {
+ "name": "Overview of autoscale in Azure",
+ "url": "https://learn.microsoft.com/azure/azure-monitor/autoscale/autoscale-overview"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Use custom autoscale for VMSS based on metrics and schedules to improve performance and cost effectiveness, adjusting instances as demand changes.\n",
+ "pgVerified": true,
+ "description": "Configure VMSS Autoscale to custom and configure the scaling metrics",
+ "potentialBenefits": "Enhances performance and cost-efficiency",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachineScaleSets",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find VMSS instances associated with autoscale settings when autoscale is disabled\r\nresources\r\n| where type == \"microsoft.compute/virtualmachinescalesets\"\r\n| project name, id, tags\r\n| join kind=leftouter (\r\n resources\r\n | where type == \"microsoft.insights/autoscalesettings\"\r\n | where tostring(properties.targetResourceUri) contains \"Microsoft.Compute/virtualMachineScaleSets\"\r\n | project id = tostring(properties.targetResourceUri), autoscalesettings = properties\r\n) on id\r\n| where isnull(autoscalesettings) or autoscalesettings.enabled == \"false\"\r\n| project recommendationId = \"ee66ff65-9aa3-2345-93c1-25827cf79f44\", name, id, tags, param1 = \"autoscalesettings: Disabled\"\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "3f85a51c-e286-9f44-b4dc-51d00768696c",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Use predictive autoscale to scale out before load demands in virtual machine scale sets",
+ "url": "https://learn.microsoft.com/azure/azure-monitor/autoscale/autoscale-predictive"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Predictive autoscale utilizes machine learning to efficiently manage and scale Azure Virtual Machine Scale Sets by forecasting CPU load through historical usage analysis, ensuring timely scale-out to meet demand.\n",
+ "pgVerified": true,
+ "description": "Enable Predictive autoscale and configure at least for Forecast Only",
+ "potentialBenefits": "Optimizes scaling with ML predictions",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachineScaleSets",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find VMSS instances associated with autoscale settings when predictiveAutoscalePolicy_scaleMode is disabled\r\nresources\r\n| where type == \"microsoft.compute/virtualmachinescalesets\"\r\n| project name, id, tags\r\n| join kind=leftouter (\r\n resources\r\n | where type == \"microsoft.insights/autoscalesettings\"\r\n | where tostring(properties.targetResourceUri) contains \"Microsoft.Compute/virtualMachineScaleSets\"\r\n | project id = tostring(properties.targetResourceUri), autoscalesettings = properties\r\n) on id\r\n| where autoscalesettings.enabled == \"true\" and autoscalesettings.predictiveAutoscalePolicy.scaleMode == \"Disabled\"\r\n| project recommendationId = \"3f85a51c-e286-9f44-b4dc-51d00768696c\", name, id, tags, param1 = \"predictiveAutoscalePolicy_scaleMode: Disabled\"\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b5a63aa0-c58e-244f-b8a6-cbba0560a6db",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Use scale-in policies with Azure Virtual Machine Scale Sets",
+ "url": "https://learn.microsoft.com/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-scale-in-policy"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Microsoft advises disabling strictly even VM instance distribution across Availability Zones in VMSS to improve scalability and flexibility, noting that uneven distribution may better serve application load demands despite the potential trade-off in resilience.\n",
+ "pgVerified": true,
+ "description": "Disable Force strictly even balance across zones to avoid scale in and out fail attempts",
+ "potentialBenefits": "Improves scaling, reduces fail attempts",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachineScaleSets",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find VMSS instances where strictly zoneBalance is set to True\r\nresources\r\n| where type == \"microsoft.compute/virtualmachinescalesets\"\r\n| where properties.orchestrationMode == \"Uniform\" and properties.zoneBalance == true\r\n| project recommendationId = \"b5a63aa0-c58e-244f-b8a6-cbba0560a6db\", name, id, tags, param1 = \"strictly zoneBalance: Enabled\"\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1422c567-782c-7148-ac7c-5fc14cf45adc",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Create a Virtual Machine Scale Set that uses Availability Zones",
+ "url": "https://learn.microsoft.com/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-use-availability-zones"
+ },
+ {
+ "name": "Update scale set to add availability zones",
+ "url": "https://learn.microsoft.com/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-use-availability-zones?tabs=cli-1%2Cportal-2#update-scale-set-to-add-availability-zones"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "When creating VMSS, implement availability zones as a protection measure for your applications and data against the rare event of datacenter failure.\n",
+ "pgVerified": true,
+ "description": "Deploy VMSS across availability zones with VMSS Flex",
+ "potentialBenefits": "Enhances disaster resilience",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachineScaleSets",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find VMSS instances with one or no Zones selected\r\nresources\r\n| where type == \"microsoft.compute/virtualmachinescalesets\"\r\n| where array_length(zones) <= 1 or isnull(zones)\r\n| project recommendationId = \"1422c567-782c-7148-ac7c-5fc14cf45adc\", name, id, tags, param1 = \"AvailabilityZones: Single Zone\"\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e4ffd7b0-ba24-c84e-9352-ba4819f908c0",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Automatic VM Guest Patching for Azure VMs",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/automatic-vm-guest-patching"
+ },
+ {
+ "name": "Auto OS Image Upgrades",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-automatic-upgrade"
+ }
+ ],
+ "recommendationControl": "Other Best Practices",
+ "longDescription": "Enabling automatic VM guest patching eases update management by safely, automatically patching virtual machines to maintain security compliance, while limiting blast radius of VMs. Note, the KQL will not return sets using Uniform orchestration.\n",
+ "pgVerified": true,
+ "description": "Set Patch orchestration options to Azure-orchestrated",
+ "potentialBenefits": "Eases patch management, enhances security",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachineScaleSets",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph query\r\n// Identifies VMs and VMSS with manual patch settings, excluding automatic patch modes\r\nresources\r\n| where type == \"microsoft.compute/virtualmachinescalesets\"\r\n| join kind=inner (\r\n resources\r\n | where type == \"microsoft.compute/virtualmachines\"\r\n | project id = tostring(properties.virtualMachineScaleSet.id), vmproperties = properties\r\n) on id\r\n| extend recommendationId = \"e4ffd7b0-ba24-c84e-9352-ba4819f908c0\", param1 = \"patchMode: Manual\", vmproperties.osProfile.linuxConfiguration.patchSettings.patchMode\r\n| where isnotnull(vmproperties.osProfile.linuxConfiguration) and vmproperties.osProfile.linuxConfiguration.patchSettings.patchMode !in (\"AutomaticByPlatform\", \"AutomaticByOS\")\r\n| distinct recommendationId, name, id, param1\r\n| union (resources\r\n| where type == \"microsoft.compute/virtualmachinescalesets\"\r\n| join kind=inner (\r\n resources\r\n | where type == \"microsoft.compute/virtualmachines\"\r\n | project id = tostring(properties.virtualMachineScaleSet.id), vmproperties = properties\r\n) on id\r\n| extend recommendationId = \"e4ffd7b0-ba24-c84e-9352-ba4819f908c0\", param1 = \"patchMode: Manual\", vmproperties.osProfile.windowsConfiguration.patchSettings.patchMode\r\n| where isnotnull(vmproperties.osProfile.windowsConfiguration) and vmproperties.osProfile.windowsConfiguration.patchSettings.patchMode !in (\"AutomaticByPlatform\", \"AutomaticByOS\")\r\n| distinct recommendationId, name, id, param1)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "83d61669-7bd6-9642-a305-175db8adcdf4",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Deprecated Azure Marketplace images",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/deprecated-images"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Ensure current versions of images are in use to avoid disruption after image deprecation. Please review the publisher, offer, sku information of the VM to ensure you are running on a supported image. Enable Auto Guest Patching or Image Upgrades, to get notifications about image deprecation.\n",
+ "pgVerified": true,
+ "description": "Upgrade VMSS Image versions scheduled to be deprecated or already retired",
+ "potentialBenefits": "Avoid disruptions by updating VMSS images.",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachineScaleSets",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "//cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "eb005943-40a8-194b-9db2-474d430046b7",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Container Registry Best Practices",
+ "url": "https://learn.microsoft.com/en-us/azure/container-registry/container-registry-best-practices"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Choose a service tier of Azure Container Registry to meet your performance needs. Premium offers the most bandwidth and highest rate of read and write operations for high-volume deployments. Use Basic to start, Standard for production, and Premium for hyper-scale performance and geo-replication.\n",
+ "pgVerified": false,
+ "description": "Use Premium tier for critical production workloads",
+ "potentialBenefits": "High-volume support and geo-replication",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerRegistry/registries",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Container Registries that are not using the Premium tier\r\nresources\r\n| where type =~ \"microsoft.containerregistry/registries\"\r\n| where sku.name != \"Premium\"\r\n| project recommendationId = \"eb005943-40a8-194b-9db2-474d430046b7\", name, id, tags, param1=strcat(\"SkuName: \", tostring(sku.name))\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "63491f70-22e4-3b4a-8b0c-845450e46fac",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Registry best practices - Enable zone redundancy",
+ "url": "https://review.learn.microsoft.com/en-us/azure/container-registry/zone-redundancy?toc=%2Fazure%2Freliability%2Ftoc.json&bc=%2Fazure%2Freliability%2Fbreadcrumb%2Ftoc.json&branch=main"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure Container Registry's optional zone redundancy enhances resiliency and high availability for registries or replication resources in a specific region by distributing resources across multiple zones.\n",
+ "pgVerified": false,
+ "description": "Enable zone redundancy",
+ "potentialBenefits": "Enhances resiliency and high availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerRegistry/registries",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Container Registries that do not have zone redundancy enabled\r\nresources\r\n| where type =~ \"microsoft.containerregistry/registries\"\r\n| where properties.zoneRedundancy != \"Enabled\"\r\n| project recommendationId = \"63491f70-22e4-3b4a-8b0c-845450e46fac\", name, id, tags, param1=strcat(\"zoneRedundancy: \", tostring(properties.zoneRedundancy))\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "36ea6c09-ef6e-d743-9cfb-bd0c928a430b",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Registry best practices - Enable geo-replication",
+ "url": "https://learn.microsoft.com/en-us/azure/container-registry/container-registry-best-practices#geo-replicate-multi-region-deployments"
+ },
+ {
+ "name": "Geo-Replicate Container Registry",
+ "url": "https://learn.microsoft.com/en-us/azure/container-registry/container-registry-geo-replication"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Use Azure Container Registry's geo-replication for multi-region deployments to simplify registry management and minimize latency. It enables serving global customers from local data centers and supports distributed development teams. Regional webhooks can notify of events in replicas.\n",
+ "pgVerified": false,
+ "description": "Enable geo-replication",
+ "potentialBenefits": "Simplifies management, reduces latency",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerRegistry/registries",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Container Registries that do not have geo-replication enabled\r\nresources\r\n| where type =~ \"microsoft.containerregistry/registries\"\r\n| project registryName = name, registryId = id, tags, primaryRegion = location\r\n| join kind=leftouter (\r\n Resources\r\n | where type =~ \"microsoft.containerregistry/registries/replications\"\r\n | project replicationRegion=name, replicationId = id\r\n | extend registryId=strcat_array(array_slice(split(replicationId, '/'), 0, -3), '/')\r\n ) on registryId\r\n| project-away registryId1, replicationId\r\n| where isempty(replicationRegion)\r\n| project recommendationId = \"36ea6c09-ef6e-d743-9cfb-bd0c928a430b\", name=registryName, id=registryId, tags\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "a5a0101a-a240-8742-90ba-81dbde9a0c0c",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Registry best practices - use repository namespaces",
+ "url": "https://learn.microsoft.com/en-us/azure/container-registry/container-registry-best-practices#repository-namespaces"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Using repository namespaces allows a single registry to be shared across multiple groups and deployments within an organization, supporting nested namespaces for group isolation. However, repositories are managed independently, not hierarchically.\n",
+ "pgVerified": false,
+ "description": "Use Repository namespaces",
+ "potentialBenefits": "Enables sharing and group isolation",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerRegistry/registries",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "8e389532-5db5-7e4c-9d4d-443b3e55ae82",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Registry best practices - Use dedicated resource group",
+ "url": "https://learn.microsoft.com/en-us/azure/container-registry/container-registry-best-practices#dedicated-resource-group"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Container registries, used across multiple hosts, should be in their own resource group to prevent accidental deletion of images when container instances are deleted, preserving the image collection while experimenting with hosts.\n",
+ "pgVerified": false,
+ "description": "Move Container Registry to a dedicated resource group",
+ "potentialBenefits": "Safeguards image collection",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerRegistry/registries",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// List container registries that contain additional resources within the same resource group.\r\nresources\r\n| where type =~ \"microsoft.containerregistry/registries\"\r\n| project registryName=name, registryId=id, registryTags=tags, resourceGroupId=strcat('/subscriptions/', subscriptionId, '/resourceGroups/', resourceGroup), resourceGroup, subscriptionId\r\n| join kind=inner (\r\n resources\r\n | where not(type =~ \"microsoft.containerregistry/registries\")\r\n | summarize recourceCount=count() by subscriptionId, resourceGroup\r\n | where recourceCount != 0\r\n) on resourceGroup, subscriptionId\r\n| project recommendationId = \"8e389532-5db5-7e4c-9d4d-443b3e55ae82\", name=registryName, id=registryId, tags=registryTags, param1=strcat('resourceGroupName:',resourceGroup), param2=strcat('resourceGroupId:',resourceGroupId)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "3ef86f16-f65b-c645-9901-7830d6dc3a1b",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Registry best practices - Manage registry size",
+ "url": "https://learn.microsoft.com/en-us/azure/container-registry/container-registry-best-practices#manage-registry-size"
+ },
+ {
+ "name": "Retention Policy",
+ "url": "https://learn.microsoft.com/en-us/azure/container-registry/container-registry-retention-policy#about-the-retention-policy"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "The storage constraints of Azure Container Registry's service tiers align with usage scenarios: Basic for starters, Standard for production, and Premium for high-scale performance and geo-replication.\n",
+ "pgVerified": false,
+ "description": "Manage registry size",
+ "potentialBenefits": "Reduce costs, optimize storage",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerRegistry/registries",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Container Registries that have their retention policy disabled\r\nresources\r\n| where type =~ \"microsoft.containerregistry/registries\"\r\n| where properties.policies.retentionPolicy.status == \"disabled\"\r\n| project recommendationId = \"3ef86f16-f65b-c645-9901-7830d6dc3a1b\", name, id, tags, param1='retentionPolicy:disabled'\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "03f4a7d8-c5b4-7842-8e6e-14997a34842b",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Enable anonymous pull access",
+ "url": "https://learn.microsoft.com/en-us/azure/container-registry/anonymous-pull-access#about-anonymous-pull-access"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "By default, Azure container registry requires authentication for pull/push actions. Enabling anonymous pull access exposes all content for public read actions. This applies to all repositories, potentially allowing unrestricted access if repository-scoped tokens are used.\n",
+ "pgVerified": false,
+ "description": "Disable anonymous pull access",
+ "potentialBenefits": "Enhanced security and controlled access",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerRegistry/registries",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Container Registries that have anonymous pull access enabled\r\nresources\r\n| where type =~ \"microsoft.containerregistry/registries\"\r\n| where properties.anonymousPullEnabled == \"true\"\r\n| project recommendationId = \"03f4a7d8-c5b4-7842-8e6e-14997a34842b\", name, id, tags\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "44107155-7a32-9348-89f3-d5aa7e7c5a1d",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Monitoring Azure Container Registry data reference - Resource Logs",
+ "url": "https://learn.microsoft.com/en-us/azure/container-registry/monitor-service-reference#resource-logs"
+ },
+ {
+ "name": "Monitor Azure Container Registry - Enable diagnostic logs",
+ "url": "https://learn.microsoft.com/en-us/azure/container-registry/monitor-service#collection-and-routing"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Resource Logs are not collected and stored until you create a diagnostic setting and route them to one or more locations.\n",
+ "pgVerified": false,
+ "description": "Configure Diagnostic Settings for all Azure Container Registries",
+ "potentialBenefits": "Enhanced tracking and debugging",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerRegistry/registries",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "d594cde6-4116-d143-a64a-25f63289a2f8",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Monitoring Azure Container Registry data reference",
+ "url": "https://learn.microsoft.com/en-us/azure/container-registry/monitor-service-reference#metrics"
+ },
+ {
+ "name": "Monitor Azure Container Registry",
+ "url": "https://learn.microsoft.com/en-us/azure/container-registry/monitor-service"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Monitoring Azure resources using Azure Monitor enhances their availability, performance, and operation. Azure Container Registry, a full-stack monitoring service, provides features for Azure and other cloud and on-premises resources.\n",
+ "pgVerified": false,
+ "description": "Monitor Azure Container Registry with Azure Monitor",
+ "potentialBenefits": "Enhanced monitoring and operation",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerRegistry/registries",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e7f0fd54-fba0-054e-9ab8-e676f2851f88",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Enable soft delete policy",
+ "url": "https://learn.microsoft.com/en-us/azure/container-registry/container-registry-soft-delete-policy"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Enabling soft delete in Azure Container Registry (ACR) allows for the management of deleted artifacts with a specified retention period. Users can list, filter, and restore these artifacts until automatically purged post-retention.\n",
+ "pgVerified": false,
+ "description": "Enable soft delete policy",
+ "potentialBenefits": "Recovery of deleted artifacts",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerRegistry/registries",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of Azure Container Registry resources that do not have soft delete enabled\r\nresources\r\n| where type =~ \"microsoft.containerregistry/registries\"\r\n| where properties.policies.softDeletePolicy.status == \"disabled\"\r\n| project recommendationId = \"e7f0fd54-fba0-054e-9ab8-e676f2851f88\", name, id, tags\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "4f63619f-5001-439c-bacb-8de891287727",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "AKS Availability Zones",
+ "url": "https://learn.microsoft.com/en-us/azure/aks/availability-zones"
+ },
+ {
+ "name": "Zone Balancing",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-use-availability-zones#zone-balancing"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure Availability Zones ensure high availability by offering independent locations within regions, equipped with their own power, cooling, and networking to ensure applications and data are protected from datacenter-level failures.\n",
+ "pgVerified": true,
+ "description": "Deploy AKS cluster across availability zones",
+ "potentialBenefits": "Enhanced fault tolerance for AKS",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Returns AKS clusters that do not have any availability zones enabled or only use a single zone\r\nresources\r\n| where type =~ \"Microsoft.ContainerService/managedClusters\"\r\n| project id, name, tags, location, pools = properties.agentPoolProfiles\r\n| mv-expand pool = pools\r\n| extend\r\n numOfAvailabilityZones = iif(isnull(pool.availabilityZones), 0, array_length(pool.availabilityZones))\r\n| where numOfAvailabilityZones < 2\r\n| project\r\n recommendationId = \"4f63619f-5001-439c-bacb-8de891287727\",\r\n id,\r\n name,\r\n tags,\r\n param1 = strcat(\"NodePoolName: \", pool.name),\r\n param2 = strcat(\"Mode: \", pool.mode),\r\n param3 = strcat(\"AvailabilityZones: \", iif(numOfAvailabilityZones == 0, \"None\", strcat(\"Zone \", strcat_array(pool.availabilityZones, \", \")))),\r\n param4 = strcat(\"Location: \", location)\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5ee083cd-6ac3-4a83-8913-9549dd36cf56",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "System and user node pools",
+ "url": "https://learn.microsoft.com/en-us/azure/aks/use-system-pools?tabs=azure-cli#system-and-user-node-pools"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "AKS assigns the kubernetes.azure.com/mode: system label to nodes in system node pools signaling the preference for system pods should be scheduled there. The CriticalAddonsOnly=true:NoSchedule taint can be added to your system nodes to prohibit application pods from being scheduled on them.\n",
+ "pgVerified": false,
+ "description": "Isolate system and application pods",
+ "potentialBenefits": "Enhanced reliability via pod isolation",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Returns each AKS cluster with nodepools that do not have system pods labelled with CriticalAddonsOnly\r\nresources\r\n| where type == \"microsoft.containerservice/managedclusters\"\r\n| mv-expand agentPoolProfile = properties.agentPoolProfiles\r\n| where agentPoolProfile.mode =~ 'System' // system node pools\r\n| extend taint = tostring(parse_json(agentPoolProfile.nodeTaints))\r\n| extend hasCriticalAddonsTaint = agentPoolProfile.kubeletConfig has 'CriticalAddonsOnly'\r\n| extend hasNodeLabel = agentPoolProfile.customNodeLabels has 'CriticalAddonsOnly'\r\n| extend hasCriticalAddonsOnly = hasCriticalAddonsTaint or hasNodeLabel or isempty(taint)\r\n| extend nodePool = tostring(parse_json(agentPoolProfile.name))\r\n| where hasCriticalAddonsOnly\r\n| project\r\n recommendationid=\"5ee083cd-6ac3-4a83-8913-9549dd36cf56\",\r\n id,\r\n name,\r\n tags,\r\n param1=strcat(\"nodepoolName: \", nodePool)\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "ca324d71-54b0-4a3e-b9e4-10e767daa9fc",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Entra integration",
+ "url": "https://learn.microsoft.com/en-us/azure/aks/concepts-identity#azure-ad-integration"
+ },
+ {
+ "name": "Use Azure role-based access control for AKS",
+ "url": "https://learn.microsoft.com/en-us/azure/aks/manage-azure-rbac?source=recommendations"
+ },
+ {
+ "name": "Manage AKS local accounts",
+ "url": "https://learn.microsoft.com/en-us/azure/aks/manage-local-accounts-managed-azure-ad?source=recommendations"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Local Kubernetes accounts in AKS, being non-auditable and legacy, are discouraged. Microsoft Entra's integration offers centralized management, multi-factor authentication, RBAC for detailed access, and a secure, scalable authentication system compatible with Azure and external identity providers.\n",
+ "pgVerified": false,
+ "description": "Disable local accounts",
+ "potentialBenefits": "Enhanced security and access control",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Returns a list of AKS clusters not using AAD enabled\r\nresources\r\n| where type == \"microsoft.containerservice/managedclusters\"\r\n| extend aadProfile = tostring (parse_json(properties.aadProfile))\r\n| extend disablelocalAdmin = tostring(parse_json(properties.disableLocalAccounts))\r\n| extend RBAC = tostring(parse_json(properties.enableRBAC))\r\n| where RBAC == \"false\"\r\n| project recommendationId=\"ca324d71-54b0-4a3e-b9e4-10e767daa9fc\", name, id, tags, param1=strcat(\"aadProfile: \", aadProfile), param2=strcat(\"disablelocalAdmin: \",disablelocalAdmin), param3=strcat(\"RBAC: \", RBAC)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "c22db132-399b-4e7c-995d-577a60881be8",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Configure Azure CNI networking",
+ "url": "https://learn.microsoft.com/en-us/azure/aks/configure-azure-cni-dynamic-ip-allocation"
+ },
+ {
+ "name": "Configure Azure CNI Overlay networking",
+ "url": "https://learn.microsoft.com/en-us/azure/aks/azure-cni-overlay"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Azure CNI enhances cluster IP and network management, allowing dynamic IP allocation, scalable subnets, direct pod-VNET connectivity, and supports diverse network policies for pods and nodes with Azure Network Policies and Calico, optimizing network efficiency and security\n",
+ "pgVerified": false,
+ "description": "Configure Azure CNI networking for dynamic allocation of IPs",
+ "potentialBenefits": "Dynamic IP allocation, scalable subnets, direct VNET access",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Check AKS Clusters using kubenet network profile\r\nresources\r\n| where type == \"microsoft.containerservice/managedclusters\"\r\n| extend networkProfile = tostring (parse_json(properties.networkProfile.networkPlugin))\r\n| where networkProfile ==\"kubenet\"\r\n| project recommendationId=\"c22db132-399b-4e7c-995d-577a60881be8\", name, id, tags, param1=strcat(\"networkProfile :\",networkProfile)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "902c82ff-4910-4b61-942d-0d6ef7f39b67",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Use the Cluster Autoscaler on AKS",
+ "url": "https://learn.microsoft.com/azure/aks/cluster-autoscaler?tabs=azure-cli"
+ },
+ {
+ "name": "Best practices for advanced scheduler features",
+ "url": "https://learn.microsoft.com/azure/aks/operator-best-practices-advanced-scheduler"
+ },
+ {
+ "name": "Node pool scaling considerations and best practices",
+ "url": "https://learn.microsoft.com/azure/aks/best-practices-performance-scale-large#node-pool-scaling"
+ },
+ {
+ "name": "Best practices for basic scheduler features",
+ "url": "https://learn.microsoft.com/azure/aks/operator-best-practices-scheduler"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "The cluster auto-scaler in AKS adjusts node counts based on pod resource needs and available capacity, enabling scaling as per demand to prevent outages.\n",
+ "pgVerified": true,
+ "description": "Enable the cluster auto-scaler on an existing cluster",
+ "potentialBenefits": "Optimizes scaling and prevents outages",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find AKS clusters with auto-scaling disabled\r\nResources\r\n| where type == \"microsoft.containerservice/managedclusters\"\r\n| extend autoScaling = tostring (parse_json(properties.agentPoolProfiles.[0].enableAutoScaling))\r\n| where autoScaling == \"false\"\r\n| project recommendationId=\"902c82ff-4910-4b61-942d-0d6ef7f39b67\", name, id, tags, param1=strcat(\"autoScaling :\", autoScaling)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "269a9f1a-6675-460a-831e-b05a887a8c4b",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "AKS Backups",
+ "url": "https://learn.microsoft.com/en-us/azure/backup/azure-kubernetes-service-cluster-backup"
+ },
+ {
+ "name": "Best Practices for AKS Backups",
+ "url": "https://learn.microsoft.com/en-us/azure/aks/operator-best-practices-storage"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "AKS, popular for stateful apps needing backups, can now use Azure Backup to secure clusters and attached volumes through an installed Backup Extension, enabling backup and restore operations via a Backup Vault.\n",
+ "pgVerified": true,
+ "description": "Back up Azure Kubernetes Service",
+ "potentialBenefits": "Ensures data safety for AKS",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find AKS clusters that do not have backup enabled\r\n\r\nresources\r\n| where type =~ 'Microsoft.ContainerService/managedClusters'\r\n| extend lname = tolower(name)\r\n| join kind=leftouter(recoveryservicesresources\r\n | where type =~ 'microsoft.dataprotection/backupvaults/backupinstances'\r\n | extend lname = tolower(tostring(split(properties.dataSourceInfo.resourceID, '/')[8]))\r\n | extend protectionState = properties.currentProtectionState\r\n | project lname, protectionState) on lname\r\n| where protectionState != 'ProtectionConfigured'\r\n| extend param1 = iif(isnull(protectionState), 'Protection Not Configured', strcat('Protection State: ', protectionState))\r\n| project recommendationID = \"269a9f1a-6675-460a-831e-b05a887a8c4b\", name, id, tags, param1\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e6188d3b-7fbc-4ecf-a37b-b658f9efcdc4",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Updating to the latest AKS version",
+ "url": "https://learn.microsoft.com/azure/aks/operator-best-practices-cluster-security?tabs=azure-cli#regularly-update-to-the-latest-version-of-kubernetes"
+ },
+ {
+ "name": "Upgrade cluster",
+ "url": "https://learn.microsoft.com/azure/aks/upgrade-cluster?tabs=azure-cli"
+ },
+ {
+ "name": "Auto-upgrading cluster",
+ "url": "https://learn.microsoft.com/azure/aks/auto-upgrade-cluster"
+ }
+ ],
+ "recommendationControl": "Service Upgrade and Retirement",
+ "longDescription": "Minor version releases bring new features and improvements. Patch releases, often weekly, focus on critical bug fixes within a minor version, including security vulnerabilities or major bugs. Unsupported Kubernetes versions may lead to unsupported clusters when seeking AKS support.\n",
+ "pgVerified": false,
+ "description": "Plan an AKS version upgrade",
+ "potentialBenefits": "Enhances features, fixes bugs, ensures support",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "d3111036-355d-431b-ab49-8ddad042800b",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Availability zones overview",
+ "url": "https://learn.microsoft.com/azure/reliability/availability-zones-overview?tabs=azure-cli"
+ },
+ {
+ "name": "Zone-redundant storage",
+ "url": "https://learn.microsoft.com/azure/storage/common/storage-redundancy#zone-redundant-storage"
+ },
+ {
+ "name": "ZRS disks",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/disks-redundancy#zone-redundant-storage-for-managed-disks"
+ },
+ {
+ "name": "Convert a disk from LRS to ZRS",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/disks-migrate-lrs-zrs"
+ },
+ {
+ "name": "Enable multi-zone storage redundancy in Azure Container Storage",
+ "url": "https://learn.microsoft.com/azure/storage/container-storage/enable-multi-zone-redundancy"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "ZRS ensures data replication across three zones, protecting against zonal outages. It's available for Azure Disks, Container Storage, Files, and Blob by setting the SKU to ZRS in storage classes, enhancing multi-zone AKS clusters from v1.29.\n",
+ "pgVerified": true,
+ "description": "Use zone-redundant storage for persistent volumes when running multi-zone AKS",
+ "potentialBenefits": "Increases data durability and availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b002c030-72e6-4a37-8217-1cb276c43169",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "CSI Storage Drivers",
+ "url": "https://learn.microsoft.com/azure/aks/csi-storage-drivers"
+ },
+ {
+ "name": "CSI Migrate in Tree Volumes",
+ "url": "https://learn.microsoft.com/azure/aks/csi-migrate-in-tree-volumes"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "From Kubernetes 1.26, Azure Disk and Azure File in-tree drivers are deprecated in favor of CSI drivers. Existing deployments remain operational but untested; users should switch to CSI drivers for new features and SKUs.\n",
+ "pgVerified": true,
+ "description": "Upgrade Persistent Volumes using in-tree drivers to Azure CSI drivers",
+ "potentialBenefits": "Ensures future compatibility",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "9a1c17e5-c9a0-43db-b920-adaf54d1bcb7",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Resource Quotas",
+ "url": "https://kubernetes.io/docs/concepts/policy/resource-quotas/"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "A ResourceQuota object sets limits on resource use per namespace, controlling the number and type of objects created, and the total compute resources available.\n",
+ "pgVerified": false,
+ "description": "Implement Resource Quota to ensure that Kubernetes resources do not exceed hard resource limits",
+ "potentialBenefits": "Limits AKS resource usage per namespace",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b4639ca7-6308-429a-8b98-92f0bf9bf813",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Virtual Nodes",
+ "url": "https://learn.microsoft.com/azure/aks/virtual-nodes"
+ },
+ {
+ "name": "Azure Container Instances",
+ "url": "https://learn.microsoft.com/azure/container-instances/container-instances-overview"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "To rapidly scale AKS workloads, utilize virtual nodes for quick pod provisioning, unlike Kubernetes auto-scaler. For clusters with availability zones, ensure one nodepool per AZ due to persistent volumes not working across AZs, preventing auto-scaler pod creation failures if lacking access.\n",
+ "pgVerified": false,
+ "description": "Attach Virtual Nodes (ACI) to the AKS cluster",
+ "potentialBenefits": "Faster scaling with virtual nodes",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "0611251f-e70f-4243-8ddd-cfe894bec2e7",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Pricing Tiers",
+ "url": "https://learn.microsoft.com/en-us/azure/aks/free-standard-pricing-tiers"
+ },
+ {
+ "name": "AKS Baseline Architecture",
+ "url": "https://learn.microsoft.com/en-us/azure/architecture/reference-architectures/containers/aks/baseline-aks?toc=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fazure%2Faks%2Ftoc.json&bc=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fazure%2Fbread%2Ftoc.json#kubernetes-api-server-sla"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Production AKS clusters require the Standard tier for a financially backed SLA and enhanced node scalability, as the free service lacks these features.\n",
+ "pgVerified": true,
+ "description": "Update AKS tier to Standard",
+ "potentialBenefits": "SLA guarantee and better scalability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Returns all AKS clusters not running on the Standard tier\r\nresources\r\n| where type == \"microsoft.containerservice/managedclusters\"\r\n| where sku.tier != \"Standard\"\r\n| project recommendationId=\"0611251f-e70f-4243-8ddd-cfe894bec2e7\", id, name, tags, param1=strcat(\"skuName: \", sku.name), param2=strcat(\"skuTier: \", sku.tier)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "dcaf8128-94bd-4d53-9235-3a0371df6b74",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Monitor AKS",
+ "url": "https://learn.microsoft.com/azure/aks/monitor-aks"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Azure Monitor enables real-time health and performance insights for AKS by collecting events, capturing container logs, and gathering CPU/Memory data from the Metrics API. It allows data visualization using Azure Monitor Container Insights, Prometheus, Grafana, or others.\n",
+ "pgVerified": true,
+ "description": "Enable AKS Monitoring",
+ "potentialBenefits": "Real-time AKS health/performance insights",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Returns AKS clusters where either Azure Monitor is not enabled and/or Container Insights is not enabled\r\nresources\r\n| where type == \"microsoft.containerservice/managedclusters\"\r\n| extend azureMonitor = tostring(parse_json(properties.azureMonitorProfile.metrics.enabled))\r\n| extend insights = tostring(parse_json(properties.addonProfiles.omsagent.enabled))\r\n| where isempty(azureMonitor) or isempty(insights)\r\n| project recommendationId=\"dcaf8128-94bd-4d53-9235-3a0371df6b74\",id, name, tags, param1=strcat(\"azureMonitorProfileEnabled: \", iff(isempty(azureMonitor), \"false\", azureMonitor)), param2=strcat(\"containerInsightsEnabled: \", iff(isempty(insights), \"false\", insights))\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "a7bfcc18-b0d8-4d37-81f3-8131ed8bead5",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Ephemeral OS disk",
+ "url": "https://learn.microsoft.com/azure/aks/concepts-storage#ephemeral-os-disk"
+ },
+ {
+ "name": "Configure an AKS cluster",
+ "url": "https://learn.microsoft.com/azure/aks/cluster-configuration"
+ },
+ {
+ "name": "Everything you want to know about ephemeral OS disks and AKS",
+ "url": "https://learn.microsoft.com/samples/azure-samples/aks-ephemeral-os-disk/aks-ephemeral-os-disk/"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Ephemeral OS disks on AKS offer lower read/write latency due to local attachment, eliminating the need for replication seen with managed disks. This enhances performance and speeds up cluster operations such as scaling or upgrading due to quicker re-imaging and boot times.\n",
+ "pgVerified": true,
+ "description": "Use Ephemeral OS disks on AKS clusters",
+ "potentialBenefits": "Lower latency, faster re-imaging and booting",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Returns any AKS cluster nodepools that do not have Ephemeral Disks\r\nresources\r\n| where type == \"microsoft.containerservice/managedclusters\"\r\n| mv-expand agentPoolProfile = properties.agentPoolProfiles\r\n| extend type = tostring(agentPoolProfile.osDiskType)\r\n| where type != 'Ephemeral'\r\n| project recommendationid=\"a7bfcc18-b0d8-4d37-81f3-8131ed8bead5\", name, id, param1=strcat(\"osDiskType: \", type)\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "26ebaf1f-c70d-4ebd-8641-4b60a0ce0094",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "AKS Baseline - Policy Management",
+ "url": "https://learn.microsoft.com/en-us/azure/architecture/reference-architectures/containers/aks/baseline-aks?toc=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fazure%2Faks%2Ftoc.json&bc=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fazure%2Fbread%2Ftoc.json#policy-management"
+ },
+ {
+ "name": "Built-in Policy Definitions for AKS",
+ "url": "https://learn.microsoft.com/en-us/azure/aks/policy-reference"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Azure Policies in AKS clusters help enforce governance best practices concerning security, authentication, provisioning, networking, and more, ensuring a robust and secure environment for operations.\n",
+ "pgVerified": false,
+ "description": "Enable and remediate Azure Policies configured for AKS",
+ "potentialBenefits": "Enhanced AKS governance and security",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Returns a count of non-compliant policy items per AKS cluster\r\nPolicyResources\r\n| where type =~ 'Microsoft.PolicyInsights/PolicyStates'\r\n| extend complianceState = tostring(properties.complianceState)\r\n| where complianceState == 'NonCompliant'\r\n| where properties.resourceType =~ 'Microsoft.ContainerService/managedClusters'\r\n| extend\r\n id = tostring(properties.resourceId)\r\n| summarize count() by id\r\n| join kind=inner (\r\n resources\r\n | where type =~ 'Microsoft.ContainerService/managedClusters'\r\n | project id, name\r\n) on id\r\n| project recommendationId=\"26ebaf1f-c70d-4ebd-8641-4b60a0ce0094\", id, name, param1=strcat(\"numNonCompliantAlerts: \", count_)\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5f3cbd68-692a-4121-988c-9770914859a9",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "GitOps with AKS",
+ "url": "https://learn.microsoft.com/en-us/azure/architecture/guide/aks/aks-cicd-github-actions-and-gitops"
+ },
+ {
+ "name": "GitOps for AKS - Reference Architecture",
+ "url": "https://learn.microsoft.com/en-us/azure/architecture/example-scenario/gitops-aks/gitops-blueprint-aks"
+ }
+ ],
+ "recommendationControl": "Other Best Practices",
+ "longDescription": "GitOps, an operating model for cloud-native apps, uses Git for storing application and infrastructure code as a source of truth for continuous delivery.\n",
+ "pgVerified": false,
+ "description": "Enable GitOps when using DevOps frameworks",
+ "potentialBenefits": "Ensures AKS config consistency",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Returns AKS clusters where GitOps is not enabled\r\nresources\r\n| where type == \"microsoft.containerservice/managedclusters\"\r\n| extend gitops = tostring (parse_json(properties.addOnProfiles.gitops.enabled))\r\n| where isempty(gitops)\r\n| project recommendationId=\"5f3cbd68-692a-4121-988c-9770914859a9\", id, name, tags, param1=strcat(\"gitopsEnabled: \", \"false\")\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "928fcc6f-5e9a-42d9-9bd4-260af42de2e5",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Topology Spread Constraints",
+ "url": "https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/"
+ },
+ {
+ "name": "Assign Pod Node",
+ "url": "https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Enhance availability and reliability by using pod topology spread constraints to control pod distribution based on node or zone topology, ensuring pods are spread across your cluster.\n",
+ "pgVerified": true,
+ "description": "Use pod topology spread constraints to ensure that pods are spread across different nodes or zones",
+ "potentialBenefits": "Ensures high availability and efficient use",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "cd6791b1-c60e-4b37-ac98-9897b1e6f4b8",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Configure probes",
+ "url": "https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/"
+ },
+ {
+ "name": "Assign Pod Node",
+ "url": "https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "AKS kubelet controller uses liveness probes to validate containers and applications health, ensuring the system knows when to restart a container based on its health status.\n",
+ "pgVerified": true,
+ "description": "Configures Pods Liveness, Readiness, and Startup Probes",
+ "potentialBenefits": "Enhances container health monitoring",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "bcfe71f1-ebed-49e5-a84a-193b81ad5d27",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Replica Sets",
+ "url": "https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Configuring multiple replicas in Pod or Deployment manifests stabilizes the number of replica Pods, ensuring that a specified number of identical Pods are always available, thereby guaranteeing their availability.\n",
+ "pgVerified": true,
+ "description": "Use deployments with multiple replicas in production applications to guarantee availability",
+ "potentialBenefits": "Ensures stable pod availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "7f7ae535-a5ba-4665-b7e0-c451dbdda01f",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "System nodepools",
+ "url": "https://learn.microsoft.com/azure/aks/use-system-pools?tabs=azure-cli"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "The system node pool should be configured with a minimum node count of two to ensure critical system pods are resilient to node outages.\n",
+ "pgVerified": true,
+ "description": "Configure system nodepool count",
+ "potentialBenefits": "Ensures pod resilience",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Returns each AKS cluster with nodepools that have system nodepools with less than 2 nodes\r\nresources\r\n| where type == \"microsoft.containerservice/managedclusters\"\r\n| mv-expand agentPoolProfile = properties.agentPoolProfiles\r\n| extend taints = tostring(parse_json(agentPoolProfile.nodeTaints))\r\n| extend nodePool = tostring(parse_json(agentPoolProfile.name))\r\n| where taints has \"CriticalAddonsOnly=true:NoSchedule\" and agentPoolProfile.minCount < 2\r\n| project recommendationid=\"7f7ae535-a5ba-4665-b7e0-c451dbdda01f\", id, name, param1=strcat(\"nodePoolName: \", nodePool), param2=strcat(\"nodePoolMinNodeCount: \", agentPoolProfile.minCount)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "005ccbbd-aeab-46ef-80bd-9bd4479412ec",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Well-Architected Framework review for Azure Kubernetes Service (AKS)",
+ "url": "https://learn.microsoft.com/azure/well-architected/service-guides/azure-kubernetes-service#design-checklist"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Configuring the user node pool with at least two nodes is essential for applications needing high availability, ensuring they remain operational and accessible without interruption.\n",
+ "pgVerified": true,
+ "description": "Configure user nodepool count",
+ "potentialBenefits": "Ensures high app availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Returns each AKS cluster with nodepools that have user nodepools with less than 2 nodes\r\nresources\r\n| where type == \"microsoft.containerservice/managedclusters\"\r\n| mv-expand agentPoolProfile = properties.agentPoolProfiles\r\n| extend taints = tostring(parse_json(agentPoolProfile.nodeTaints))\r\n| extend nodePool = tostring(parse_json(agentPoolProfile.name))\r\n| where taints !has \"CriticalAddonsOnly=true:NoSchedule\" and agentPoolProfile.minCount < 2\r\n| project recommendationid=\"005ccbbd-aeab-46ef-80bd-9bd4479412ec\", id, name, param1=strcat(\"nodePoolName: \", nodePool), param2=strcat(\"nodePoolMinNodeCount: \", agentPoolProfile.minCount)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "a08a06a0-e41a-4b99-83bb-69ce8bca54cb",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Configure PDBs",
+ "url": "https://kubernetes.io/docs/tasks/run-application/configure-pdb/"
+ },
+ {
+ "name": "Plan availability using PDBs",
+ "url": "https://learn.microsoft.com/azure/aks/operator-best-practices-scheduler#plan-for-availability-using-pod-disruption-budgets"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "A Pod Disruption Budget is a Kubernetes resource configuring the minimum number or percentage of pods that should remain available during disruptions like maintenance or scaling, ensuring a minimum number of pods are always available in the cluster.\n",
+ "pgVerified": true,
+ "description": "Configure pod disruption budgets (PDBs)",
+ "potentialBenefits": "Ensures cluster resiliency during disruptions",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e620fa98-7a40-41a0-bfc9-b4407297fb58",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure CNI Dynamic IP Allocation",
+ "url": "https://learn.microsoft.com/azure/aks/configure-azure-cni-dynamic-ip-allocation"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Nodepool subnets sized for max auto-scale settings enable AKS to efficiently scale out nodes, meeting increased demand while reducing resource constraints and potential service disruptions.\n",
+ "pgVerified": false,
+ "description": "Nodepool subnet size needs to accommodate maximum auto-scale settings",
+ "potentialBenefits": "Efficient scaling, reduced disruptions",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Returns each AKS cluster with nodepools that have user nodepools with a subnetmask that does not match autoscale configured max-nodes\r\n// Subtracting the network address, broadcast address, and default 3 addresses Azure reserves within each subnet\r\n\r\nresources\r\n| where type == \"microsoft.containerservice/managedclusters\"\r\n| extend nodePools = properties['agentPoolProfiles']\r\n| mv-expand nodePools = properties.agentPoolProfiles\r\n| where nodePools.enableAutoScaling == true\r\n| extend nodePoolName=nodePools.name, maxNodes = nodePools.maxCount, subnetId = tostring(nodePools.vnetSubnetID)\r\n| project clusterId = id, clusterName=name, nodePoolName=nodePools.name, toint(maxNodes), subnetId\r\n| join kind = leftouter (\r\n resources\r\n | where type == 'microsoft.network/virtualnetworks'\r\n | extend subnets = properties.subnets\r\n | mv-expand subnets\r\n | project id = tostring(subnets.id), addressPrefix = tostring(subnets.properties['addressPrefix'])\r\n | extend subnetmask = toint(substring(addressPrefix, indexof(addressPrefix, '/')+1, string_size(addressPrefix)))\r\n | extend possibleMaxNodeCount = toint(exp2(32-subnetmask) - 5)\r\n) on $left.subnetId == $right.id\r\n| project-away id, subnetmask\r\n| where possibleMaxNodeCount <= maxNodes\r\n| extend param1 = strcat(nodePoolName, \" autoscaler upper limit: \", maxNodes)\r\n| extend param2 = strcat(\"ip addresses on subnet: \", possibleMaxNodeCount)\r\n| project recommendationId=\"e620fa98-7a40-41a0-bfc9-b4407297fb58\", name=clusterName, id=clusterId, param1, param2\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "a01afc4c-7439-4919-b2da-3565992ea2a7",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Quotas",
+ "url": "https://learn.microsoft.com/azure/quotas/quotas-overview"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Node pool settings should not exceed the subscription core quota to ensure AKS can scale out nodes efficiently, meeting increased demand while reducing resource constraints and potential service disruptions.\n",
+ "pgVerified": false,
+ "description": "Node pool auto-scale settings should not exceed subscription core quota",
+ "potentialBenefits": "Reduced disruptions",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ContainerService/managedClusters",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "0e835cc2-2551-a247-b1f1-3c5f25c9cb70",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Databricks runtime support lifecycles",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/release-notes/runtime/databricks-runtime-ver"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Databricks recommends migrating workloads to the latest or LTS version of its runtime for enhanced stability and support. If on Runtime 11.3 LTS or above, move directly to the latest 12.x version. If below, first migrate to 11.3 LTS, then to the latest 12.x version as per the migration guide.\n",
+ "pgVerified": true,
+ "description": "Databricks runtime version is not latest or is not LTS version",
+ "potentialBenefits": "Enhanced stability and support",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "c166602e-0804-e34b-be8f-09b4d56e1fcd",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Databricks pools pre-provision VMs, reducing risks of provisioning errors during cluster start or scale, enhancing reliability.\n",
+ "pgVerified": true,
+ "description": "Use Databricks Pools",
+ "potentialBenefits": "Reduces provisioning errors",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5877a510-8444-7a4c-8412-a8dab8662f7e",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure managed disk types",
+ "url": "https://learn.microsoft.com/azure/virtual-machines/disks-types#premium-ssd"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Upgrade HDDs in premium VMs to SSDs for better speed and reliability. Premium SSDs boost IO-heavy apps; Standard SSDs balance cost and performance. Ideal for critical workloads, upgrading improves connectivity with brief reboot. Consider for vital VMs\n",
+ "pgVerified": true,
+ "description": "Use SSD backed VMs for Worker VM Type and Driver type",
+ "potentialBenefits": "Faster, reliable VM performance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5c72f0d6-55ec-d941-be84-36c194fa78c0",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices#enable-autoscaling-for-batch-workloadss"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Autoscaling adjusts cluster sizes automatically based on workload demands, offering benefits for many use cases in terms of costs and performance. It includes guidance on when and how to best utilize Autoscaling. For streaming, Delta Live Tables with autoscaling is advised.\n",
+ "pgVerified": true,
+ "description": "Enable autoscaling for batch workloads",
+ "potentialBenefits": "Cost and performance optimization",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "362ad2b6-b92c-414f-980a-0cf69467ccce",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices#enable-autoscaling-for-sql-warehouse"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "The scaling parameter of a SQL warehouse defines the min and max number of clusters for distributing queries. By default, it's set to one. Increasing the cluster count can accommodate more concurrent users effectively.\n",
+ "pgVerified": true,
+ "description": "Enable autoscaling for SQL warehouse",
+ "potentialBenefits": "Improves concurrency and efficiency",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "cd77db98-9b13-6e4b-bd2b-74c2cb538628",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/azure/databricks/lakehouse-architecture/reliability/best-practices"
+ },
+ {
+ "name": "Databricks enhanced autoscaling",
+ "url": "https://learn.microsoft.com/azure/databricks/delta-live-tables/settings#use-autoscaling-to-increase-efficiency-and-reduce-resource-usage"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Databricks enhanced autoscaling optimizes cluster utilization by automatically allocating cluster resources based on workload volume, with minimal impact on the data processing latency of your pipelines.\n",
+ "pgVerified": true,
+ "description": "Use Delta Live Tables enhanced autoscaling",
+ "potentialBenefits": "Optimized resource use and minimal latency",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "3d3e53b5-ebd1-db42-b43b-d4fad74824ec",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability?",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "To conserve cluster resources, you can terminate a cluster to store its configuration for future reuse or autostart jobs. Clusters can auto-terminate after inactivity, but this only tracks Spark jobs, not local processes, which might still be running even after Spark jobs end.\n",
+ "pgVerified": true,
+ "description": "Automatic Job Termination is enabled, ensure there are no user-defined local processes",
+ "potentialBenefits": "Saves cluster resources, avoids idle use",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "7fb90127-5364-bb4d-86fa-30778ed713fb",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Create a cluster",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/clusters/configure#cluster-log-delivery"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "When creating a Databricks cluster, you can set a log delivery location for the Spark driver, worker nodes, and events. Logs are delivered every 5 mins and archived hourly. Upon cluster termination, all generated logs until that point are guaranteed to be delivered.\n",
+ "pgVerified": true,
+ "description": "Enable Logging-Cluster log delivery",
+ "potentialBenefits": "Improved troubleshooting and audit",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "da4ea916-4df3-8c4d-8060-17b49da45977",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Delta Lake is an open source storage format enhancing data lakes' reliability with ACID transactions, schema enforcement, and scalable metadata handling.\n",
+ "pgVerified": true,
+ "description": "Use Delta Lake for higher reliability",
+ "potentialBenefits": "Enhances data reliability and processing",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "892ca809-e2b5-9a47-924a-71132bf6f902",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices#use-apache-spark-or-photon-for-distributed-compute"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Apache Spark in Databricks Lakehouse ensures resilient distributed data processing by automatically rescheduling failed tasks, aiding in overcoming external issues like network problems or revoked VMs.\n",
+ "pgVerified": true,
+ "description": "Use Photon Acceleration",
+ "potentialBenefits": "Boosts speed and reliability for Spark tasks",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "7e52d64d-8cc0-8548-a593-eb49ab45630d",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices"
+ }
+ ],
+ "recommendationControl": "Business Continuity",
+ "longDescription": "Invalid or nonconforming data can crash workloads dependent on specific data formats. Best practices recommend filtering such data at ingestion to improve end-to-end resilience, ensuring no data is lost or missed.\n",
+ "pgVerified": true,
+ "description": "Automatically rescue invalid or nonconforming data with Databricks Auto Loader or Delta Live Tables",
+ "potentialBenefits": "Enhanced data resilience and integrity",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "84e44da6-8cd7-b349-b02c-c8bf72cf587c",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Use Databricks and MLflow for deploying models as Spark UDFs for job scheduling, retries, autoscaling. Model serving offers scalable infrastructure, processes models using MLflow, and serves them via REST API using serverless compute managed in Databricks cloud.\n",
+ "pgVerified": true,
+ "description": "Configure jobs for automatic retries and termination",
+ "potentialBenefits": "Enhanced reliability and autoscaling",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "4cbb7744-ff3d-0447-badb-baf068c95696",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Use Databricks and MLflow for deploying models as Apache Spark UDFs, benefiting from job scheduling, retries, autoscaling, etc.\n",
+ "pgVerified": true,
+ "description": "Use a scalable and production-grade model serving infrastructure",
+ "potentialBenefits": "Enhances scalability and reliability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1b0d0893-bf0e-8f4c-9dc6-f18f145c1ecf",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Curate data by creating a layered architecture to increase data quality across layers. Start with a raw layer for ingested source data, continue with a curated layer for cleansed and refined data, and finish with a final layer catered to business needs, focusing on security and performance.\n",
+ "pgVerified": true,
+ "description": "Use a layered storage architecture",
+ "potentialBenefits": "Enhances data quality and trust",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e93fe702-e385-d741-ba37-1f1656482ecd",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices"
+ }
+ ],
+ "recommendationControl": "Business Continuity",
+ "longDescription": "Copying data leads to redundancy, lost integrity, lineage, and access issues, affecting lakehouse data quality. Temporary copies are useful for agility and innovation but can become problematic operational data silos, questioning data's master status and currency.\n",
+ "pgVerified": true,
+ "description": "Improve data integrity by reducing data redundancy",
+ "potentialBenefits": "Enhanced data integrity and quality",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b7e1d13f-54c9-1648-8a52-34c0abe8ce16",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices"
+ }
+ ],
+ "recommendationControl": "Other Best Practices",
+ "longDescription": "Uncontrolled schema changes can lead to invalid data and failing jobs. Databricks validates and enforces schema through Delta Lake, which prevents bad records during ingestion, and Auto Loader, which detects new columns and supports schema evolution to maintain data integrity.\n",
+ "pgVerified": true,
+ "description": "Actively manage schemas",
+ "potentialBenefits": "Prevents invalid data and job failures",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "a42297c4-7e4f-8b41-8d4b-114033263f0e",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices#use-constraints-and-data-expectations"
+ }
+ ],
+ "recommendationControl": "Business Continuity",
+ "longDescription": "Delta tables verify data quality automatically with SQL constraints, triggering an error for violations. Delta Live Tables enhance this by defining expectations for data quality, utilizing Python or SQL, to manage actions for record failures, ensuring data integrity and compliance.\n",
+ "pgVerified": true,
+ "description": "Use constraints and data expectations",
+ "potentialBenefits": "Ensures data quality and integrity",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "932d45d6-b46d-e341-abfb-d97bce832f1f",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices#create-regular-backups"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "To recover from a failure, regular backups are needed. The Databricks Labs project migrate lets admins create backups by exporting workspace assets using the Databricks CLI/API. These backups help in restoring or migrating workspaces.\n",
+ "pgVerified": true,
+ "description": "Create regular backups",
+ "potentialBenefits": "Ensures data recovery and migration",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "12e9d852-5cdc-2743-bffe-ee21f2ef7781",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices#recover-from-structured-streaming-query-failures"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Structured Streaming ensures fault-tolerance and data consistency in streaming queries. With Azure Databricks workflows, you can set up your queries to automatically restart after failure, picking up precisely where they left off.\n",
+ "pgVerified": true,
+ "description": "Recover from Structured Streaming query failures",
+ "potentialBenefits": "Fault-tolerance and auto-restart for queries",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "a18d60f8-c98c-ba4e-ad6e-2fac72879df1",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices#recover-etl-jobs-based-on-delta-time-travel"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Despite thorough testing, a production job can fail or yield unexpected data. Sometimes, repairs are done by adding jobs post-issue identification and pipeline correction.\n",
+ "pgVerified": true,
+ "description": "Recover ETL jobs based on Delta time travel",
+ "potentialBenefits": "Easy rollback and fix for ETL jobs",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "c0e22580-3819-444d-8546-a80e4ed85c83",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/reliability/best-practices"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Databricks Workflows enable efficient error recovery in multi-task jobs by offering a matrix view for issue examination. Fixes can be applied to initiate repair runs targeting only failed and dependent tasks, preserving successful outcomes and thereby saving time and money.\n",
+ "pgVerified": true,
+ "description": "Use Databricks Workflows and built-in recovery",
+ "potentialBenefits": "Saves time and money with smart recovery",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "4fdb7112-4531-6f48-b60e-c917a6068d9b",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Databricks Best Practices",
+ "url": "https://github.com/Azure/AzureDatabricksBestPractices/tree/master"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Implementing a disaster recovery pattern is vital for Azure Databricks, a cloud-native data analytics platform, ensuring data teams' access even during rare regional outages caused by disasters like hurricanes or earthquakes.\n",
+ "pgVerified": false,
+ "description": "Configure a disaster recovery pattern",
+ "potentialBenefits": "Ensures service continuity during disasters",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "42aedaa8-6151-424d-b782-b8666c779969",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for operational excellence",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/operational-excellence/best-practices#2-automate-deployments-and-workloads"
+ }
+ ],
+ "recommendationControl": "Other Best Practices",
+ "longDescription": "The Databricks Terraform provider manages Azure Databricks workspaces and cloud infrastructure flexibly and powerfully.\n",
+ "pgVerified": false,
+ "description": "Automate deployments and workloads",
+ "potentialBenefits": "Efficient, reliable automation",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "20193ff9-dbcd-a74e-b197-71d7d9d3c1e6",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Best practices for operational excellence",
+ "url": "https://learn.microsoft.com/en-us/azure/databricks/lakehouse-architecture/operational-excellence/best-practices#system-monitoring"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "The Databricks Terraform provider is a flexible, powerful tool for managing Azure Databricks workspaces and cloud infrastructure.\n",
+ "pgVerified": false,
+ "description": "Set up monitoring, alerting, and logging",
+ "potentialBenefits": "Enhanced reliability and automation",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "397cdebb-9d6e-ab4f-83a1-8c481de0a3a7",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Databricks Best Practices",
+ "url": "https://github.com/Azure/AzureDatabricksBestPractices/blob/master/toc.md#deploy-workspaces-in-multiple-subscriptions-to-honor-azure-capacity-limits"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Customers often naturally divide workspaces by teams or departments. However, it's crucial to also consider Azure Subscription and ADB Workspace limits when partitioning.\n",
+ "pgVerified": false,
+ "description": "Deploy workspaces in separate Subscriptions",
+ "potentialBenefits": "Enhanced limits management, team separation",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5e722c4f-415a-9b4c-bd4c-96b74dce29ad",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Databricks Best Practices",
+ "url": "https://github.com/Azure/AzureDatabricksBestPractices/blob/master/toc.md#consider-isolating-each-workspace-in-its-own-vnet"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Deploying only one Databricks Workspace per VNet aligns with ADB's isolation model.\n",
+ "pgVerified": false,
+ "description": "Isolate each workspace in its own Vnet",
+ "potentialBenefits": "Enhanced security and resource isolation",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "14310ba6-77ad-3641-a2db-57a2218b9bc7",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Databricks Best Practices",
+ "url": "https://github.com/Azure/AzureDatabricksBestPractices/blob/master/toc.md#do-not-store-any-production-data-in-default-dbfs-foldersr"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Driven by security and data availability concerns, each Azure Databricks Workspace comes with a default DBFS designed for system-level artifacts like libraries and Init scripts, not for production data.\n",
+ "pgVerified": false,
+ "description": "Do not Store any Production Data in Default DBFS Folders",
+ "potentialBenefits": "Enhanced security, data protection",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b5af7e26-3939-1b48-8fba-f8d4a475c67a",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Use Azure Spot Virtual Machines",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/spot-vms"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure Spot VMs are not suitable for critical production workloads needing high availability and reliability. They are meant for fault-tolerant tasks and can be evicted with 30-seconds notice if Azure needs the capacity, with no SLA guarantees.\n",
+ "pgVerified": false,
+ "description": "Do not use Azure Spot VMs for critical Production workloads",
+ "potentialBenefits": "Ensures high reliability for production",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "8aa63c34-dd9d-49bd-9582-21ec310dfbdd",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Databricks control plane addresses",
+ "url": "https://learn.microsoft.com/azure/databricks/resources/supported-regions#--azure-databricks-control-plane-addresses"
+ },
+ {
+ "name": "Migrate - maintained by Databricks Inc.",
+ "url": "https://github.com/databrickslabs/migrate"
+ },
+ {
+ "name": "Databricks Terraform Exporter - maintained by Databricks Inc. (Experimental)",
+ "url": "https://registry.terraform.io/providers/databricks/databricks/latest/docs/guides/experimental-exporter"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Move workspaces to in-region control plane for increased regional isolation. Identify current control plane region using the workspace URL and nslookup. When region from CNAME differs from workspace region and an in-region control is available, consider migration using tools provided below.\n",
+ "pgVerified": false,
+ "description": "Evaluate regional isolation for workspaces",
+ "potentialBenefits": "Improves resilience and data sovereignty",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "028593be-956e-4736-bccf-074cb10b92f4",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Compute configuration best practices",
+ "url": "https://learn.microsoft.com/azure/databricks/compute/cluster-config-best-practices"
+ },
+ {
+ "name": "GPU-enabled compute",
+ "url": "https://learn.microsoft.com/azure/databricks/compute/gpu"
+ }
+ ],
+ "recommendationControl": "Personalized",
+ "longDescription": "Azure Databricks planning should include VM SKU swap strategies for capacity issues. VMs are regional, and allocation failures may occur, shown by a \"CLOUD PROVIDER\" error.\n",
+ "pgVerified": false,
+ "description": "Define alternate VM SKUs",
+ "potentialBenefits": "Ensures service availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Databricks/workspaces",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "88856605-53d8-4bbd-a75b-4a7b14939d32",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "High availability concepts in Azure Database for MySQL - Flexible Server",
+ "url": "https://learn.microsoft.com/azure/mysql/flexible-server/concepts-high-availability"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Enable HA with zone redundancy on flexible server instances to deploy a standby replica in a different zone, offering automatic failover capability for improved reliability and disaster recovery.\n",
+ "pgVerified": true,
+ "description": "Enable HA with zone redundancy",
+ "potentialBenefits": "Enhanced uptime and data protection",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DBforMySQL/flexibleServers",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find Database for MySQL instances that are not zone redundant\r\nresources\r\n| where type == \"microsoft.dbformysql/flexibleservers\"\r\n| where properties.highAvailability.mode != \"ZoneRedundant\"\r\n| project recommendationId = \"88856605-53d8-4bbd-a75b-4a7b14939d32\", name, id, tags, param1 = \"ZoneRedundant: False\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "82a9a0f2-24ee-496f-9ad2-25f81710942d",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Scheduled maintenance in Azure Database for MySQL - Flexible Server",
+ "url": "https://learn.microsoft.com/azure/mysql/flexible-server/concepts-maintenance"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Use custom maintenance schedule on flexible server instances to select a preferred time for service updates to be applied.\n",
+ "pgVerified": true,
+ "description": "Enable custom maintenance schedule",
+ "potentialBenefits": "Control update timings",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DBforMySQL/flexibleServers",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find Database for MySQL instances that do not have a custom maintenance window\r\nresources\r\n| where type =~ \"microsoft.dbformysql/flexibleservers\"\r\n| where properties.maintenanceWindow.customWindow != \"Enabled\"\r\n| project recommendationId = \"82a9a0f2-24ee-496f-9ad2-25f81710942d\", name, id, tags, param1 = strcat(\"customWindow:\", properties['maintenanceWindow']['customWindow'])"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "ca87914f-aac4-4783-ab67-82a6f936f194",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Overview of high availability with Azure Database for PostgreSQL",
+ "url": "https://learn.microsoft.com/azure/postgresql/flexible-server/concepts-high-availability"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Enable HA with zone redundancy on flexible server instances to deploy a standby replica in a different zone, offering automatic failover capability for improved reliability and disaster recovery.\n",
+ "pgVerified": true,
+ "description": "Enable HA with zone redundancy",
+ "potentialBenefits": "Enhanced uptime and data protection",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DBforPostgreSQL/flexibleServers",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find Database for PostgreSQL instances that are not zone redundant\r\nresources\r\n| where type == \"microsoft.dbforpostgresql/flexibleservers\"\r\n| where properties.highAvailability.mode != \"ZoneRedundant\"\r\n| project recommendationId = \"ca87914f-aac4-4783-ab67-82a6f936f194\", name, id, tags, param1 = \"ZoneRedundant: False\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b2bad57d-7e03-4c0f-9024-597c9eb295bb",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Scheduled maintenance in Azure Database for PostgreSQL - Flexible Server",
+ "url": "https://learn.microsoft.com/azure/postgresql/flexible-server/concepts-maintenance"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Use custom maintenance schedule on flexible server instances to select a preferred time for service updates to be applied.\n",
+ "pgVerified": true,
+ "description": "Enable custom maintenance schedule",
+ "potentialBenefits": "Control update timings",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DBforPostgreSQL/flexibleServers",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find Database for PostgreSQL instances that do not have a custom maintenance window\r\nresources\r\n| where type == \"microsoft.dbforpostgresql/flexibleservers\"\r\n| where properties.maintenanceWindow.customWindow != \"Enabled\"\r\n| project recommendationId = \"b2bad57d-7e03-4c0f-9024-597c9eb295bb\", name, id, tags, param1 = strcat(\"customWindow:\", properties['maintenanceWindow']['customWindow'])\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "013ac34e-7c4b-425f-9e0c-216f0cc06181",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/azure/virtual-desktop/configure-validation-environment?tabs=azure-portal"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Create a Validation Pool for early issue detection with planned AVD updates. Adjust limits based on needs. Scale by adding multiple host pools for more users. Regularly test updates on host pools. Validate changes before applying to main environment to avoid downtime.\n",
+ "pgVerified": true,
+ "description": "Create a validation host pool for testing of planned updates",
+ "potentialBenefits": "Enhanced environment stability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DesktopVirtualization/hostPools",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "979ff8be-5f3a-4d8e-9aa3-407ecdd6d6f7",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/azure/virtual-desktop/scheduled-agent-updates"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Create maintenance schedules for AVD agent updates to avoid disruptions. Use Scheduled Agent Updates to set maintenance windows for updating Azure Virtual Desktop agent, side-by-side stack, and Geneva Monitoring agent.\n",
+ "pgVerified": true,
+ "description": "Configure host pool scheduled agent updates",
+ "potentialBenefits": "Enhanced environment stability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DesktopVirtualization/hostPools",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// Azure Resource Graph Query\r\n// This resource graph query will return all AVD host pools that does not have scheduled agent updates configured\r\nresources\r\n| where type =~ \"Microsoft.DesktopVirtualization/hostpools\"\r\n| where isnull(properties.agentUpdate)\r\n| project recommendationID = \"979ff8be-5f3a-4d8e-9aa3-407ecdd6d6f7\", name, id, tags, param1 = 'No scheduled agent updates'\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "939cb85c-102a-4e0a-ab82-5c92116d3778",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/deploy/virtual-dc/adds-on-azure-vm#configure-the-vms-and-install-active-directory-domain-services"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "For optimized AVD configuration, place Hybrid VMs in unique OUs. Segregate Prod and DR units for environment-specific settings. This ensures targeted configurations for session hosts, including Fslogix, timeouts, and session controls.\n",
+ "pgVerified": true,
+ "description": "Ensure a unique OU is used when deploying host pools with domain joined session hosts",
+ "potentialBenefits": "Improved AVD hostpool config & segmentation",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DesktopVirtualization/hostPools",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "38721758-2cc2-4d6b-b7b7-8b47dadbf7df",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/en-us/azure/site-recovery/site-recovery-overview"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Implement Azure Site Recovery (ASR) or Azure Backup for personal host pools to enable seamless failover and failback. This replicates VMs supporting personal desktops to a secondary Azure region, ensuring recovery from a known state in case of a disaster or outage.\n",
+ "pgVerified": true,
+ "description": "Use Azure Site Recovery or backups to protect VMs supporting personal desktops",
+ "potentialBenefits": "Ensures VM recovery & failover",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Compute/virtualMachines",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "499769ae-67c9-492e-9ca5-cfd4cece5209",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/azure/virtual-desktop/autoscale-scaling-plan?tabs=portal"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Each region has its own scaling plans assigned to host pools within that region. However, these plans can become inaccessible if there's a regional failure. To mitigate this risk, it's advisable to create a secondary scaling plan in another region.\n",
+ "pgVerified": true,
+ "description": "Scaling plans should be created per region and not scaled across regions",
+ "potentialBenefits": "Enhanced scaling",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DesktopVirtualization/scalingPlans",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "783c6c18-760b-4867-9ced-3010a0bc5aa3",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Import and export IoT Hub device identities in bulk",
+ "url": "https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-bulk-identity-mgmt"
+ },
+ {
+ "name": "IoT Hub high availability and disaster recovery",
+ "url": "https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-ha-dr#manual-failover"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Device Identities should be copied to the failover region IoT-Hub for all IoT devices to ensure connectivity in case of a failover. Manual Failover to another region is quicker (RTO), suitable for mission critical workloads.\n",
+ "pgVerified": false,
+ "description": "Device Identities are exported to a secondary region",
+ "potentialBenefits": "Faster failover; Ensures device connectivity",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Devices/IotHubs",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "eeba3a49-fef0-481f-a471-7ff01139b474",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Choose the right IoT Hub tier and size for your solution",
+ "url": "https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-scaling"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "In a production scenario, the IoT Hub tier should not be Free because the Free tier does not provide the necessary Service Level Agreement.\n",
+ "pgVerified": false,
+ "description": "Do not use free tier",
+ "potentialBenefits": "Ensures SLA for production",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Devices/IotHubs",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// list all IoT Hubs that are using the Free tier\r\nresources\r\n| where type =~ \"microsoft.devices/iothubs\" and\r\n tostring(sku.tier) =~ 'Free'\r\n| project recommendationId=\"eeba3a49-fef0-481f-a471-7ff01139b474\", name, id, tags, param1=strcat(\"tier:\", tostring(sku.tier))\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "214cbc46-747e-4354-af6e-6bf0054196a5",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure IoT Hub high availability and disaster recovery",
+ "url": "https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-ha-dr#availability-zones"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "In regions supporting Availability Zones for IoT Hub, using these zones boosts availability. They're automatically activated for new IoT Hubs in supported areas.\n",
+ "pgVerified": false,
+ "description": "Use Availability Zones",
+ "potentialBenefits": "Boosts IoT Hub availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Devices/IotHubs",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b1e1378d-4572-4414-bebd-b8872a6d4d1c",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "IoT Hub Device Provisioning Service (DPS) terminology",
+ "url": "https://learn.microsoft.com/en-us/azure/iot-dps/concepts-service"
+ },
+ {
+ "name": "Best practices for large-scale IoT device deployments",
+ "url": "https://learn.microsoft.com/en-us/azure/iot-dps/concepts-deploy-at-scale"
+ },
+ {
+ "name": "IoT Hub Device Provisioning Service high availability and disaster recovery",
+ "url": "https://learn.microsoft.com/en-us/azure/iot-dps/iot-dps-ha-dr"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Device Provisioning Service (DPS) enables easy redistribution of IoT devices for scaling and availability, allowing devices to be reassigned and not bound to specific IoT Hub instances. Devices in IoT Hubs using DPS should be verified for DPS utilization.\n",
+ "pgVerified": false,
+ "description": "Use Device Provisioning Service",
+ "potentialBenefits": "Enhances scalability and availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Devices/IotHubs",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// list all IoT Hubs that do not have a linked IoT Hub Device Provisioning Service (DPS)\r\nresources\r\n| where type =~ \"microsoft.devices/iothubs\"\r\n| project id, iotHubName=tostring(properties.hostName), tags, resourceGroup\r\n| join kind=fullouter (\r\n resources\r\n | where type == \"microsoft.devices/provisioningservices\"\r\n | mv-expand iotHubs=properties.iotHubs\r\n | project iotHubName = tostring(iotHubs.name), dpsName = name, name=iotHubs.name\r\n) on iotHubName\r\n| where dpsName == ''\r\n| project recommendationId=\"b1e1378d-4572-4414-bebd-b8872a6d4d1c\", name=iotHubName, id, tags, param1='DPS:none'\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "02568a5d-335e-4e51-9f7c-fe2ada977300",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "IoT Hub high availability and disaster recovery",
+ "url": "https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-ha-dr"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "In case of a regional failure, an IoT Hub can failover to a second region, automatically or manually, to ensure your application continues working.\n",
+ "pgVerified": false,
+ "description": "Define Failover Guidelines",
+ "potentialBenefits": "Ensures business continuity",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Devices/IotHubs",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e7dbd21f-b27a-4b8c-a901-cedb1e6d8e1e",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Use message routing - Fallback route",
+ "url": "https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-messages-d2c#fallback-route"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Using message routing for custom endpoints in IoT Hub, messages might not reach these destinations if specific conditions are unmet. A default route ensures all messages are received, but disabling this safety net risks leaving some messages undelivered.\n",
+ "pgVerified": false,
+ "description": "Disabled Fallback Route",
+ "potentialBenefits": "Prevents undelivered messages",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Devices/IotHubs",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// list all IoT Hubs that have the fallback route disabled\r\nresources\r\n| where type == \"microsoft.devices/iothubs\"\r\n| extend fallbackEnabled=properties.routing.fallbackRoute.isEnabled\r\n| where fallbackEnabled == false\r\n| project recommendationId=\"e7dbd21f-b27a-4b8c-a901-cedb1e6d8e1e\", name, id, tags, param1='FallbackRouteEnabled:false'\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "43663217-a1d3-844b-80ea-571a2ce37c6c",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Distribute data globally with Azure Cosmos DB | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/cosmos-db/distribute-data-globally"
+ },
+ {
+ "name": "Tips for building highly available applications | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/cosmos-db/high-availability#tips-for-building-highly-available-applications"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Enable a secondary region in Cosmos DB for higher SLA without downtime. Simple as pinning a location on a map. For Strong consistency, configure at least three regions for write availability in case of failure.\n",
+ "pgVerified": true,
+ "description": "Configure at least two regions for high availability",
+ "potentialBenefits": "Enhances SLA and resilience",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DocumentDB/databaseAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Query to find Azure Cosmos DB accounts that have less than 2 regions or less than 3 regions with strong consistency level\r\nResources\r\n| where type =~ 'Microsoft.DocumentDb/databaseAccounts'\r\n| where\r\n array_length(properties.locations) < 2 or\r\n (array_length(properties.locations) < 3 and properties.consistencyPolicy.defaultConsistencyLevel == 'Strong')\r\n| project recommendationId='43663217-a1d3-844b-80ea-571a2ce37c6c', name, id, tags\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "9cabded7-a1fc-6e4a-944b-d7dd98ea31a2",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Manage an Azure Cosmos DB account by using the Azure portal | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/cosmos-db/how-to-manage-database-account#automatic-failover"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Cosmos DB boasts high uptime and resiliency. Even so, issues may arise. With Service-Managed failover, if a region is down, Cosmos DB automatically switches to the next available region, requiring no user action.\n",
+ "pgVerified": true,
+ "description": "Enable service-managed failover for multi-region accounts with single write region",
+ "potentialBenefits": "Auto failover for high uptime",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DocumentDB/databaseAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Query to list all Azure Cosmos DB accounts that do not have multiple write locations or automatic failover enabled\r\nResources\r\n| where type =~ 'Microsoft.DocumentDb/databaseAccounts'\r\n| where\r\n array_length(properties.locations) > 1 and\r\n tobool(properties.enableAutomaticFailover) == false and\r\n tobool(properties.enableMultipleWriteLocations) == false\r\n| project recommendationId='9cabded7-a1fc-6e4a-944b-d7dd98ea31a2', name, id, tags\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "9ce78192-74a0-104c-b5bb-9a443f941649",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Distribute data globally with Azure Cosmos DB | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/cosmos-db/distribute-data-globally"
+ },
+ {
+ "name": "Conflict resolution types and resolution policies in Azure Cosmos DB | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/cosmos-db/conflict-resolution-policies"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Multi-region write capability allows for designing applications that are highly available across multiple regions, though it demands careful attention to consistency requirements and conflict resolution. Improper setup may decrease availability and cause data corruption due to unhandled conflicts.\n",
+ "pgVerified": true,
+ "description": "Evaluate multi-region write capability",
+ "potentialBenefits": "Enhances high availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DocumentDB/databaseAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Query to find Azure Cosmos DB accounts that have multiple read locations but do not have multiple write locations enabled\r\nResources\r\n| where type =~ 'Microsoft.DocumentDb/databaseAccounts'\r\n| where\r\n array_length(properties.locations) > 1 and\r\n properties.enableMultipleWriteLocations == false\r\n| project recommendationId='9ce78192-74a0-104c-b5bb-9a443f941649', name, id, tags\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e544520b-8505-7841-9e77-1f1974ee86ec",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Continuous backup with point in time restore feature in Azure Cosmos DB | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/cosmos-db/continuous-backup-restore-introduction"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Cosmos DB's backup is always on, offering protection against data mishaps. Continuous mode allows for self-serve restoration to a pre-mishap point, unlike periodic mode which requires contacting Microsoft support, leading to longer restore times.\n",
+ "pgVerified": true,
+ "description": "Configure continuous backup mode",
+ "potentialBenefits": "Faster self-serve data restore",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DocumentDB/databaseAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Query all Azure Cosmos DB accounts that do not have continuous backup mode configured\r\nResources\r\n| where type =~ 'Microsoft.DocumentDb/databaseAccounts'\r\n| where\r\n properties.backupPolicy.type == 'Periodic' and\r\n properties.enableMultipleWriteLocations == false and\r\n properties.enableAnalyticalStorage == false\r\n| project recommendationId='e544520b-8505-7841-9e77-1f1974ee86ec', name, id, tags\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "c006604a-0d29-684c-99f0-9729cb40dac5",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Pagination in Azure Cosmos DB | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/cosmos-db/nosql/query/pagination#handling-multiple-pages-of-results"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Cosmos DB has a 4 MB response limit, leading to paginated results for large or partition-spanning queries. Each page shows availability and provides a continuation token for the next. A while loop in code is necessary to traverse all pages until completion.\n",
+ "pgVerified": true,
+ "description": "Ensure query results are fully drained",
+ "potentialBenefits": "Maximizes data retrieval efficiency",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DocumentDB/databaseAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "7eb32cf9-9a42-1540-acf8-597cbba8a418",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Designing resilient applications with Azure Cosmos DB SDKs | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/cosmos-db/nosql/conceptual-resilient-sdk-applications"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Using a single instance of the SDK client for each account and application is crucial as connections are tied to the client. Compute environments have a limit on open connections, affecting connectivity when exceeded.\n",
+ "pgVerified": true,
+ "description": "Maintain singleton pattern in your client",
+ "potentialBenefits": "Optimizes connections and efficiency",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DocumentDB/databaseAccounts",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "fa6ac22f-0584-bb4b-80e4-80f4755d1a97",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Designing resilient applications with Azure Cosmos DB SDKs | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/cosmos-db/nosql/conceptual-resilient-sdk-applications"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Cosmos DB SDKs automatically manage many transient errors through retries. Despite this, it's crucial for applications to implement additional retry policies targeting specific cases that the SDKs can't generically address, ensuring more robust error handling.\n",
+ "pgVerified": true,
+ "description": "Implement retry logic in your client",
+ "potentialBenefits": "Enhances error handling resilience",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DocumentDB/databaseAccounts",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "deaea200-013c-414b-ac9f-bfa7a7fb13f0",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Create alerts for Azure Cosmos DB using Azure Monitor | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/cosmos-db/create-alerts"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Monitoring the availability and responsiveness of Azure Cosmos DB resources and having alerts set up for your workload is a good practice. This ensures you stay proactive in handling unforeseen events.\n",
+ "pgVerified": true,
+ "description": "Monitor Cosmos DB health and set up alerts",
+ "potentialBenefits": "Proactive issue management",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.DocumentDB/databaseAccounts",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "54c3191b-b535-1946-bba9-b754f44060f6",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Event Grid - Enable diagnostic logs for Event Grid resources",
+ "url": "https://learn.microsoft.com/en-us/azure/event-grid/enable-diagnostic-logs-topic"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Enabling diagnostic settings on Azure Event Grid resources like custom topics, system topics, and domains lets you capture and view diagnostic information to troubleshoot failures effectively.\n",
+ "pgVerified": false,
+ "description": "Configure Diagnostic Settings for all Azure Event Grid resources",
+ "potentialBenefits": "Enhanced troubleshooting for Event Grid",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.EventGrid/topics",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "92162eb5-4323-3145-8a6c-525ce2f0700e",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Event Grid delivery and retry",
+ "url": "https://learn.microsoft.com/en-us/azure/event-grid/delivery-and-retry#dead-letter-events"
+ }
+ ],
+ "recommendationControl": "Personalized",
+ "longDescription": "Event Grid may not deliver an event within a specific time or after several attempts, leading to dead-lettering where undelivered events are sent to a storage account.\n",
+ "pgVerified": false,
+ "description": "Configure Dead-letter to save events that cannot be delivered",
+ "potentialBenefits": "Saves undelivered events",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.EventGrid/topics",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b2069f64-4741-3d4a-a71d-50c8b03f5ab7",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Configure private endpoints for Azure Event Grid topics or domains",
+ "url": "https://learn.microsoft.com/en-us/azure/event-grid/configure-private-endpoints"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Use private endpoints for secure event ingress to custom topics/domains via a private link, avoiding the public internet. It employs an IP from the VNet space for your topic/domain.\n",
+ "pgVerified": false,
+ "description": "Configure Private Endpoints",
+ "potentialBenefits": "Secure, private VNet ingress",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.EventGrid/topics",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all eventgrid services not protected by private endpoints.\r\nResources\r\n| where type contains \"eventgrid\"\r\n| where properties['publicNetworkAccess'] == \"Enabled\"\r\n| project recommendationId = \"b2069f64-4741-3d4a-a71d-50c8b03f5ab7\", name, id, tags\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "84636c6c-b317-4722-b603-7b1ffc16384b",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Disabled",
+ "learnMoreLink": [
+ {
+ "name": "Azure Event Hubs - Geo-disaster recovery",
+ "url": "https://learn.microsoft.com/azure/event-hubs/event-hubs-geo-dr?tabs=portal#availability-zones"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "When you use the Azure portal, zone redundancy via support for availability zones is automatically enabled. Avoid disabling this capability to continue replicating metadata and data (events) across data centers in an availability zone.\n",
+ "pgVerified": false,
+ "description": "Don't disable zone redundancy",
+ "potentialBenefits": "Enhanced fault tolerance for Event Hub",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.EventHub/namespaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// under-development\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "fbfef3df-04a5-41b2-a8fd-b8541eb04956",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Event Hubs - Automatically scale throughput units",
+ "url": "https://learn.microsoft.com/azure/event-hubs/event-hubs-auto-inflate"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Enable auto-inflate on Event Hub Standard tier namespaces to automatically scale up TUs, meeting usage needs and preventing data ingress or egress throttle scenarios by adjusting to allowed rates.\n",
+ "pgVerified": false,
+ "description": "Enable auto-inflate on Event Hub Standard tier",
+ "potentialBenefits": "Prevents throttling by autoscaling TUs",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.EventHub/namespaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find Event Hub namespace instances that are Standard tier and do not have Auto Inflate enabled\r\nresources\r\n| where type == \"microsoft.eventhub/namespaces\"\r\n| where sku.tier == \"Standard\"\r\n| where properties.isAutoInflateEnabled == \"false\"\r\n| project recommendationId = \"fbfef3df-04a5-41b2-a8fd-b8541eb04956\", name, id, tags, param1 = \"AutoInflateEnabled: False\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "be448849-0d7d-49ba-9c94-9573ee533d5d",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Resource Health",
+ "url": "https://learn.microsoft.com/en-us/azure/service-health/resource-health-overview"
+ },
+ {
+ "name": "Configure Resource Health alerts in the Azure portal",
+ "url": "https://learn.microsoft.com/en-us/azure/service-health/resource-health-alert-monitor-guide#create-a-resource-health-alert-rule-in-the-azure-portal"
+ },
+ {
+ "name": "Alerts Health",
+ "url": "https://learn.microsoft.com/en-us/azure/service-health/alerts-activity-log-service-notifications-portal"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Configure Resource Health Alerts for all applicable resources to stay informed about the current and historical health status of your Azure resources. They notify you when these resources have a change in their health status.\n",
+ "pgVerified": true,
+ "description": "Configure Resource Health Alerts",
+ "potentialBenefits": "Stay informed on resource status",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Insights/activityLogAlerts",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "9729c89d-8118-41b4-a39b-e12468fa872b",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "What is Azure Service Health?",
+ "url": "https://learn.microsoft.com/azure/service-health/overview"
+ },
+ {
+ "name": "Configure alerts for service health events",
+ "url": "https://learn.microsoft.com/azure/service-health/alerts-activity-log-service-notifications-portal"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Service health gives a personalized health view of Azure services and regions used, offering the best place for notifications on outages, planned maintenance, and health advisories by knowing the services used.\n",
+ "pgVerified": true,
+ "description": "Configure Service Health Alerts",
+ "potentialBenefits": "Proactive outage and maintenance alerts",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Insights/activityLogAlerts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This resource graph query will return all subscriptions without Service Health alerts configured.\r\n\r\nresourcecontainers\r\n| where type == 'microsoft.resources/subscriptions'\r\n| project subscriptionAlerts=tostring(id),name,tags\r\n| join kind=leftouter (\r\n resources\r\n | where type == 'microsoft.insights/activitylogalerts' and properties.condition contains \"ServiceHealth\"\r\n | extend subscriptions = properties.scopes\r\n | project subscriptions\r\n | mv-expand subscriptions\r\n | project subscriptionAlerts = tostring(subscriptions)\r\n) on subscriptionAlerts\r\n| where isempty(subscriptionAlerts1)\r\n| project-away subscriptionAlerts1\r\n| project recommendationID = \"9729c89d-8118-41b4-a39b-e12468fa872b\",id=subscriptionAlerts,name,tags\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "dac421ec-2832-4c37-839e-b6dc5a38f2fa",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Migrate an Application Insights classic resource to a workspace-based resource",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-monitor/app/convert-classic-resource"
+ }
+ ],
+ "recommendationControl": "Service Upgrade and Retirement",
+ "longDescription": "Classic Application Insights retires in February 2024. To minimize disruption to existing application monitoring scenarios, transition to workspace-based Application Insights before 29 February 2024.\n",
+ "pgVerified": false,
+ "description": "Convert Classic Deployments",
+ "potentialBenefits": "Avoid service disruption post-Feb 2024",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Insights/components",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph query\r\n// Filters Application Insights resources with ‘Classic’ deployment type\r\nresources\r\n| where type =~ \"microsoft.insights/components\"\r\n| extend IngestionMode = properties.IngestionMode\r\n| where IngestionMode =~ 'ApplicationInsights'\r\n| project recommendationId= \"dac421ec-2832-4c37-839e-b6dc5a38f2fa\", name, id, tags, param1=\"ApplicationInsightsDeploymentType: Classic\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1cca00d2-d9ab-8e42-a788-5d40f49405cb",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Key Vault soft-delete overview",
+ "url": "https://learn.microsoft.com/azure/key-vault/general/soft-delete-overview"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Key Vault's soft-delete feature enables recovery of deleted vaults and objects like keys, secrets, and certificates. When enabled, marked resources are retained for 90 days, allowing for their recovery, essentially undoing deletion.\n",
+ "pgVerified": false,
+ "description": "Key vaults should have soft delete enabled",
+ "potentialBenefits": "Enables recovery of deleted items",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.KeyVault/vaults",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This Resource Graph query will return all Key Vaults that do not have soft delete enabled.\r\nresources\r\n| where type == \"microsoft.keyvault/vaults\"\r\n| where isnull(properties.enableSoftDelete) or properties.enableSoftDelete != \"true\"\r\n| project recommendationId = \"1cca00d2-d9ab-8e42-a788-5d40f49405cb\", name, id, tags, param1 = \"EnableSoftDelete: Disabled\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "70fcfe6d-00e9-5544-a63a-fff42b9f2edb",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Key Vault purge-protection overview",
+ "url": "https://learn.microsoft.com/azure/key-vault/general/soft-delete-overview#purge-protection"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Purge protection secures against malicious deletions by enforcing a retention period for soft deleted key vaults, ensuring no one, not even insiders or Microsoft, can purge your key vaults during this period, preventing permanent data loss.\n",
+ "pgVerified": false,
+ "description": "Key vaults should have purge protection enabled",
+ "potentialBenefits": "Protects from insider attacks, avoids data loss",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.KeyVault/vaults",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This resource graph query will return all Key Vaults that do not have Purge Protection enabled.\r\nresources\r\n| where type == \"microsoft.keyvault/vaults\"\r\n| where isnull(properties.enablePurgeProtection) or properties.enablePurgeProtection != \"true\"\r\n| project recommendationId = \"70fcfe6d-00e9-5544-a63a-fff42b9f2edb\", name, id, tags, param1 = \"EnablePurgeProtection: Disabled\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "00c3d2b0-ea6e-4c4b-89be-b78a35caeb51",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Key Vault Private Link Service overview",
+ "url": "https://learn.microsoft.com/azure/key-vault/general/security-features#network-security"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Azure Private Link Service lets you securely and privately connect to Azure Key Vault via a Private Endpoint in your VNet, using a private IP and eliminating public Internet exposure.\n",
+ "pgVerified": false,
+ "description": "Enable Azure Private Link Service for Key vault",
+ "potentialBenefits": "Secure Key Vault with Private Link",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.KeyVault/vaults",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This resource graph query will return all Key Vaults that does not have a Private Endpoint Connection or where a private endpoint exists but public access is enabled\r\n\r\nresources\r\n| where type == \"microsoft.keyvault/vaults\"\r\n| where isnull(properties.privateEndpointConnections) or properties.privateEndpointConnections[0].properties.provisioningState != (\"Succeeded\") or (isnull(properties.networkAcls) and properties.publicNetworkAccess == 'Enabled')\r\n| extend param1 = strcat('Private Endpoint: ', iif(isnotnull(properties.privateEndpointConnections),split(properties.privateEndpointConnections[0].properties.privateEndpoint.id,'/')[8],'No Private Endpoint'))\r\n| extend param2 = strcat('Access: ', iif(properties.publicNetworkAccess == 'Disabled', 'Public Access Disabled', iif(isnotnull(properties.networkAcls), 'NetworkACLs in place','Public Access Enabled')))\r\n| project recommendationID = \"00c3d2b0-ea6e-4c4b-89be-b78a35caeb51\", name, id, tags, param1, param2\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e7091145-3642-bd41-bb58-66502e64d2cd",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Key Vault best practices overview",
+ "url": "https://learn.microsoft.com/azure/key-vault/general/best-practices#why-we-recommend-separate-key-vaults"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Key vaults are security boundaries for secret storage. Grouping secrets together increases risk during a security event, as attacks could access multiple secrets.\n",
+ "pgVerified": false,
+ "description": "Use separate key vaults per application per environment",
+ "potentialBenefits": "Enhanced security, Reduced risk",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.KeyVault/vaults",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1dc0821d-4f14-7644-bab4-ba208ff5f7fa",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Key Vault logging overview",
+ "url": "https://learn.microsoft.com/azure/key-vault/general/logging?tabs=Vault"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Enable logs, set up alerts, and adhere to retention requirements for improved monitoring and security of Key Vault access, detailing the frequency and identity of users.\n",
+ "pgVerified": false,
+ "description": "Diagnostic logs in Key Vault should be enabled",
+ "potentialBenefits": "Enhanced monitoring and security compliance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.KeyVault/vaults",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "af426a99-62a6-6b4c-9662-42d220b413b8",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Service levels for Azure NetApp Files | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/azure-netapp-files-service-levels"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Service levels, part of capacity pool attributes, determine the maximum throughput per volume quota in Azure NetApp Files. It combines read and write speed, offering three levels: Standard (16 MiB/s per 1TiB), Premium (64 MiB/s per 1TiB), and Ultra (128 MiB/s per 1TiB) throughput.\n",
+ "pgVerified": true,
+ "description": "Use the correct service level and volume quota size for the expected performance level",
+ "potentialBenefits": "Optimized performance and cost efficiency",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.NetApp/netAppAccounts",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "ab984130-c57b-6c4a-8d04-6723b4e1bdb6",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Guidelines for Azure NetApp Files network planning | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/azure-netapp-files-network-topologies"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Standard network feature in Azure NetApp Files enhances IP limits and VNet capabilities, including network security groups, user-defined routes on subnets, and diverse connectivity options.\n",
+ "pgVerified": true,
+ "description": "Use standard network features for production in Azure NetApp Files",
+ "potentialBenefits": "Enhanced connectivity and security",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.NetApp/netAppAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This Resource Graph query will return all Azure NetApp Files volumes without standard network features.\r\nresources\r\n| where type =~ \"microsoft.netapp/netappaccounts/capacitypools/volumes\"\r\n| where properties.networkFeatures != \"Standard\"\r\n| project recommendationId = \"ab984130-c57b-6c4a-8d04-6723b4e1bdb6\", name, id, tags\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "47d100a5-7f85-5742-967a-67eb5081240a",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Use availability zones for high availability in Azure NetApp Files | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/use-availability-zones"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Availability zones are distinct locations within an Azure region to withstand local failures. Deploy your workload in multiple availability zones and use application-based replication and failover technologies or consider using Azure NetApp Files cross-zone replication to achieve high availability.\n",
+ "pgVerified": true,
+ "description": "Use availability zones for high availability in Azure NetApp Files",
+ "potentialBenefits": "High Availability across availability zones",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.NetApp/netAppAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This Resource Graph query will return all Azure NetApp Files volumes without an availability zone defined.\r\nResources\r\n| where type =~ \"Microsoft.NetApp/netAppAccounts/capacityPools/volumes\"\r\n| where array_length(zones) == 0 or isnull(zones)\r\n| project recommendationId = \"47d100a5-7f85-5742-967a-67eb5081240a\", name, id, tags\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "8bb690e8-64d5-4838-8703-9ee3dbac688f",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Manage availability zone volume placement for Azure NetApp Files | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/manage-availability-zone-volume-placement"
+ }
+ ],
+ "recommendationControl": "Other Best Practices",
+ "longDescription": "Azure NetApp Files' availability zone (AZ) volume placement feature lets you deploy volumes in the same AZ with Azure compute and other services to have within AZ latency and share the same AZ failure domain.\n",
+ "pgVerified": true,
+ "description": "Deploy ANF volumes in the same availability zone with Azure compute and other services",
+ "potentialBenefits": "Within AZ latency and tolerate failure of other AZ",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.NetApp/netAppAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "72827434-c773-4345-9493-34848ddf5803",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "How Azure NetApp Files snapshots work | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/snapshots-introduction"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure NetApp Files snapshot technology ensures stability, scalability, and swift data recoverability without affecting performance. It supports automatic snapshot creation via policies for Azure NetApp Files data.\n",
+ "pgVerified": true,
+ "description": "Use snapshots for data protection in Azure NetApp Files",
+ "potentialBenefits": "Stable, scalable, swift recovery, no perf impact",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.NetApp/netAppAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This Resource Graph query will return all Azure NetApp Files volumes without a snapshot policy defined.\r\nresources\r\n| where type == \"microsoft.netapp/netappaccounts/capacitypools/volumes\"\r\n| where properties.dataProtection.snapshot.snapshotPolicyId == \"\"\r\n| project recommendationId = \"72827434-c773-4345-9493-34848ddf5803\", name, id, tags\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b2fb3e60-97ec-e34d-af29-b16a0d61c2ac",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Understand Azure NetApp Files backup | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/backup-introduction"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Azure NetApp Files offers a fully managed backup solution enhancing long-term recovery, archiving, and compliance.\n",
+ "pgVerified": true,
+ "description": "Enable backup for data protection in Azure NetApp Files",
+ "potentialBenefits": "Enhances data recovery and compliance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.NetApp/netAppAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This Resource Graph query will return all Azure NetApp Files volumes without a backup policy defined.\r\nresources\r\n| where type == \"microsoft.netapp/netappaccounts/capacitypools/volumes\"\r\n| where properties.dataProtection.backup.backupPolicyId == \"\"\r\n| project recommendationId = \"b2fb3e60-97ec-e34d-af29-b16a0d61c2ac\", name, id, tags\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e30317d2-c502-4dfe-a2d3-0a737cc79545",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Cross-region replication of Azure NetApp Files volumes",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-netapp-files/cross-region-replication-introduction"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Azure NetApp Files replication offers data protection by allowing asynchronous cross-region volume replication for application failover in case of regional outages. Volumes can be replicated across regions, not concurrently with cross-zone replication.\n",
+ "pgVerified": true,
+ "description": "Enable Cross-region replication of Azure NetApp Files volumes",
+ "potentialBenefits": "Enhanced data protection and disaster recovery",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.NetApp/netAppAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This Resource Graph query will return all Azure NetApp Files volumes without cross-region replication.\r\nresources\r\n| where type == \"microsoft.netapp/netappaccounts/capacitypools/volumes\"\r\n| extend remoteVolumeRegion = properties.dataProtection.replication.remoteVolumeRegion\r\n| extend volumeType = properties.volumeType\r\n| extend replicationType = iff((remoteVolumeRegion == location), \"CZR\", iff((remoteVolumeRegion == \"\"),\"n/a\",\"CRR\"))\r\n| where replicationType != \"CRR\" and volumeType != \"DataProtection\"\r\n| project recommendationId = \"e30317d2-c502-4dfe-a2d3-0a737cc79545\", name, id, tags\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e3d742e1-dacd-9b48-b6b1-510ec9f87c96",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Cross-zone replication of Azure NetApp Files volumes | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/cross-zone-replication-introduction"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "The cross-zone replication (CZR) feature enables asynchronous data replication between Azure NetApp Files volumes across different availability zones, ensuring data protection and critical application failover in case of zone-wide disasters.\n",
+ "pgVerified": true,
+ "description": "Enable Cross-zone replication of Azure NetApp Files volumes",
+ "potentialBenefits": "Enhances disaster recovery across availability zones",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.NetApp/netAppAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This Resource Graph query will return all Azure NetApp Files volumes without cross-zone replication.\r\nresources\r\n| where type == \"microsoft.netapp/netappaccounts/capacitypools/volumes\"\r\n| extend remoteVolumeRegion = properties.dataProtection.replication.remoteVolumeRegion\r\n| extend volumeType = properties.volumeType\r\n| extend replicationType = iff((remoteVolumeRegion == location), \"CZR\", iff((remoteVolumeRegion == \"\"),\"n/a\",\"CRR\"))\r\n| where replicationType != \"CZR\" and volumeType != \"DataProtection\"\r\n| project recommendationId = \"e3d742e1-dacd-9b48-b6b1-510ec9f87c96\", name, id, tags\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "2f579fc9-e599-0d44-8b97-254f50ae04d8",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Ways to monitor Azure NetApp Files | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/monitor-azure-netapp-files"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Azure NetApp Files offers metrics like allocated storage, actual usage, volume IOPS, and latency, enabling a better understanding of usage patterns and volume performance for NetApp accounts.\n",
+ "pgVerified": true,
+ "description": "Monitor Azure NetApp Files metrics to better understand usage pattern and performance",
+ "potentialBenefits": "Optimize usage and performance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.NetApp/netAppAccounts",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "687ae58f-517f-ca43-90fe-922497e61283",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Policy definitions for Azure NetApp Files | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/azure-policy-definitions"
+ },
+ {
+ "name": "Creating custom policy definitions | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/governance/policy/tutorials/create-custom-policy-definition"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Azure NetApp Files supports Azure policy integration using either built-in policy definitions or by creating custom ones to maintain organizational standards and compliance.\n",
+ "pgVerified": true,
+ "description": "Enforce standards and assess compliance in Azure NetApp Files with Azure policy",
+ "potentialBenefits": "Enforce standards and assess compliance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.NetApp/netAppAccounts",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "cfa2244b-5436-47de-8287-b217875d3b0a",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Configure network features for an Azure NetApp Files volume",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/configure-network-features"
+ },
+ {
+ "name": "Manage SMB share ACLs in Azure NetApp Files",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/manage-smb-share-access-control-lists"
+ },
+ {
+ "name": "Configure export policy for NFS or dual-protocol volumes",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/azure-netapp-files-configure-export-policy"
+ },
+ {
+ "name": "Configure access control lists on NFSv4.1 volumes for Azure NetApp Files",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/configure-access-control-lists"
+ },
+ {
+ "name": "Configure Unix permissions and change ownership mode for NFS and dual-protocol volumes",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/configure-unix-permissions-change-ownership-mode"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Access to the delegated subnet should be limited to specific Azure Virtual Networks. SMB-enabled volumes' share permissions should move away from 'Everyone/Full control'. NFS-enabled volumes' access needs to be controlled via export policies and/or NFSv4.1 ACLs.\n",
+ "pgVerified": true,
+ "description": "Restrict default access to Azure NetApp Files volumes",
+ "potentialBenefits": "Enhanced security, Reduced data breach risk",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.NetApp/netAppAccounts",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "d1e7ccc3-e6c1-40e9-a36e-fd134711c808",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Do I need to take special precautions for SMB-based applications? | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/faq-application-resilience#do-i-need-to-take-special-precautions-for-smb-based-applications"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Certain SMB applications need SMB Transparent Failover for maintenance without interrupting server connectivity. Azure NetApp Files provides this through SMB Continuous Availability for applications like Citrix App Layering, FSLogix user/profile containers, Microsoft SQL Server, MSIX app attach.\n",
+ "pgVerified": true,
+ "description": "Make use of SMB continuous availability for supported applications",
+ "potentialBenefits": "Zero downtime for SMB apps",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.NetApp/netAppAccounts",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "60f36f9b-fac9-4160-bbf5-57af04da4f53",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "What do you recommend for handling potential application disruptions due to storage service maintenance events? | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/azure-netapp-files/faq-application-resilience#what-do-you-recommend-for-handling-potential-application-disruptions-due-to-storage-service-maintenance-events"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure NetApp Files might undergo occasional planned maintenance such as platform updates or service and software upgrades. It's important to be aware of the application's resiliency settings to cope with these storage service maintenance events.\n",
+ "pgVerified": true,
+ "description": "Ensure application resilience for service maintenance events",
+ "potentialBenefits": "Minimizes downtime during maintenance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.NetApp/netAppAccounts",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "823b0cff-05c0-2e4e-a1e7-9965e1cfa16f",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Application Gateway Autoscaling Zone-Redundant",
+ "url": "https://learn.microsoft.com/azure/application-gateway/application-gateway-autoscaling-zone-redundant#autoscaling-and-high-availability"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Azure Application Gateways v2 are always deployed in a highly available fashion with multiple instances by default. Enabling autoscale ensures the service is not reliant on manual intervention for scaling.\n",
+ "pgVerified": true,
+ "description": "Ensure autoscale has been enabled",
+ "potentialBenefits": "Enhances uptime and enables autoscaling",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/applicationGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This query will return all Application Gateways that do not have autoscale enabled or have a min capacity of 1\r\nresources\r\n| where type =~ \"microsoft.network/applicationGateways\"\r\n| where isnull(properties.autoscaleConfiguration) or properties.autoscaleConfiguration.minCapacity <= 1\r\n| project recommendationId = \"823b0cff-05c0-2e4e-a1e7-9965e1cfa16f\", name, id, tags, param1 = \"autoScaleConfiguration: isNull or MinCapacity <= 1\"\r\n| order by id asc\r\n\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "233a7008-71e9-e745-923e-1a1c7a0b92f3",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Application Gateway Security",
+ "url": "https://learn.microsoft.com/azure/well-architected/services/networking/azure-application-gateway#security"
+ },
+ {
+ "name": "Application Gateway SSL Overview",
+ "url": "https://learn.microsoft.com/azure/application-gateway/ssl-overview"
+ },
+ {
+ "name": "Application Gateway SSL Policy Overview",
+ "url": "https://learn.microsoft.com/azure/application-gateway/application-gateway-ssl-policy-overview"
+ },
+ {
+ "name": "Application Gateway KeyVault Certs",
+ "url": "https://learn.microsoft.com/azure/application-gateway/key-vault-certs"
+ },
+ {
+ "name": "Application Gateway SSL Cert Management",
+ "url": "https://learn.microsoft.com/azure/application-gateway/ssl-certificate-management"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Secure all incoming connections using HTTPS for production services with end-to-end SSL/TLS or SSL/TLS termination at the Application Gateway to protect against attacks and ensure data remains private and encrypted between the web server and browsers.\n",
+ "pgVerified": true,
+ "description": "Secure all incoming connections with SSL",
+ "potentialBenefits": "Enhanced security and privacy",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/applicationGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// You can use the following Azure Resource Graph query to check if an HTTP rule is using an SSL certificate or is using Azure Key Vault to store the certificates\r\nresources\r\n| where type =~ \"microsoft.network/applicationGateways\"\r\n| mv-expand frontendPorts = properties.frontendPorts\r\n| mv-expand httpListeners = properties.httpListeners\r\n| where isnull(parse_json(httpListeners.properties.sslCertificate))\r\n| project recommendationId=\"233a7008-71e9-e745-923e-1a1c7a0b92f3\", name, id, tags, param1=strcat(\"frontendPort: \", frontendPorts.properties.port), param2=\"tls: false\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "8d9223c4-730d-ca47-af88-a9a024c37270",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Well-Architected Framework Application Gateway Overview",
+ "url": "https://learn.microsoft.com/azure/well-architected/services/networking/azure-application-gateway"
+ },
+ {
+ "name": "Application Gateway - Web Application Firewall",
+ "url": "https://learn.microsoft.com/azure/application-gateway/features#web-application-firewall"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Use Application Gateway with Web Application Firewall (WAF) in an application virtual network to safeguard inbound HTTP/S internet traffic. WAF offers centralized defense against potential exploits through OWASP core rule sets-based rules.\n",
+ "pgVerified": true,
+ "description": "Enable Web Application Firewall policies",
+ "potentialBenefits": "Enhanced security for HTTP/S traffic",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/applicationGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This query will return all Application Gateways that do not have WAF enabled\r\nResources\r\n| where type =~ \"microsoft.network/applicationGateways\"\r\n| where properties.firewallpolicy != \"\"\r\n| project recommendationId = \"8d9223c4-730d-ca47-af88-a9a024c37270\", name, id, tags, param1 = \"webApplicationFirewallConfiguration: isNull\"\r\n| order by id asc\r\n\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "7893f0b3-8622-1d47-beed-4b50a19f7895",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Application Gateway Overview V2",
+ "url": "https://learn.microsoft.com/azure/application-gateway/overview-v2"
+ },
+ {
+ "name": "Application Gateway Feature Comparison Between V1 and V2",
+ "url": "https://learn.microsoft.com/azure/application-gateway/overview-v2#feature-comparison-between-v1-sku-and-v2-sku"
+ },
+ {
+ "name": "Application Gateway V1 Retirement",
+ "url": "https://azure.microsoft.com/updates/application-gateway-v1-will-be-retired-on-28-april-2026-transition-to-application-gateway-v2/"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Use Application Gateway v2 for built-in features like autoscaling, static VIPs, Azure KeyVault integration for better traffic management and performance, unless v1 is necessary.\n",
+ "pgVerified": true,
+ "description": "Use Application GW V2 instead of V1",
+ "potentialBenefits": "Better performance, autoscaling, more features",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/applicationGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Get all Application Gateways, which are using the deprecated V1 SKU\r\nresources\r\n| where type =~ 'microsoft.network/applicationgateways'\r\n| extend tier = properties.sku.tier\r\n| where tier == 'Standard' or tier == 'WAF'\r\n| project recommendationId = \"7893f0b3-8622-1d47-beed-4b50a19f7895\", name, id, tags\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5d035919-898d-a047-8d5d-454e199692e5",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Application Gateway Metrics",
+ "url": "https://learn.microsoft.com/azure/application-gateway/application-gateway-metrics"
+ },
+ {
+ "name": "Application Gateway Diagnostics",
+ "url": "https://learn.microsoft.com/azure/application-gateway/application-gateway-diagnostics"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Enable logging in storage accounts, Log Analytics, and monitoring services for auditing and insights. If using NSGs, enable NSG flow logs to be stored, providing in-depth traffic analysis into Azure Cloud.\n",
+ "pgVerified": true,
+ "description": "Monitor and Log the configurations and traffic",
+ "potentialBenefits": "Enhanced traffic insight and audit",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/applicationGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "847a8d88-21c4-bc48-a94e-562206edd767",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Application Gateway Probe Overview",
+ "url": "https://learn.microsoft.com/azure/application-gateway/application-gateway-probe-overview"
+ },
+ {
+ "name": "Well-Architected Framework Application Gateway Overview",
+ "url": "https://learn.microsoft.com/azure/well-architected/services/networking/azure-application-gateway"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Using custom health probes enhances understanding of backend availability and facilitates monitoring of backend services for any impact.\n",
+ "pgVerified": true,
+ "description": "Use Health Probes to detect backend availability",
+ "potentialBenefits": "Ensures backend uptime monitoring.",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/applicationGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Application Gateways are not using health probes to monitor the availability of the backend systems\r\nresources\r\n| where type =~ \"microsoft.network/applicationGateways\"\r\n| where array_length(properties.probes) == 0\r\n| project recommendationId=\"847a8d88-21c4-bc48-a94e-562206edd767\", name, id, tags, param1=\"customHealthProbeUsed: false\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "c9c00f2a-3888-714b-a72b-b4c9e8fcffb2",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Well-Architected Framework Application Gateway Reliability",
+ "url": "https://learn.microsoft.com/azure/well-architected/services/networking/azure-application-gateway#reliability"
+ },
+ {
+ "name": "Application Gateway V2 Overview",
+ "url": "https://learn.microsoft.com/azure/application-gateway/overview-v2"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Deploying Application Gateway in a zone-aware configuration ensures continued customer access to services even if a specific zone goes down, as services in other zones remain available.\n",
+ "pgVerified": true,
+ "description": "Deploy Application Gateway in a zone-redundant configuration",
+ "potentialBenefits": "Enhanced uptime and customer access",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/applicationGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// list Application Gateways that are not configured to use at least 2 Availability Zones\r\nresources\r\n| where type =~ \"microsoft.network/applicationGateways\"\r\n| where isnull(zones) or array_length(zones) < 2\r\n| extend zoneValue = iff((isnull(zones)), \"null\", zones)\r\n| project recommendationId = \"c9c00f2a-3888-714b-a72b-b4c9e8fcffb2\", name, id, tags, param1=\"Zones: No Zone or Zonal\", param2=strcat(\"Zones value: \", zoneValue )\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "10f02bc6-e2e7-004d-a2c2-f9bf9f16b915",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Application Gateway Connection Draining",
+ "url": "https://learn.microsoft.com/azure/application-gateway/features#connection-draining"
+ },
+ {
+ "name": "Application Gateway Connection Draining HTTP Settings",
+ "url": "https://learn.microsoft.com/azure/application-gateway/configuration-http-settings#connection-draining"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Using connection draining for backend maintenance ensures graceful removal of backend pool members during updates or health issues. It's enabled via Backend Setting and applies to all members during rule creation.\n",
+ "pgVerified": true,
+ "description": "Plan for backend maintenance by using connection draining",
+ "potentialBenefits": "Smooth updates, no dropped users",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/applicationGateways",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This query will check if connection draining is enabled\r\nresources\r\n| where type =~ \"microsoft.network/applicationGateways\"\r\n| mv-expand backendHttpSettings = properties.backendHttpSettingsCollection\r\n| extend connectionDrainingEnabled = backendHttpSettings.properties.connectionDraining.enabled\r\n| where connectionDrainingEnabled != true\r\n| extend backendPoolName = backendHttpSettings.name\r\n| project recommendationId = \"10f02bc6-e2e7-004d-a2c2-f9bf9f16b915\", name, id, tags, param1 = \"connectionDraining: Disabled\", param2 = strcat(\"backendSettingsName: \", backendPoolName)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "8364fd0a-7c0e-e240-9d95-4bf965aec243",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Application Gateway infrastructure configuration | Microsoft Learn",
+ "url": "https://learn.microsoft.com/en-us/azure/application-gateway/configuration-infrastructure#size-of-the-subnet"
+ }
+ ],
+ "recommendationControl": "Other Best Practices",
+ "longDescription": "Application Gateway v2 (Standard_v2 or WAF_v2 SKU) can support up to 125 instances. A /24 subnet isn't mandatory for deployment but is advised to provide enough space for autoscaling and maintenance upgrades.\n",
+ "pgVerified": true,
+ "description": "Ensure Application Gateway Subnet is using a /24 subnet mask",
+ "potentialBenefits": "Allows autoscaling and maintenance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/applicationGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This query will validate the subnet id for an appGW ends with a /24\r\n\r\nresources\r\n| where type =~ 'Microsoft.Network/applicationGateways'\r\n| extend subnetid = tostring(properties.gatewayIPConfigurations[0].properties.subnet.id)\r\n| join kind=leftouter(resources\r\n | where type == \"microsoft.network/virtualnetworks\"\r\n | mv-expand properties.subnets\r\n | extend subnetid = tostring(properties_subnets.id)\r\n | extend addressprefix = tostring(properties_subnets.properties.addressPrefix)\r\n | project subnetid, addressprefix) on subnetid\r\n| where addressprefix !endswith '/24'\r\n| project recommendationID = \"8364fd0a-7c0e-e240-9d95-4bf965aec243\", name, id, tags, param1 = strcat('AppGW subnet prefix: ', addressprefix)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "c72b7fee-1fa0-5b4b-98e5-54bcae95bb74",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Well Architected Framework - Azure Firewall",
+ "url": "https://learn.microsoft.com/azure/architecture/framework/services/networking/azure-firewall"
+ },
+ {
+ "name": "Deploy Azure Firewall across multiple availability zones",
+ "url": "https://learn.microsoft.com/azure/firewall/deploy-availability-zone-powershell"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure Firewall offers different SLAs depending on its deployment; in a single availability zone or across multiple, potentially improving reliability and performance.\n",
+ "pgVerified": true,
+ "description": "Deploy Azure Firewall across multiple availability zones",
+ "potentialBenefits": "Enhanced SLA and reliability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/azureFirewalls",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// List all Azure Firewalls that are not configured with multiple availability zones or deployed without a zone\r\nresources\r\n| where type == 'microsoft.network/azurefirewalls'\r\n| where array_length(zones) <= 1 or isnull(zones)\r\n| where isempty(properties.virtualHub.id) or isnull(properties.virtualHub.id)\r\n| project recommendationId = \"c72b7fee-1fa0-5b4b-98e5-54bcae95bb74\", name, id, tags, param1=\"multipleZones:false\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "3c8fa7c6-6b78-a24a-a63f-348a7c71acb9",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Firewall metrics supported in Azure Monitor",
+ "url": "https://learn.microsoft.com/azure/azure-monitor/essentials/metrics-supported#microsoftnetworkazurefirewalls"
+ },
+ {
+ "name": "Azure Firewall performance",
+ "url": "https://learn.microsoft.com/azure/firewall/firewall-performance"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Monitor Azure Firewall for overall health, processed throughput, and outbound SNAT port usage. Get alerted before limits impact services. Consider NAT gateway integration with zonal deployments; note limitations with zone redundant firewalls and secure virtual hub networks.\n",
+ "pgVerified": true,
+ "description": "Monitor Azure Firewall metrics",
+ "potentialBenefits": "Improve health and performance monitoring",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/azureFirewalls",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// List all Azure Firewalls resources in-scope, along with any metrics associated to Azure Monitor alert rules, that are not fully configured.\r\nresources\r\n| where type == \"microsoft.network/azurefirewalls\"\r\n| project firewallId = tolower(id), name, tags\r\n| join kind = leftouter (\r\n resources\r\n | where type == \"microsoft.insights/metricalerts\"\r\n | mv-expand properties.scopes\r\n | mv-expand properties.criteria.allOf\r\n | where properties_scopes contains \"azureFirewalls\"\r\n | project metricId = tolower(properties_scopes), monitoredMetric = properties_criteria_allOf.metricName, tags\r\n | summarize monitoredMetrics = make_list(monitoredMetric) by tostring(metricId)\r\n | project\r\n metricId,\r\n monitoredMetrics,\r\n allAlertsConfigured = monitoredMetrics contains(\"FirewallHealth\") and monitoredMetrics contains (\"Throughput\") and monitoredMetrics contains (\"SNATPortUtilization\")\r\n) on $left.firewallId == $right.metricId\r\n| extend alertsNotFullyConfigured = isnull(allAlertsConfigured) or not(allAlertsConfigured)\r\n| where alertsNotFullyConfigured\r\n| project recommendationId = \"c8fa7c6-6b78-a24a-a63f-348a7c71acb9\", name, id = firewallId, tags, param1 = strcat(\"MetricsAlerts:\", monitoredMetrics)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1b2dbf4a-8a0b-5e4b-8f4e-3f758188910d",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure DDoS Protection overview",
+ "url": "https://learn.microsoft.com/azure/ddos-protection/ddos-protection-overview"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Associate a DDoS protection plan with the virtual network hosting Azure Firewall to provide enhanced mitigation against DDoS attacks. Azure Firewall Manager integrates the creation of firewall infrastructure and DDoS protection plans.\n",
+ "pgVerified": true,
+ "description": "Configure DDoS Protection on the Azure Firewall VNet",
+ "potentialBenefits": "Enhanced DDoS attack defense",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/azureFirewalls",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// List all in-scope Azure Firewall resources, where the VNet is not associated to a DDoS Protection Plan\r\nresources\r\n| where type =~ \"Microsoft.Network/azureFirewalls\"\r\n| where isempty(properties.virtualHub.id) or isnull(properties.virtualHub.id)\r\n| mv-expand ipConfig = properties.ipConfigurations\r\n| project\r\n name,\r\n firewallId = id,\r\n tags,\r\n vNetName = split(ipConfig.properties.subnet.id, \"/\", 8)[0],\r\n vNetId = tolower(substring(ipConfig.properties.subnet.id, 0, indexof(ipConfig.properties.subnet.id, \"/subnet\")))\r\n| join kind=fullouter (\r\n resources\r\n | where type =~ \"Microsoft.Network/ddosProtectionPlans\"\r\n | mv-expand vNet = properties.virtualNetworks\r\n | project ddosProtectionPlanId = id, vNetId = tolower(vNet.id)\r\n )\r\n on vNetId\r\n| where isempty(ddosProtectionPlanId)\r\n| project recommendationId = \"1b2dbf4a-8a0b-5e4b-8f4e-3f758188910d\", name, id = firewallId, tags, param1 = strcat(\"vNet: \", vNetName), param2 = \"ddosProtection: Disabled\"\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "3a63560a-1ed3-6140-acd1-d1d23f9a2e12",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Firewall Policy hierarchy",
+ "url": "https://learn.microsoft.com/azure/firewall-manager/rule-hierarchy"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Azure Firewall policy supports rule hierarchies for compliance enforcement, using a central base policy with higher priority over child policies, and employs Azure custom roles to safeguard base policy and manage access within subscriptions or groups.\n",
+ "pgVerified": true,
+ "description": "Leverage Azure Policy inheritance model",
+ "potentialBenefits": "Enhanced compliance and rule hierarchy",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/azureFirewalls",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "d2e4a38e-2307-4299-a217-4c0cebc9a7f6",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Well-Architected Framework review - Azure Firewall",
+ "url": "https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-firewall#recommendations"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Configure a minimum of two to four public IP addresses per Azure Firewall to avoid SNAT exhaustion. Azure Firewall offers SNAT for all outbound traffic to public IPs, providing 2,496 SNAT ports for each additional PIP.\n",
+ "pgVerified": false,
+ "description": "Configure 2-4 PIPs for SNAT Port utilization",
+ "potentialBenefits": "Avoids SNAT exhaustion.",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/azureFirewalls",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "8faace2d-a36e-425c-aa58-2ad99e3e0b7a",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Well-Architected Framework review - Azure Firewall",
+ "url": "https://learn.microsoft.com/azure/well-architected/service-guides/azure-firewall#recommendations"
+ },
+ {
+ "name": "Azure Firewall metrics overview",
+ "url": "https://learn.microsoft.com/azure/firewall/metrics"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Creating a metric to monitor latency probes over 20ms for periods longer than 30ms helps identify when firewall instance CPUs are stressed, potentially indicating issues.\n",
+ "pgVerified": false,
+ "description": "Monitor AZFW Latency Probes metric",
+ "potentialBenefits": "Improved CPU stress detection",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/azureFirewalls",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "f6a14b32-a727-4ace-b5fa-7b1c6bdff402",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "About ExpressRoute FastPath",
+ "url": "https://learn.microsoft.com/en-us/azure/expressroute/about-fastpath"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "ExpressRoute gateways facilitate network traffic and route exchanges. FastPath enhances on-premises to virtual network data path performance by directing traffic straight to virtual machines, bypassing the gateway for improved resiliency through reduced gateway utilization.\n",
+ "pgVerified": true,
+ "description": "For better data path performance enable FastPath on ExpressRoute Direct and Gateway",
+ "potentialBenefits": "Enhances speed and resiliency",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/connections",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "a5f3a4bd-4cf1-4196-a3cb-f5a0876198b2",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Protect your Azure resources with a lock - Azure Resource Manager | Microsoft Learn",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/lock-resources?tabs=json"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Configure an Azure Resource lock for Gateway Connection resources to prevent accidental deletion and maintain connectivity between on-premises networks and Azure workloads.\n",
+ "pgVerified": true,
+ "description": "Configure an Azure Resource Lock on connections to prevent accidental deletion",
+ "potentialBenefits": "Prevents accidental deletion of connections",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/connections",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "ae054bf2-aefa-cf4a-8282-741194cef8da",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Monitoring Azure DDoS Protection",
+ "url": "https://learn.microsoft.com/en-us/azure/ddos-protection/monitor-ddos-protection-reference"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Azure DDoS Plan metrics differentiate packets and bytes by tags: Dropped (packets scrubbed by DDoS), Forwarded (packets to VIP not filtered), and No tag (total packets, sum of dropped and forwarded).\n",
+ "pgVerified": true,
+ "description": "Monitor Azure DDoS Protection Plan metrics",
+ "potentialBenefits": "Enhanced security and traffic insight",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/ddosProtectionPlans",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "4d703025-dafc-f840-a183-5dc440456134",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Designing for disaster recovery with ExpressRoute private peering",
+ "url": "https://learn.microsoft.com/azure/expressroute/designing-for-disaster-recovery-with-expressroute-privatepeering"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Connecting each ExpressRoute Gateway to a minimum of two circuits in different peering locations enhances redundancy and reliability by ensuring alternate pathways for data in case one circuit fails.\n",
+ "pgVerified": true,
+ "description": "Connect on-prem networks to Azure critical workloads via multiple ExpressRoutes",
+ "potentialBenefits": "Enhanced reliability and redundancy",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/expressRouteCircuits",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "0e19cc41-8274-1342-b0db-0e4146eacef8",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Designing for high availability with ExpressRoute",
+ "url": "https://learn.microsoft.com/en-us/azure/expressroute/designing-for-high-availability-with-expressroute"
+ },
+ {
+ "name": "Azure Well-Architected Framework review - Azure ExpressRoute - Design Checklist",
+ "url": "https://learn.microsoft.com/azure/well-architected/services/networking/azure-expressroute#recommendations"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Microsoft or the ExpressRoute provider always ensures physical redundancy in their services. It's essential to maintain this level of physical redundancy (two devices, two links) from the ExpressRoute peering location to your network for optimal performance and reliability.\n",
+ "pgVerified": true,
+ "description": "Ensure ExpressRoute's physical links connect to distinct network edge devices",
+ "potentialBenefits": "Enhanced reliability and fault tolerance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/expressRouteCircuits",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "f06a2bbe-5839-d447-9f39-fc3d20562d88",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Designing for high availability with ExpressRoute - Active-active connections",
+ "url": "https://learn.microsoft.com/azure/expressroute/designing-for-high-availability-with-expressroute#active-active-connections"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Operating both connections of an ExpressRoute circuit in active-active mode enhances high availability as the Microsoft network will load balance the traffic across the connections on a per-flow basis.\n",
+ "pgVerified": true,
+ "description": "Ensure both connections of an ExpressRoute circuit are configured in active-active mode",
+ "potentialBenefits": "Improved high availability and load balancing",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/expressRouteCircuits",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "2a5bf650-586d-db4c-a292-d922be7d3e0e",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Configure BFD over ExpressRoute",
+ "url": "https://learn.microsoft.com/azure/expressroute/expressroute-bfd"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Enabling BFD over ExpressRoute speeds up link failure detection between MSEE devices and routers configured for ExpressRoute (CE/PE), applicable over both customer and Partner Edge routing devices with managed Layer 3 service.\n",
+ "pgVerified": true,
+ "description": "Activate Bidirectional Forwarding Detection on edge devices for faster failover",
+ "potentialBenefits": "Faster link failure detection",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/expressRouteCircuits",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "9771a435-d031-814e-9827-9b5fdafc0f87",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Monitor Baseline Alerts - expressRouteCircuits",
+ "url": "https://azure.github.io/azure-monitor-baseline-alerts/services/Network/expressRouteCircuits/"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Use Network Insights for monitoring ExpressRoute circuit availability, QoS, and throughput. Set alerts based on Azure Monitor Baseline Alerts for availability, QoS metrics, and throughput metrics exceeding specific thresholds.\n",
+ "pgVerified": true,
+ "description": "Configure monitoring and alerting for ExpressRoute circuits",
+ "potentialBenefits": "Enhanced network performance and health",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/expressRouteCircuits",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "26cb547f-aabc-dc40-be02-d0a9b6b04b1a",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "How to view and configure alerts for Azure ExpressRoute circuit maintenance",
+ "url": "https://learn.microsoft.com/azure/expressroute/maintenance-alerts"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "ExpressRoute leverages service health for notifications on both planned and unplanned maintenance, ensuring users are informed about any changes to their ExpressRoute circuits.\n",
+ "pgVerified": true,
+ "description": "Configure service health to receive ExpressRoute circuit maintenance notification",
+ "potentialBenefits": "Stay informed on circuit updates",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/expressRouteCircuits",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "f902cf86-2b53-2942-abc2-781f4fb62be6",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Using S2S VPN as a backup for ExpressRoute private peering",
+ "url": "https://learn.microsoft.com/azure/expressroute/use-s2s-vpn-as-backup-for-expressroute-privatepeering"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "If you haven't added a second ExpressRoute circuit, use a site-to-site VPN as a temporary solution until the second circuit is available. This ensures network reliability and continuity of service.\n",
+ "pgVerified": true,
+ "description": "Use a site-to-site VPN as an interim backup solution for a single ExpressRoute circuit",
+ "potentialBenefits": "Ensures continuity and reliability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/expressRouteCircuits",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "60077378-7cb1-4b35-89bb-393884d9921d",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "How to configure ExpressRoute Direct Change Admin State of links",
+ "url": "https://learn.microsoft.com/en-us/azure/expressroute/expressroute-howto-erdirect#state"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "In Azure ExpressRoute Direct, the \"Admin State\" indicates the administrative status of layer 1 links, showing if a link is enabled or disabled, effectively turning the physical port on or off.\n",
+ "pgVerified": true,
+ "description": "The Admin State of both Links of an ExpressRoute Direct should be in Enabled state",
+ "potentialBenefits": "Ensures optimal connectivity.",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/ExpressRoutePorts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Express Route Directs that do not have Admin State of both Links Enabled\r\nresources\r\n| where type == \"microsoft.network/expressrouteports\"\r\n| where properties['links'][0]['properties']['adminState'] == \"Disabled\" or properties['links'][1]['properties']['adminState'] == \"Disabled\"\r\n| project recommendationId = \"60077378-7cb1-4b35-89bb-393884d9921d\", name, id, tags, param1 = strcat(\"Link1AdminState: \", properties['links'][0]['properties']['adminState']), param2 = strcat(\"Link2AdminState: \", properties['links'][1]['properties']['adminState'])\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "0bee356b-7348-4799-8cab-0c71ffe13018",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "About ExpressRoute Direct Circuit Sizes",
+ "url": "https://learn.microsoft.com/en-us/azure/expressroute/expressroute-erdirect-about?source=recommendations#circuit-sizes"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Provisioning ExpressRoute circuits on a 10-Gbps or 100-Gbps ExpressRoute Direct resource up to 20-Gbps or 200-Gbps is possible but not recommended for resiliency. If an ExpressRoute Direct port fails, and circuits are using full capacity, the remaining port won't handle the extra load.\n",
+ "pgVerified": true,
+ "description": "Ensure you do not over-subscribe an ExpressRoute Direct",
+ "potentialBenefits": "Improves resilience during port failures",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/ExpressRoutePorts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Express Route Directs that are over subscribed\r\nresources\r\n| where type == \"microsoft.network/expressrouteports\"\r\n| where toint(properties['provisionedBandwidthInGbps']) > toint(properties['bandwidthInGbps'])\r\n| project recommendationId = \"0bee356b-7348-4799-8cab-0c71ffe13018\", name, id, tags, param1 = strcat(\"provisionedBandwidthInGbps: \", properties['provisionedBandwidthInGbps']), param2 = strcat(\"bandwidthInGbps: \", properties['bandwidthInGbps'])\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "d40c769d-2f08-4980-8d8f-a386946276e6",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Rate limiting for ExpressRoute Direct circuits (Preview)",
+ "url": "https://learn.microsoft.com/en-us/azure/expressroute/rate-limit"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Rate limiting controls traffic volume between on-premises networks and Azure via ExpressRoute Direct, applying to private or Microsoft peering. It distributes port bandwidth, ensures stability, and prevents congestion, with steps outlined for enabling on circuits.\n",
+ "pgVerified": true,
+ "description": "Implement rate-limiting across ExpressRoute Direct Circuits to optimize network flow",
+ "potentialBenefits": "Optimizes network, prevents congestion",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/ExpressRoutePorts",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "55815823-d588-4cb7-a5b8-ae581837356e",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Monitor Baseline Alerts - expressRoutePorts",
+ "url": "https://azure.github.io/azure-monitor-baseline-alerts/services/Network/expressRoutePorts/"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Use Network Insights for monitoring ExpressRoute Port light levels, bits per second in/out, and line protocol. Set alerts based on Azure Monitor Baseline Alerts for light levels, bits per second in/out, and line protocol exceeding specific thresholds.\n",
+ "pgVerified": false,
+ "description": "Configure monitoring and alerting for ExpressRoute Ports",
+ "potentialBenefits": "Enhanced network performance and health",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/expressRoutePorts",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "d0cfe47f-686b-5043-bf83-5a3868acb80a",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Web Application Firewall monitoring and logging - Access Log",
+ "url": "https://learn.microsoft.com/azure/web-application-firewall/afds/waf-front-door-monitor?pivots=front-door-standard-premium#access-logs"
+ },
+ {
+ "name": "Understanding WAF logs",
+ "url": "https://learn.microsoft.com/azure/web-application-firewall/afds/waf-front-door-tuning?pivots=front-door-standard-premium#understanding-waf-logs"
+ },
+ {
+ "name": "Web Application Firewall exclusion lists",
+ "url": "https://learn.microsoft.com/azure/web-application-firewall/ag/application-gateway-waf-configuration?tabs=portal"
+ },
+ {
+ "name": "Fixing a false positive",
+ "url": "https://learn.microsoft.com/azure/web-application-firewall/ag/web-application-firewall-troubleshoot#fixing-false-positives"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "WAF may mistakenly block legitimate requests (false positives). These can be identified by examining the last 24 hours of blocked requests in Log Analytics.\n",
+ "pgVerified": true,
+ "description": "Inspect Azure Front Door WAF logs for wrongfully blocked legitimate requests",
+ "potentialBenefits": "Reduces false positives, improves access",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/frontdoorWebApplicationFirewallPolicies",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "537b4d94-edd1-4041-b13d-8217dfa485f0",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Web Application Firewall Monitoring and Logging",
+ "url": "https://learn.microsoft.com/azure/web-application-firewall/ag/application-gateway-waf-metrics#logs-and-diagnostics"
+ },
+ {
+ "name": "Diagnostic logs",
+ "url": "https://learn.microsoft.com/azure/web-application-firewall/ag/web-application-firewall-logs#diagnostic-logs"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "WAF may block legitimate requests as false positives. Identifying blocked requests within the last 24 hours through Log Analytics can help manage and mitigate these incorrect blockages efficiently.\n",
+ "pgVerified": true,
+ "description": "Check Azure Application Gateway WAF logs for mistakenly blocked valid requests",
+ "potentialBenefits": "Improve false positive identification",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/frontdoorWebApplicationFirewallPolicies",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5357ae22-0f52-1a49-9fd4-1f00ace6add0",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "WAF monitoring",
+ "url": "https://learn.microsoft.com/azure/web-application-firewall/ag/ag-overview#waf-monitoring"
+ },
+ {
+ "name": "Azure Monitor Workbook for WAF",
+ "url": "https://github.com/Azure/Azure-Network-Security/tree/master/Azure%20WAF/Workbook%20-%20WAF%20Monitor%20Workbook"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Monitoring the health of your Web Application Firewall and the applications it protects is crucial. This can be achieved through integration with Microsoft Defender for Cloud, Azure Monitor, and Azure Monitor logs, ensuring optimal performance and security.\n",
+ "pgVerified": false,
+ "description": "Monitor Web Application Firewall",
+ "potentialBenefits": "Enhanced security and health insight",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/frontdoorWebApplicationFirewallPolicies",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "38c3bca1-97a1-eb42-8cd3-838b243f35ba",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Reliability and Azure Load Balancer",
+ "url": "https://learn.microsoft.com/azure/architecture/framework/services/networking/azure-load-balancer/reliability"
+ },
+ {
+ "name": "Resiliency checklist for specific Azure services- Azure Load Balancer",
+ "url": "https://learn.microsoft.com/azure/architecture/checklist/resiliency-per-service#azure-load-balancer"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Selecting Standard SKU Load Balancer enhances reliability through availability zones and zone resiliency, ensuring deployments withstand zone and region failures. Unlike Basic, it supports global load balancing and offers an SLA.\n",
+ "pgVerified": true,
+ "description": "Use Standard Load Balancer SKU",
+ "potentialBenefits": "Enhanced reliability and SLA support",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/loadBalancers",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all LoadBalancers using Basic SKU\r\nresources\r\n| where type =~ 'Microsoft.Network/loadBalancers'\r\n| where sku.name == 'Basic'\r\n| project recommendationId = \"38c3bca1-97a1-eb42-8cd3-838b243f35ba\", name, id, tags, Param1=strcat(\"sku-tier: basic\")\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "6d82d042-6d61-ad49-86f0-6a5455398081",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Resiliency checklist for specific Azure services- Azure Load Balancer",
+ "url": "https://learn.microsoft.com/azure/architecture/checklist/resiliency-per-service#azure-load-balancer"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Deploying Azure Load Balancers with at least two instances in the backend prevents a single point of failure and supports scalability. Pairing with Virtual Machine Scale Sets is advised for optimal scale building.\n",
+ "pgVerified": true,
+ "description": "Ensure the Backend Pool contains at least two instances",
+ "potentialBenefits": "Enhances reliability and scalability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/loadBalancers",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all LoadBalancers which only have 1 backend pool defined or only 1 VM in the backend pool\r\nresources\r\n| where type =~ 'Microsoft.Network/loadBalancers'\r\n| extend bep = properties.backendAddressPools\r\n| extend BackEndPools = array_length(bep)\r\n| where BackEndPools == 0\r\n| project recommendationId = \"6d82d042-6d61-ad49-86f0-6a5455398081\", name, id, Param1=\"backendPools\", Param2=toint(0), tags\r\n| union (resources\r\n | where type =~ 'Microsoft.Network/loadBalancers'\r\n | where sku.name == \"Standard\"\r\n | extend bep = properties.backendAddressPools\r\n | extend BackEndPools = toint(array_length(bep))\r\n | mv-expand bip = properties.backendAddressPools\r\n | extend BackendAddresses = array_length(bip.properties.loadBalancerBackendAddresses)\r\n | where toint(BackendAddresses) <= 1\r\n | project recommendationId = \"6d82d042-6d61-ad49-86f0-6a5455398081\", name, id, tags, Param1=\"backendAddresses\", Param2=toint(BackendAddresses))\r\n| union (\r\n resources\r\n | where type =~ 'Microsoft.Network/loadBalancers'\r\n | where sku.name == \"Basic\"\r\n | mv-expand properties.backendAddressPools\r\n | extend backendPoolId = properties_backendAddressPools.id\r\n | project id, name, tags, tostring(backendPoolId), recommendationId = \"6d82d042-6d61-ad49-86f0-6a5455398081\", Param1=\"BackEndPools\"\r\n | join kind = leftouter (\r\n resources\r\n | where type =~ \"Microsoft.Network/networkInterfaces\"\r\n | mv-expand properties.ipConfigurations\r\n | mv-expand properties_ipConfigurations.properties.loadBalancerBackendAddressPools\r\n | extend backendPoolId = tostring(properties_ipConfigurations_properties_loadBalancerBackendAddressPools.id)\r\n | summarize poolMembers = count() by backendPoolId\r\n | project tostring(backendPoolId), poolMembers ) on backendPoolId\r\n | where toint(poolMembers) <= 1\r\n | extend BackendAddresses = poolMembers\r\n | project id, name, tags, recommendationId, Param1=\"backendAddresses\", Param2=toint(BackendAddresses))\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "8d319a05-677b-944f-b9b4-ca0fb42e883c",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Resiliency checklist for specific Azure services- Azure Load Balancer",
+ "url": "https://learn.microsoft.com/azure/architecture/checklist/resiliency-per-service#azure-load-balancer"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Outbound rules for Standard Public Load Balancer involve manual port allocation for backend pools, limiting scalability and risk of SNAT port exhaustion. NAT Gateway is recommended for its dynamic scaling and secure internet connectivity.\n",
+ "pgVerified": true,
+ "description": "Use NAT Gateway instead of Outbound Rules for Production Workloads",
+ "potentialBenefits": "Enhanced scalability and reliability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/loadBalancers",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all LoadBalancers with Outbound rules configured\r\nresources\r\n| where type =~ 'Microsoft.Network/loadBalancers'\r\n| extend outboundRules = array_length(properties.outboundRules)\r\n| where outboundRules > 0\r\n| project recommendationId = \"8d319a05-677b-944f-b9b4-ca0fb42e883c\", name, id, tags, Param1 = \"outboundRules: >=1\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "621dbc78-3745-4d32-8eac-9e65b27b7512",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Load Balancer and Availability Zones",
+ "url": "https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-standard-availability-zones#zone-redundant"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "In regions with Availability Zones, assigning a zone-redundant frontend IP to a Standard Load Balancer ensures continuous traffic distribution even if one availability zone fails, provided other healthy zones and backend instances are available to receive the traffic.\n",
+ "pgVerified": true,
+ "description": "Ensure Standard Load Balancer is zone-redundant",
+ "potentialBenefits": "Enhances uptime and resilience",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/loadBalancers",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all LoadBalancers with with regional or zonal public IP Addresses\r\nresources\r\n| where type == \"microsoft.network/loadbalancers\"\r\n| where tolower(sku.name) != 'basic'\r\n| mv-expand feIPconfigs = properties.frontendIPConfigurations\r\n| extend\r\n feConfigName = (feIPconfigs.name),\r\n PrivateSubnetId = toupper(feIPconfigs.properties.subnet.id),\r\n PrivateIPZones = feIPconfigs.zones,\r\n PIPid = toupper(feIPconfigs.properties.publicIPAddress.id),\r\n JoinID = toupper(id)\r\n| where isnotempty(PrivateSubnetId)\r\n| where isnull(PrivateIPZones) or array_length(PrivateIPZones) < 2\r\n| project name, feConfigName, id\r\n| union (resources\r\n | where type == \"microsoft.network/loadbalancers\"\r\n | where tolower(sku.name) != 'basic'\r\n | mv-expand feIPconfigs = properties.frontendIPConfigurations\r\n | extend\r\n feConfigName = (feIPconfigs.name),\r\n PIPid = toupper(feIPconfigs.properties.publicIPAddress.id),\r\n JoinID = toupper(id)\r\n | where isnotempty(PIPid)\r\n | join kind=innerunique (\r\n resources\r\n | where type == \"microsoft.network/publicipaddresses\"\r\n | where isnull(zones) or array_length(zones) < 2\r\n | extend\r\n LBid = toupper(substring(properties.ipConfiguration.id, 0, indexof(properties.ipConfiguration.id, '/frontendIPConfigurations'))),\r\n InnerID = toupper(id)\r\n ) on $left.PIPid == $right.InnerID)\r\n| project recommendationId = \"621dbc78-3745-4d32-8eac-9e65b27b7512\", name, id, tags, param1=\"Zones: No Zone or Zonal\", param2=strcat(\"Frontend IP Configuration:\", \" \", feConfigName)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e5f5fcea-f925-4578-8599-9a391e888a60",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Load Balancer Health Probe Overview",
+ "url": "https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-custom-probe-overview"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Health probes are used by Azure Load Balancers to determine the status of backend endpoints. Using custom health probes that are aligned with vendor recommendations enhances understanding of backend availability and facilitates monitoring of backend services for any impact.\n",
+ "pgVerified": true,
+ "description": "Use Health Probes to detect backend instances availability",
+ "potentialBenefits": "Ensures backend uptime monitoring.",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/loadBalancers",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// List the load balancers which don't have health probe configured\r\nresources\r\n| where type =~ \"microsoft.network/loadbalancers\"\r\n| where array_length(properties.probes) == 0\r\n| project recommendationId=\"e5f5fcea-f925-4578-8599-9a391e888a60\", name, id, tags, param1=\"customHealthProbeUsed: false\"\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "4281631c-3d19-4994-8d96-084c2a51a534",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Scale a NAT gateway to meet the demand of a dynamic workload",
+ "url": "https://learn.microsoft.com/en-us/azure/nat-gateway/nat-gateway-design#scale-a-nat-gateway-to-meet-the-demand-of-a-dynamic-workload"
+ },
+ {
+ "name": "Total SNAT Connection Count",
+ "url": "https://learn.microsoft.com/en-us/azure/nat-gateway/nat-metrics#total-snat-connection-count"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "NAT Gateway provides 64,512 SNAT ports per public IP address and supports up to 16 public IP addresses. Monitor \"Total SNAT connection count\" metric to determine if you're nearing the connection limit of NAT gateway. You can scale the NAT gateway by adding more public IP addresses.\n",
+ "pgVerified": false,
+ "description": "Scale a NAT gateway to meet the demand of a dynamic workload",
+ "potentialBenefits": "Enhances reliability and scalability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/natGateways",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "babf75d6-6407-4d90-b01e-5a1768e621f5",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "What is Azure NAT Gateway metrics and alerts?",
+ "url": "https://learn.microsoft.com/en-us/azure/nat-gateway/nat-metrics"
+ },
+ {
+ "name": "AMBA - NAT Gateway",
+ "url": "https://azure.github.io/azure-monitor-baseline-alerts/services/Network/natGateways/"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Use Network Insights for monitoring and alerting on your NAT gateway.Use Total SNAT connection count metric to determine if you're nearing the connection limit of NAT gateway. Set alerts based on Azure Monitor Baseline Alerts (AMBA) thresholds for NAT Gateway\n",
+ "pgVerified": false,
+ "description": "Configure monitoring and alerting for NAT gateway",
+ "potentialBenefits": "Enhanced network performance and health",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/natGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "419df1ea-336b-460a-b6b2-fefe2588fcef",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Zonal NAT gateway resource for each zone in a region to create zone-resiliency",
+ "url": "https://learn.microsoft.com/en-us/azure/nat-gateway/nat-availability-zones#zonal-nat-gateway-resource-for-each-zone-in-a-region-to-create-zone-resiliency"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "A zonal promise for zone isolation scenarios exists when a virtual machine instance using a NAT gateway resource is in the same zone as the NAT gateway resource and its public IP addresses. The pattern you want to use for zone isolation is creating a \"zonal stack\" per availability zone.\n",
+ "pgVerified": false,
+ "description": "Consider zonal NAT gateway deployment for zone isolation scenarios",
+ "potentialBenefits": "Enhances reliability and scalability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/natGateways",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "d2976d3e-294b-4b49-a1f0-c42566a3758f",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Diagnostic settings in Azure Monitor",
+ "url": "https://learn.microsoft.com/azure/azure-monitor/essentials/diagnostic-settings"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Resource Logs are not collected and stored until you create a diagnostic setting and route them to one or more locations.\n",
+ "pgVerified": true,
+ "description": "Configure Diagnostic Settings for all network security groups",
+ "potentialBenefits": "Enhanced monitoring and security insights",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/networkSecurityGroups",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "8bb4a57b-55e4-d24e-9c19-2679d8bc779f",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Monitor activity log",
+ "url": "https://learn.microsoft.com/azure/azure-monitor/essentials/activity-log?tabs=powershell"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Create Alerts with Azure Monitor for operations like creating or updating Network Security Group rules to catch unauthorized/undesired changes to resources and spot attempts to bypass firewalls or access resources from the outside.\n",
+ "pgVerified": true,
+ "description": "Monitor changes in Network Security Groups with Azure Monitor",
+ "potentialBenefits": "Enhanced security and change monitoring",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/networkSecurityGroups",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Network Security Groups without alerts for modification configured.\r\nresources\r\n| where type =~ \"Microsoft.Network/networkSecurityGroups\"\r\n| project name, id, tags, lowerCaseNsgId = tolower(id)\r\n| join kind = leftouter (\r\n resources\r\n | where type =~ \"Microsoft.Insights/activityLogAlerts\" and properties.enabled == true\r\n | mv-expand scope = properties.scopes\r\n | where scope has \"Microsoft.Network/networkSecurityGroups\"\r\n | project alertName = name, conditionJson = dynamic_to_json(properties.condition.allOf), scope\r\n | where conditionJson has '\"Administrative\"' and (\r\n // Create or Update Network Security Group\r\n (conditionJson has '\"Microsoft.Network/networkSecurityGroups/write\"') or\r\n // All administrative operations\r\n (conditionJson !has '\"Microsoft.Network/networkSecurityGroups/write\"' and conditionJson !has '\"Microsoft.Network/networkSecurityGroups/delete\"' and conditionJson !has '\"Microsoft.Network/networkSecurityGroups/join/action\"')\r\n )\r\n | project lowerCaseNsgIdOfScope = tolower(scope)\r\n )\r\n on $left.lowerCaseNsgId == $right.lowerCaseNsgIdOfScope\r\n| where isempty(lowerCaseNsgIdOfScope)\r\n| project recommendationId = \"8bb4a57b-55e4-d24e-9c19-2679d8bc779f\", name, id, tags, param1 = \"ModificationAlert: Not configured/Disabled\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "52ac35e8-9c3e-f84d-8ce8-2fab955333d3",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Lock your resources to protect your infrastructure",
+ "url": "https://learn.microsoft.com/azure/azure-resource-manager/management/lock-resources?toc=%2Fazure%2Fvirtual-network%2Ftoc.json&tabs=json"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "As an administrator, you can lock an Azure subscription, resource group, or resource to protect them from accidental deletions and modifications. The lock overrides user permissions. Locks can prevent either deletions or modifications and are known as Delete and Read-only in the portal.\n",
+ "pgVerified": true,
+ "description": "Configure locks for Network Security Groups to avoid accidental changes and/or deletion",
+ "potentialBenefits": "Prevents accidental edits/deletions",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/networkSecurityGroups",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "da1a3c06-d1d5-a940-9a99-fcc05966fe7c",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Flow logging for network security groups",
+ "url": "https://learn.microsoft.com/azure/network-watcher/network-watcher-nsg-flow-logging-overview"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Monitoring, managing, and understanding your network is crucial for protection and optimization. Knowing the current state, who and from where connections are made, open internet ports, expected and irregular behavior, and traffic spikes is essential.\n",
+ "pgVerified": true,
+ "description": "Configure NSG Flow Logs",
+ "potentialBenefits": "Enhances security and optimizes network",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/networkSecurityGroups",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Network Security Groups without NSG Flow logs configured or disabled.\r\nresources\r\n| where type =~ \"Microsoft.Network/networkSecurityGroups\"\r\n| project name, id, tags, lowerCaseNsgId = tolower(id)\r\n| join kind = leftouter (\r\n resources\r\n | where type == \"microsoft.network/networkwatchers/flowlogs\" and properties.enabled == true\r\n | project flowLogName = name, lowerCaseTargetNsgId = tolower(properties.targetResourceId)\r\n )\r\n on $left.lowerCaseNsgId == $right.lowerCaseTargetNsgId\r\n| where isempty(lowerCaseTargetNsgId)\r\n| project recommendationId = \"da1a3c06-d1d5-a940-9a99-fcc05966fe7c\", name, id, tags, param1 = \"NSGFlowLog: Not configured/Disabled\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "8291c1fa-650c-b44b-b008-4deb7465919d",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Security rules",
+ "url": "https://learn.microsoft.com/azure/virtual-network/network-security-groups-overview#security-rules"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Azure network security groups filter network traffic between resources in a virtual network, using security rules to allow or deny inbound or outbound traffic based on source, destination, port, and protocol.\n",
+ "pgVerified": true,
+ "description": "The NSG only has Default Security Rules, make sure to configure the necessary rules",
+ "potentialBenefits": "Enhanced traffic control and security",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/networkSecurityGroups",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This query will return all NSGs that have NO security rules\r\nresources\r\n| where type =~ \"microsoft.network/networksecuritygroups\"\r\n| extend sr = string_size(properties.securityRules)\r\n| where sr <=2 or isnull(properties.securityRules)\r\n| project recommendationId = \"8291c1fa-650c-b44b-b008-4deb7465919d\", name, id\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "4e133bd0-8762-bc40-a95b-b29142427d73",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "What is Azure Network Watcher?",
+ "url": "https://learn.microsoft.com/azure/network-watcher/network-watcher-overview"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Azure Network Watcher offers tools for monitoring, diagnosing, viewing metrics, and managing logs for IaaS resources. It helps maintain the health of VMs, VNets, application gateways, load balancers, but not for PaaS or Web analytics.\n",
+ "pgVerified": true,
+ "description": "Deploy Network Watcher in all regions where you have networking services",
+ "potentialBenefits": "Enhanced monitoring and diagnostics for Azure IaaS",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/networkWatchers",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This query will return all locations that do not have a Network Watcher deployed\r\nresources\r\n| where location != \"global\"\r\n| union (Resources\r\n | where type =~ \"microsoft.network/networkwatchers\")\r\n| summarize NetworkWatcherCount = countif(type =~ 'Microsoft.Network/networkWatchers') by location\r\n| where NetworkWatcherCount == 0\r\n| project recommendationId = \"4e133bd0-8762-bc40-a95b-b29142427d73\", name=location, id=\"n/a\", param1 = strcat(\"LocationMisingNetworkWatcher:\", location)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "22a769ed-0ecb-8b49-bafe-8f52e6373d9c",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Manage NSG flow logs using the Azure portal",
+ "url": "https://learn.microsoft.com/azure/network-watcher/nsg-flow-logging"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Network security group flow logging is a feature of Azure Network Watcher that logs IP traffic info through a network security group. If in Failed state, monitoring data from the associated resource is not collected.\n",
+ "pgVerified": true,
+ "description": "Fix Flow Log configurations in Failed state or Disabled Status",
+ "potentialBenefits": "Ensures IP traffic logging",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/networkWatchers",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This query will return all Network Watcher Flow Logs that are not enabled or in a succeeded state\r\nresources\r\n| where type =~ \"microsoft.network/networkwatchers/flowlogs\" and isnotnull(properties)\r\n| extend targetResourceId = tostring(properties.targetResourceId)\r\n| extend status = iff(properties.enabled =~ 'true', \"Enabled\", \"Disabled\")\r\n| extend provisioningState = tostring(properties.provisioningState)\r\n| extend flowLogType = iff(properties.targetResourceId contains \"Microsoft.Network/virtualNetworks\", 'Virtual network', 'Network security group')\r\n| where provisioningState != \"Succeeded\" or status != \"Enabled\"\r\n| project recommendationId = \"22a769ed-0ecb-8b49-bafe-8f52e6373d9c\", name, id, tags, param1 = strcat(\"provisioningState:\", provisioningState), param2=strcat(\"Status:\", status), param3=strcat(\"targetResourceId:\",targetResourceId), param4=strcat(\"flowLogType:\",flowLogType)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "2820f6d6-a23c-7a40-aec5-506f3bd1aeb6",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Protecting private DNS Zones and Records - Azure DNS",
+ "url": "https://learn.microsoft.com/en-us/azure/dns/dns-protect-private-zones-recordsets"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Private DNS zones and records are critical and their deletion can cause service outages. To protect against unauthorized or accidental changes, the Private DNS Zone Contributor role, a built-in role for managing these resources, should be assigned to specific users or groups.\n",
+ "pgVerified": true,
+ "description": "Protect private DNS zones and records",
+ "potentialBenefits": "Prevents DNS outages",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/privateDnsZones",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "ab896e8c-49b9-2c44-adec-98339aff7821",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Supported metrics for Microsoft.Network/privateDnsZones",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-monitor/reference/supported-metrics/microsoft-network-privatednszones-metrics"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "The records in a private DNS zone are only resolvable from linked virtual networks. You can link a private DNS zone to multiple networks and enable autoregistration to manage DNS records for virtual machines automatically.\n",
+ "pgVerified": true,
+ "description": "Monitor Private DNS Zones health and set up alerts",
+ "potentialBenefits": "Enhanced DNS reliability and alerting",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/privateDnsZones",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1e02335c-1f90-fd4e-a5a5-d359c7b22d70",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Scenarios for Azure Private DNS zones",
+ "url": "https://learn.microsoft.com/en-us/azure/dns/private-dns-scenarios"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Azure Private DNS offers a reliable, secure way to handle domain names within virtual networks, using custom domains instead of default Azure names. Records in these zones aren't internet-accessible, only resolvable within linked virtual networks.\n",
+ "pgVerified": true,
+ "description": "Align Production and DR zones with identical workload and resource failover entries",
+ "potentialBenefits": "Ensures seamless failover for DNS",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/privateDnsZones",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b89c9acc-0aba-fb44-9ff2-3dbfcf97dce7",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Private endpoint connections",
+ "url": "https://learn.microsoft.com/azure/private-link/manage-private-endpoint?tabs=manage-private-link-powershell#private-endpoint-connections"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "A private endpoint has two custom properties, static IP address and the network interface name, which must be set at creation. If not in Succeeded state, there may be issues with the endpoint or associated resource.\n",
+ "pgVerified": true,
+ "description": "Resolve issues with Private Endpoints in non Succeeded connection state",
+ "potentialBenefits": "Ensure connection availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/privateEndpoints",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This query will return all Private Endpoints that are not in a Succeeded state\r\nresources\r\n| where type =~ \"microsoft.network/privateendpoints\"\r\n| where properties.provisioningState != \"Succeeded\" or properties.privateLinkServiceConnections[0].properties.provisioningState != \"Succeeded\"\r\n| project recommendationId = \"b89c9acc-0aba-fb44-9ff2-3dbfcf97dce7\", name, id, tags, param1 = strcat(\"provisioningState: \", tostring(properties.provisioningState)), param2 = strcat(\"provisioningState: \", tostring(properties.privateLinkServiceConnections[0].properties.provisioningState))\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "c63b81fb-7afc-894c-a840-91bb8a8dcfaf",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Public IP addresses - Availability Zones",
+ "url": "https://learn.microsoft.com/azure/virtual-network/ip-services/public-ip-addresses#availability-zone"
+ },
+ {
+ "name": "Upgrading a basic public IP address to Standard SKU",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/public-ip-basic-upgrade-guidance#steps-to-complete-the-upgrade"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Public IP addresses in Azure can be of standard SKU, available as non-zonal, zonal, or zone-redundant. Zone-redundant IPs are accessible across all zones, resisting any single zone failure, thereby providing higher resilience.\n",
+ "pgVerified": true,
+ "description": "Use Standard SKU and Zone-Redundant IPs when applicable",
+ "potentialBenefits": "Enhanced resilience with zone redundancy",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/publicIPAddresses",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph query\r\n// List public IP addresses that are not Zone-Redundant\r\nResources\r\n| where type =~ \"Microsoft.Network/publicIPAddresses\" and sku.tier =~ \"Regional\"\r\n| where isempty(zones) or array_length(zones) <= 1\r\n| extend az = case(isempty(zones), \"Non-zonal\", array_length(zones) <= 1, strcat(\"Zonal (\", strcat_array(zones, \",\"), \")\"), zones)\r\n| project recommendationId = \"c63b81fb-7afc-894c-a840-91bb8a8dcfaf\", name, id, tags, param1 = strcat(\"sku: \", sku.name), param2 = strcat(\"availabilityZone: \", az)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1adba190-5c4c-e646-8527-dd1b2a6d8b15",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Use NAT GW for outbound connectivity",
+ "url": "https://learn.microsoft.com/azure/advisor/advisor-reference-reliability-recommendations#use-nat-gateway-for-outbound-connectivity"
+ },
+ {
+ "name": "TCP and SNAT Ports",
+ "url": "https://learn.microsoft.com/azure/architecture/framework/services/compute/azure-app-service/reliability#tcp-and-snat-ports"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Prevent connectivity failures due to SNAT port exhaustion by employing NAT gateway for outbound traffic from virtual networks, ensuring dynamic scaling and secure internet connections.\n",
+ "pgVerified": true,
+ "description": "Use NAT gateway for outbound connectivity to avoid SNAT Exhaustion",
+ "potentialBenefits": "Avoids SNAT port exhaustion risks",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/publicIPAddresses",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph query\r\n// Lists VMs with PIPs\r\nresources\r\n| where type =~ 'Microsoft.Network/publicIPAddresses'\r\n| where tostring(properties.ipConfiguration.id) contains \"microsoft.network/networkinterfaces\"\r\n| project recommendationid=\"1adba190-5c4c-e646-8527-dd1b2a6d8b15\", name, id, tags, param1=strcat(\"Migrate from instance IP to NAT Gateway\")\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5cea1501-6fe4-4ec4-ac8f-f72320eb18d3",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Upgrading a basic public IP address to Standard SKU - Guidance",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/public-ip-basic-upgrade-guidance"
+ },
+ {
+ "name": "Upgrade to Standard SKU public IP addresses in Azure by 30 September 2025 as Basic SKU will be retired",
+ "url": "https://azure.microsoft.com/en-us/updates/upgrade-to-standard-sku-public-ip-addresses-in-azure-by-30-september-2025-basic-sku-will-be-retired/"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Basic SKU public IP addresses will be retired on September 30, 2025. Users are advised to upgrade to Standard SKU public IP addresses before this date to avoid service disruptions.\n",
+ "pgVerified": true,
+ "description": "Upgrade Basic SKU public IP addresses to Standard SKU",
+ "potentialBenefits": "Avoids service disruption",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/publicIPAddresses",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph query\r\n// List Basic SKU public IP addresses\r\nResources\r\n| where type =~ \"Microsoft.Network/publicIPAddresses\"\r\n| where sku.name =~ \"Basic\"\r\n| project recommendationId = \"5cea1501-6fe4-4ec4-ac8f-f72320eb18d3\", name, id, tags, param1 = strcat(\"sku: \", sku.name)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "23b2dfc7-7e5d-9443-9f62-980ca621b561",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure activity log - Azure Monitor | Microsoft Learn",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/activity-log?tabs=powershell"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Create Alerts with Azure Monitor for operations like Create or Update Route Table to spot unauthorized/undesired changes in production resources. This setup aids in identifying improper routing changes, including efforts to evade firewalls or access resources from outside.\n",
+ "pgVerified": true,
+ "description": "Monitor changes in Route Tables with Azure Monitor",
+ "potentialBenefits": "Enhanced security and change detection",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/routeTables",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Route Tables without alerts for modification configured.\r\nresources\r\n| where type =~ \"Microsoft.Network/routeTables\"\r\n| project name, id, tags, lowerCaseRouteTableId = tolower(id)\r\n| join kind = leftouter (\r\n resources\r\n | where type =~ \"Microsoft.Insights/activityLogAlerts\" and properties.enabled == true\r\n | mv-expand scope = properties.scopes\r\n | where scope has \"Microsoft.Network/routeTables\"\r\n | project alertName = name, conditionJson = dynamic_to_json(properties.condition.allOf), scope\r\n | where conditionJson has '\"Administrative\"' and (\r\n // Create or Update Route Table\r\n (conditionJson has '\"Microsoft.Network/routeTables/write\"') or\r\n // All Administrative operations\r\n (conditionJson !has '\"Microsoft.Network/routeTables/write\"' and conditionJson !has '\"Microsoft.Network/routeTables/delete\"' and conditionJson !has '\"Microsoft.Network/routeTables/join/action\"')\r\n )\r\n | project lowerCaseRouteTableIdOfScope = tolower(scope)\r\n )\r\n on $left.lowerCaseRouteTableId == $right.lowerCaseRouteTableIdOfScope\r\n| where isempty(lowerCaseRouteTableIdOfScope)\r\n| project recommendationId = \"23b2dfc7-7e5d-9443-9f62-980ca621b561\", name, id, tags, param1 = \"ModificationAlert: Not configured/Disabled\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "89d1166a-1a20-0f46-acc8-3194387bf127",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Protect your Azure resources with a lock - Azure Resource Manager | Microsoft Learn",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/lock-resources?toc=%2Fazure%2Fvirtual-network%2Ftoc.json&tabs=json"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "As an administrator, you can protect Azure subscriptions, resource groups, or resources from accidental deletions and modifications by setting locks.\n",
+ "pgVerified": true,
+ "description": "Configure locks for Route Tables to avoid accidental changes or deletion",
+ "potentialBenefits": "Prevents accidental edits/deletions",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/routeTables",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "f05a3e6d-49db-2740-88e2-2b13706c1f67",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Traffic Manager endpoint monitoring",
+ "url": "https://learn.microsoft.com/azure/traffic-manager/traffic-manager-monitoring"
+ },
+ {
+ "name": "Enable or disable health checks",
+ "url": "https://learn.microsoft.com/azure/traffic-manager/traffic-manager-monitoring#enable-or-disable-health-checks-preview"
+ },
+ {
+ "name": "Troubleshooting degraded state on Azure Traffic Manager",
+ "url": "https://learn.microsoft.com/azure/traffic-manager/traffic-manager-troubleshooting-degraded"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Monitor status should be online to ensure failover for application workload. If Traffic Manager's health shows Degraded, one or more endpoints may also be Degraded.\n",
+ "pgVerified": true,
+ "description": "Traffic Manager Monitor Status Should be Online",
+ "potentialBenefits": "Ensures failover functionality",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/trafficManagerProfiles",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find traffic manager profiles that have an endpoint monitor status of not 'Online'\r\nresources\r\n| where type == \"microsoft.network/trafficmanagerprofiles\"\r\n| mv-expand properties.endpoints\r\n| where properties_endpoints.properties.endpointMonitorStatus != \"Online\"\r\n| project recommendationId = \"f05a3e6d-49db-2740-88e2-2b13706c1f67\", name, id, tags, param1 = strcat('Profile name: ',properties_endpoints.name), param2 = strcat('endpointMonitorStatus: ', properties_endpoints.properties.endpointMonitorStatus)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5b422a7f-8caa-3d48-becb-511599e5bba9",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Traffic Manager Endpoint Types",
+ "url": "https://learn.microsoft.com/azure/traffic-manager/traffic-manager-endpoint-types"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "When configuring the Azure traffic manager, provision at least two endpoints to ensure workloads can fail-over to another instance, enhancing reliability and availability.\n",
+ "pgVerified": true,
+ "description": "Traffic manager profiles should have more than one endpoint",
+ "potentialBenefits": "Enhances failover capabilities",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/trafficManagerProfiles",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find traffic manager profiles that have less than 2 endpoints\r\nresources\r\n| where type == \"microsoft.network/trafficmanagerprofiles\"\r\n| where array_length(properties.endpoints) < 2\r\n| project recommendationId = \"5b422a7f-8caa-3d48-becb-511599e5bba9\", name, id, tags, param1 = strcat('EndpointCount: ', array_length(properties.endpoints))\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1ad9d7b7-9692-1441-a8f4-93792efbe97a",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Reliability recommendations",
+ "url": "https://learn.microsoft.com/azure/advisor/advisor-reference-reliability-recommendations#add-at-least-one-more-endpoint-to-the-profile-preferably-in-another-azure-region"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Profiles should have multiple endpoints to ensure availability in case an endpoint fails. It's also advised to distribute these endpoints across different regions for enhanced reliability.\n",
+ "pgVerified": true,
+ "description": "Configure at least one endpoint within a another region",
+ "potentialBenefits": "Enhances availability across regions",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/trafficManagerProfiles",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "c31f76a0-48cd-9f44-aa43-99ee904db9bc",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Add an endpoint configured to \"All (World)\"",
+ "url": "https://learn.microsoft.com/azure/advisor/advisor-reference-reliability-recommendations#add-an-endpoint-configured-to-all-world"
+ },
+ {
+ "name": "Traffic Manager profile - GeographicProfile (Add an endpoint configured to \"\"All (World)\"\").",
+ "url": "https://aka.ms/Rf7vc5"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "For geographic routing, traffic is directed to endpoints based on specific regions. If a region fails, without a predefined failover, configuring an endpoint to \"All (World)\" for geographic profiles can prevent traffic black holes, ensuring service remains available.\n",
+ "pgVerified": true,
+ "description": "Ensure endpoint configured to (All World) for geographic profiles",
+ "potentialBenefits": "Avoids traffic black holing, ensures availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/trafficManagerProfiles",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of Traffic Manager resources that are not confirgured for all-World access\r\nResources\r\n| where type == 'microsoft.network/trafficmanagerprofiles'\r\n| where properties.trafficRoutingMethod =~ \"Geographic\"\r\n| extend endpoints = properties.endpoints\r\n| mv-expand endpoint = endpoints\r\n| where endpoint.properties.geoMapping !contains \"WORLD\"\r\n| extend endpointName = endpoint.name\r\n| project recommendationId=\"c31f76a0-48cd-9f44-aa43-99ee904db9bc\", name, id, tags, param1=strcat(\"endpointName:\",endpointName), param2=strcat(\"GeoMapping:\", tostring(endpoint.properties.geoMapping))\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "d37db635-157f-584d-9bce-4f6fc8c65ce5",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Designing for disaster recovery with ExpressRoute private peering",
+ "url": "https://learn.microsoft.com/azure/expressroute/designing-for-disaster-recovery-with-expressroute-privatepeering"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "To increase reliability, it's advised that each ExpressRoute gateway connects to at least two circuits, with each circuit originating from a different peering location than the other, ensuring diverse connectivity paths for enhanced resilience.\n",
+ "pgVerified": true,
+ "description": "Connect ExpressRoute gateway with circuits from diverse peering locations for resilience",
+ "potentialBenefits": "Enhanced resiliency for Azure service",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworkGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of ExpressRoute Gateways that are not connected to two or more ExpressRoute Circuits. Baremetal circuits are excluded from consideration\r\n//This query assumes that the running entity has visibilty to the gateway, connection, and circuit scopes.\r\n//Start with a full list of gateways\r\n(resources\r\n| where type == \"microsoft.network/virtualnetworkgateways\"\r\n| where properties.gatewayType == \"ExpressRoute\"\r\n| extend exrGatewayId = tolower(tostring(id))\r\n| join kind=inner(\r\nresources\r\n| where type == \"microsoft.network/virtualnetworkgateways\"\r\n| where properties.gatewayType == \"ExpressRoute\"\r\n| extend exrGatewayId = tolower(tostring(id))\r\n| join kind=leftouter(\r\n//connections joined with circuit peer info\r\nresources\r\n| where type == \"microsoft.network/connections\"\r\n| extend connectionType = properties.connectionType\r\n| extend exrGatewayId = tolower(tostring(properties.virtualNetworkGateway1.id))\r\n| extend peerId = tolower(tostring(properties.peer.id))\r\n| extend connectionId = tolower(tostring(id))\r\n| where connectionType == \"ExpressRoute\"\r\n| join kind=leftouter(\r\n resources\r\n | where type == \"microsoft.network/expressroutecircuits\"\r\n //should this be location instead of peeringLocation\r\n | extend circuitId = tolower(tostring(id))\r\n | extend peeringLocation = tostring(properties.serviceProviderProperties.peeringLocation)\r\n | extend peerId = tolower(id)\r\n) on peerId ) on exrGatewayId\r\n//remove bare metal services connections/circuits\r\n| where not(isnotnull(connectionId) and isnull(sku1))\r\n//group by gateway ID's and peering locations\r\n| summarize by exrGatewayId, peeringLocation\r\n//summarize to connections with fewer than two unique connections\r\n| summarize connCount = count() by exrGatewayId\r\n| where connCount < 2) on exrGatewayId\r\n| project recommendationId = \"d37db635-157f-584d-9bce-4f6fc8c65ce5\", name, id, tags, param1 = \"twoOrMoreCircuitsConnectedFromDifferentPeeringLocations: false\")\r\n| union\r\n(\r\nresources\r\n| where type == \"microsoft.network/virtualnetworkgateways\"\r\n| where properties.gatewayType == \"ExpressRoute\"\r\n| extend exrGatewayId = tolower(tostring(id))\r\n| join kind=leftouter(\r\n//connections joined with circuit peer info\r\nresources\r\n| where type == \"microsoft.network/connections\"\r\n| extend connectionType = properties.connectionType\r\n| extend exrGatewayId = tolower(tostring(properties.virtualNetworkGateway1.id))\r\n| extend peerId = tolower(tostring(properties.peer.id))\r\n| extend connectionId = tolower(tostring(id))\r\n| where connectionType == \"ExpressRoute\") on exrGatewayId\r\n| where isnull(connectionType)\r\n| project recommendationId = \"d37db635-157f-584d-9bce-4f6fc8c65ce5\", name, id, tags, param1 = \"twoOrMoreCircuitsConnectedFromDifferentPeeringLocations: false\", param2 = \"noConnectionsOnGateway: true\"\r\n)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "bbe668b7-eb5c-c746-8b82-70afdedf0cae",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "About ExpressRoute virtual network gateways - Zone-redundant gateway SKUs",
+ "url": "https://learn.microsoft.com/azure/expressroute/expressroute-about-virtual-network-gateways#zrgw"
+ },
+ {
+ "name": "About zone-redundant virtual network gateway in Azure availability zones",
+ "url": "https://learn.microsoft.com/azure/vpn-gateway/about-zone-redundant-vnet-gateways"
+ },
+ {
+ "name": "Create a zone-redundant virtual network gateway in Azure Availability Zones",
+ "url": "https://learn.microsoft.com/azure/vpn-gateway/create-zone-redundant-vnet-gateway"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure ExpressRoute gateway offers variable SLAs based on deployment in single or multiple availability zones. To deploy virtual network gateways across zones automatically, use zone-redundant gateways for accessing critical, scalable services with increased resilience.\n",
+ "pgVerified": true,
+ "description": "Use Zone-redundant ExpressRoute gateway SKUs",
+ "potentialBenefits": "Enhanced SLA and resilience",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworkGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// For all VNGs of type ExpressRoute, show any that do not have AZ in the SKU tier\r\nresources\r\n| where type =~ \"Microsoft.Network/virtualNetworkGateways\"\r\n| where properties.gatewayType == \"ExpressRoute\"\r\n| where properties.sku.tier !contains 'AZ'\r\n| project recommendationId = \"bbe668b7-eb5c-c746-8b82-70afdedf0cae\", name, id, tags, param1= strcat(\"sku-tier: \" , properties.sku.tier), param2=location\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "c0f23a92-d322-4d4d-97e9-a238b5e3bbb8",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Protect your Azure resources with a lock - Azure Resource Manager | Microsoft Learn",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/lock-resources?tabs=json"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Configuring an Azure Resource lock for ExpressRoute gateway prevents accidental deletion by enabling administrators to lock an Azure subscription, resource group, or resource, thereby protecting them from unintended user deletions and modifications, with the lock overriding all user permissions.\n",
+ "pgVerified": true,
+ "description": "Configure an Azure Resource lock for ExpressRoute gateway to prevent accidental deletion",
+ "potentialBenefits": "Prevents accidental deletions",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworkGateways",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1c34faa8-8b99-974c-adbf-71922eae943c",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "ExpressRoute monitoring, metrics, and alerts | ExpressRoute gateways",
+ "url": "https://learn.microsoft.com/azure/expressroute/expressroute-monitoring-metrics-alerts#expressroute-gateways"
+ },
+ {
+ "name": "Azure ExpressRoute Insights using Network Insights",
+ "url": "https://learn.microsoft.com/en-us/azure/expressroute/expressroute-network-insights"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Use Network Insights for monitoring ExpressRoute Gateway's health, including availability, performance, and scalability.\n",
+ "pgVerified": true,
+ "description": "Monitor gateway health for ExpressRoute gateways",
+ "potentialBenefits": "Enhanced monitoring and alerting",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworkGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "194c14ac-0d7a-5a48-ae32-75fa450ee564",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "About ExpressRoute virtual network gateways - VNet-to-VNet connectivity",
+ "url": "https://learn.microsoft.com/azure/expressroute/expressroute-about-virtual-network-gateways#vnet-to-vnet-connectivity"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "While multiple VNets can connect via the same ExpressRoute gateway, Microsoft recommends using alternatives like VNet peering, Azure Firewall, NVA, Azure Route Server, site-to-site VPN, virtual WAN, or SD-WAN for VNet-to-VNet communication to optimize network performance and management.\n",
+ "pgVerified": true,
+ "description": "Avoid using ExpressRoute circuits for VNet to VNet communication",
+ "potentialBenefits": "Enhanced VNet integration efficiency",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworkGateways",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "3e115044-a3aa-433e-be01-ce17d67e50da",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Configure customer-controlled maintenance for your virtual network gateway - ExpressRoute | Microsoft Learn",
+ "url": "https://learn.microsoft.com/en-us/azure/expressroute/customer-controlled-gateway-maintenance#azure-portal-steps"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "ExpressRoute gateways are updated for improved functionality, reliability, performance, and security. Customer-controlled maintenance configuration and scheduling minimize update impact and align with your maintenance windows.\n",
+ "pgVerified": true,
+ "description": "Configure customer-controlled ExpressRoute gateway maintenance",
+ "potentialBenefits": "Minimizes update impact",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworkGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Virtual Network Gateways without Maintenance Configurations\r\n\r\nresources\r\n| where type =~ \"Microsoft.Network/virtualNetworkGateways\"\r\n| extend resourceId = tolower(id)\r\n| join kind=leftouter (\r\n maintenanceresources\r\n | where type =~ \"Microsoft.Maintenance/configurationAssignments\"\r\n | project JsonData = parse_json(properties)\r\n | extend maintenanceConfigurationId = tolower(tostring(JsonData.maintenanceConfigurationId))\r\n | join kind=inner (\r\n resources\r\n | where type =~ \"Microsoft.Maintenance/maintenanceConfigurations\"\r\n | project maintenanceConfigurationId=tolower(id)\r\n ) on maintenanceConfigurationId\r\n | project maintenanceConfigurationId, resourceId=tolower(tostring(JsonData.resourceId))\r\n) on resourceId\r\n| where isempty(maintenanceConfigurationId)\r\n| project recommendationId = \"3e115044-a3aa-433e-be01-ce17d67e50da\", name, id, tags, param1= strcat(\"sku-tier: \" , properties.sku.tier), param2=location\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5b1933a6-90e4-f642-a01f-e58594e5aab2",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Zone redundant Virtual network gateway in availability zone",
+ "url": "https://learn.microsoft.com/azure/vpn-gateway/about-zone-redundant-vnet-gateways"
+ },
+ {
+ "name": "Gateway SKU",
+ "url": "https://learn.microsoft.com/azure/vpn-gateway/about-zone-redundant-vnet-gateways#gwskus"
+ },
+ {
+ "name": "SLA summary for Azure services",
+ "url": "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure VPN gateway offers variable SLAs based on deployment in one or two availability zones. Deploying zone-redundant virtual network gateways across availability zones ensures zone-resiliency, improving access to mission-critical, scalable services on Azure.\n",
+ "pgVerified": true,
+ "description": "Choose a Zone-redundant VPN gateway",
+ "potentialBenefits": "Enhanced reliability and scalability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworkGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// For all VNGs of type Vpn, show any that do not have AZ in the SKU tier\r\nresources\r\n| where type =~ \"Microsoft.Network/virtualNetworkGateways\"\r\n| where properties.gatewayType == \"Vpn\"\r\n| where properties.sku.tier !contains 'AZ'\r\n| project recommendationId = \"5b1933a6-90e4-f642-a01f-e58594e5aab2\", name, id, tags, param1= strcat(\"sku-tier: \" , properties.sku.tier), param2=location\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "281a2713-c0e0-3c48-b596-19f590c46671",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Active-active VPN gateway",
+ "url": "https://learn.microsoft.com/azure/vpn-gateway/active-active-portal#gateway"
+ },
+ {
+ "name": "Gateway SKU",
+ "url": "https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-about-vpn-gateway-settings#gwsku"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "The active-active mode is available for all SKUs except Basic, allowing for two Gateway IP configurations and two public IP addresses, enhancing redundancy and traffic handling.\n",
+ "pgVerified": true,
+ "description": "Plan for Active-Active mode with VPN Gateways",
+ "potentialBenefits": "Enhanced reliability and network capacity",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworkGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Identifies non-active-active VPN type virtual network gateways\r\nresources\r\n| where type =~ 'Microsoft.Network/virtualNetworkGateways'\r\n| where properties.gatewayType =~ \"vpn\"\r\n| extend gatewayType = properties.gatewayType, vpnType = properties.vpnType, connections = properties.connections, activeactive=properties.activeActive\r\n| where activeactive == false\r\n| project recommendationId = \"281a2713-c0e0-3c48-b596-19f590c46671\", name, id, tags\r\n\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "af11fc4c-c06c-4f4c-b98d-6eee6d5c4c70",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Dual-redundancy active-active VPN gateways for both Azure and on-premises networks",
+ "url": "https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-highlyavailable#dual-redundancy-active-active-vpn-gateways-for-both-azure-and-on-premises-networks"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Deploying active-active VPN concentrators and Azure VPN Gateways maximizes resilience and availability using a fully-meshed topology with four IPSec tunnels.\n",
+ "pgVerified": true,
+ "description": "Deploy active-active VPN concentrators on your premises for maximum resiliency with VPN gateways",
+ "potentialBenefits": "Maximizes resilience and availability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworkGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "9eab120e-f6d3-ee49-ba0d-766562ce7df1",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "VPN gateway data reference",
+ "url": "https://learn.microsoft.com/azure/vpn-gateway/monitor-vpn-gateway-reference"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Set up monitoring and alerts for Virtual Network Gateway health to utilize a variety of metrics for ensuring operational efficiency and prompt response to any disruptions.\n",
+ "pgVerified": true,
+ "description": "Monitor VPN gateway connections and health",
+ "potentialBenefits": "Improved uptime and issue awareness",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworkGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "9186dae0-7ddc-8f4b-bea5-55538cea4893",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Getting started with Azure Metrics Explorer",
+ "url": "https://learn.microsoft.com/azure/azure-monitor/essentials/metrics-getting-started"
+ },
+ {
+ "name": "Monitor VPN gateway",
+ "url": "https://learn.microsoft.com/azure/vpn-gateway/monitor-vpn-gateway-reference#metrics"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "VPN gateway leverages service health to inform users about both planned and unplanned maintenance, ensuring they are notified about modifications to their VPN connectivity.\n",
+ "pgVerified": true,
+ "description": "Enable VPN gateway service health",
+ "potentialBenefits": "Improves VPN maintenance alerts",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworkGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "4bae5a28-5cf4-40d9-bcf1-623d28f6d917",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "About zone-redundant virtual network gateway in Azure availability zones",
+ "url": "https://learn.microsoft.com/azure/vpn-gateway/about-zone-redundant-vnet-gateways"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "For zone-redundant VPN gateways, always use zone-redundant Standard SKU public IPs to avoid deploying all instances in one zone. This ensures the gateway's reliability, applying to both active-passive (single IP) and active-active (dual IP) setups.\n",
+ "pgVerified": true,
+ "description": "Deploy zone-redundant VPN gateways with zone-redundant Public IP(s)",
+ "potentialBenefits": "Enhanced reliability and disaster recovery",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworkGateways",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of zone-redundant Azure VPN gateways associated with non-zone-redundant Public IPs\r\nresources\r\n| where type =~ \"Microsoft.Network/virtualNetworkGateways\"\r\n| where properties.gatewayType == \"Vpn\"\r\n| where properties.sku.tier contains 'AZ'\r\n| mv-expand ipconfig = properties.ipConfigurations\r\n| extend pipId = tostring(ipconfig.properties.publicIPAddress.id)\r\n| join kind=inner (\r\n resources\r\n | where type == \"microsoft.network/publicipaddresses\"\r\n | where isnull(zones) or array_length(zones) < 3 )\r\n on $left.pipId == $right.id\r\n| project recommendationId = \"4bae5a28-5cf4-40d9-bcf1-623d28f6d917\", name, id, tags, param1 = strcat(\"PublicIpAddressName: \", name1), param2 = strcat (\"PublicIpAddressId: \",id1), param3 = strcat (\"PublicIpAddressTags: \",tags1)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "f0bf9ae6-25a5-974d-87d5-025abec73539",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Virtual Network - Concepts and best practices | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/virtual-network/concepts-and-best-practices"
+ },
+ {
+ "name": "GatewaySUbnet",
+ "url": "https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-about-vpn-gateway-settings#gwsub"
+ },
+ {
+ "name": "Can I associate a network security group (NSG) to the RouteServerSubnet?",
+ "url": "https://learn.microsoft.com/en-us/azure/route-server/route-server-faq#can-i-associate-a-network-security-group-nsg-to-the-routeserversubnet"
+ },
+ {
+ "name": "Are Network Security Groups (NSGs) supported on the AzureFirewallSubnet?",
+ "url": "https://learn.microsoft.com/en-us/azure/firewall/firewall-faq#are-network-security-groups--nsgs--supported-on-the-azurefirewallsubnet"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Network security groups and application security groups allow filtering of inbound and outbound traffic by IP, port, and protocol, adding a security layer at the Subnet level.\n",
+ "pgVerified": true,
+ "description": "All Subnets should have a Network Security Group associated",
+ "potentialBenefits": "Enhanced subnet security and traffic control",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworks",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find Subnets without NSG associated\r\nresources\r\n| where type =~ 'Microsoft.Network/virtualnetworks'\r\n| mv-expand subnets = properties.subnets\r\n| extend sn = string_size(subnets.properties.networkSecurityGroup)\r\n| where sn == 0 and subnets.name !in (\"GatewaySubnet\", \"AzureFirewallSubnet\", \"AzureFirewallManagementSubnet\", \"RouteServerSubnet\")\r\n| project recommendationId = \"f0bf9ae6-25a5-974d-87d5-025abec73539\", name, id, tags, param1 = strcat(\"SubnetName: \", subnets.name), param2 = \"NSG: False\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "69ea1185-19b7-de40-9da1-9e8493547a5c",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Reliability and Azure Virtual Network - Microsoft Azure Well-Architected Framework | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/architecture/framework/services/networking/azure-virtual-network/reliability"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Azure DDoS Protection offers enhanced mitigation features against DDoS attacks and is auto-tuned to protect specific resources in a virtual network, combined with application design best practices.\n",
+ "pgVerified": true,
+ "description": "Shield public endpoints in Azure VNets with Azure DDoS Standard Protection Plans",
+ "potentialBenefits": "Enhanced DDoS attack mitigation",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworks",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find virtual networks without DDoS Protection\r\nresources\r\n| where type =~ 'Microsoft.Network/virtualNetworks'\r\n| where isnull(properties.enableDdosProtection) or properties.enableDdosProtection contains \"false\"\r\n| project recommendationId = \"69ea1185-19b7-de40-9da1-9e8493547a5c\", name, id, tags, param1 = strcat(\"EnableDdosProtection: \", properties.enableDdosProtection)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "24ae3773-cc2c-3649-88de-c9788e25b463",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Virtual Network FAQ | Microsoft Learn",
+ "url": "https://learn.microsoft.com/azure/virtual-network/virtual-networks-faq"
+ },
+ {
+ "name": "Reliability and Network connectivity - Microsoft Azure Well-Architected Framework | Microsoft LearnNetworking Reliability",
+ "url": "https://learn.microsoft.com/azure/architecture/framework/services/networking/network-connectivity/reliability"
+ },
+ {
+ "name": "Azure Private Link availability",
+ "url": "https://learn.microsoft.com/en-us/azure/private-link/availability"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Use VNet service endpoints only if Private Link isn't available and no data movement concerns. This feature restricts Azure service access to specified VNet and subnet, enhancing network security and isolating service traffic.\n",
+ "pgVerified": true,
+ "description": "When available, use Private Endpoints instead of Service Endpoints for PaaS Services",
+ "potentialBenefits": "Enhanced security and data isolation",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Network/virtualNetworks",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find Subnets with Service Endpoint enabled for services that offer Private Link\r\nresources\r\n| where type =~ 'Microsoft.Network/virtualnetworks'\r\n| mv-expand subnets = properties.subnets\r\n| extend se = array_length(subnets.properties.serviceEndpoints)\r\n| where se >= 1\r\n| project name, id, tags, subnets, serviceEndpoints=todynamic(subnets.properties.serviceEndpoints)\r\n| mv-expand serviceEndpoints\r\n| project name, id, tags, subnetName=subnets.name, serviceName=tostring(serviceEndpoints.service)\r\n| where serviceName in (parse_json('[\"Microsoft.CognitiveServices\",\"Microsoft.AzureCosmosDB\",\"Microsoft.DBforMariaDB\",\"Microsoft.DBforMySQL\",\"Microsoft.DBforPostgreSQL\",\"Microsoft.EventHub\",\"Microsoft.KeyVault\",\"Microsoft.ServiceBus\",\"Microsoft.Sql\", \"Microsoft.Storage\",\"Microsoft.StorageSync\",\"Microsoft.Synapse\",\"Microsoft.Web\"]'))\r\n| project recommendationId = \"24ae3773-cc2c-3649-88de-c9788e25b463\", name, id, tags, param1 = strcat(\"subnet=\", subnetName), param2=strcat(\"serviceName=\",serviceName), param3=\"ServiceEndpoints=true\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1ceea4b5-1d8b-4be0-9bbe-9594557be51a",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure ExpressRoute Traffic Collector",
+ "url": "https://learn.microsoft.com/en-us/azure/expressroute/traffic-collector"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "ExpressRoute Traffic Collector samples network flows over ExpressRoute Direct circuits, sending flow logs to a Log Analytics workspace for analysis or export to visualization tools/SIEM.\n",
+ "pgVerified": true,
+ "description": "Ensure ExpressRoute Traffic Collector is enabled and configured for ExpressRoute Direct circuits",
+ "potentialBenefits": "Enhanced network flow analysis and DR readiness",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.NetworkFunction/azureTrafficCollectors",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b36fd2ac-dd83-664a-ab48-ff7b8d3b189d",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Log Analytics workspace data export in Azure Monitor",
+ "url": "https://learn.microsoft.com/azure/azure-monitor/logs/logs-data-export"
+ },
+ {
+ "name": "Azure Monitor configuration recommendations",
+ "url": "https://learn.microsoft.com/azure/azure-monitor/best-practices-logs#configuration-recommendations"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Data export in a Log Analytics workspace to an Azure Storage account enhances data protection against regional failures by using geo-redundant (GRS) or geo-zone-redundant storage (GZRS), mainly for compliance and integration with other Azure services and tools.\n",
+ "pgVerified": true,
+ "description": "Enable Log Analytics data export to GRS or GZRS",
+ "potentialBenefits": "Enhances compliance and regional fault tolerance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.OperationalInsights/workspaces",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "4b77191c-cc3c-8c4e-844b-0f56d0927890",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Monitor Log Analytics workspace health",
+ "url": "https://learn.microsoft.com/azure/azure-monitor/logs/log-analytics-workspace-health"
+ },
+ {
+ "name": "Azure Monitor configuration recommendations",
+ "url": "https://learn.microsoft.com/azure/azure-monitor/best-practices-logs#configuration-recommendations"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "A health status alert will proactively notify you if a workspace becomes unavailable because of a datacenter or regional failure.\n",
+ "pgVerified": true,
+ "description": "Create a health status alert rule for your Log Analytics workspace",
+ "potentialBenefits": "Early alert for workspace failure",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.OperationalInsights/workspaces",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e93bb813-b356-48f3-9bdf-a06a0a6ba039",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Setup network mapping for site recovery",
+ "url": "https://learn.microsoft.com/en-us/azure/site-recovery/azure-to-azure-network-mapping#set-up-ip-addressing-for-target-vms"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Ensure VM failover settings' static IP addresses are available in the failover subnet to maintain consistent IP assignment during failover, with the target VM receiving the same static IP if it's available or the next available IP otherwise. IP adjustments can be made in VM Network settings.\n",
+ "pgVerified": true,
+ "description": "Ensure static IP addresses in Site Recovery VM failover settings are available in failover subnet",
+ "potentialBenefits": "Smooth failover IP management",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.RecoveryServices/vaults",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "17e877f7-3a89-4205-8a24-0670de54ddcd",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Run a test failover",
+ "url": "https://learn.microsoft.com/en-us/azure/site-recovery/azure-to-azure-tutorial-dr-drill#run-a-test-failover"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Perform a test failover to validate your BCDR strategy and ensure that your applications are functioning correctly in the target region without impacting your production environment. Test your Disaster Recovery plan periodically without any data loss or downtime, using test failovers.\n",
+ "pgVerified": true,
+ "description": "Validate VM functionality with a Site Recovery test failover to check performance at target",
+ "potentialBenefits": "Ensures BCDR plan accuracy and VM performance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.RecoveryServices/vaults",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all VMs where replication has been enabled but Test Failover was never performed\r\nrecoveryservicesresources\r\n| where type == \"microsoft.recoveryservices/vaults/replicationfabrics/replicationprotectioncontainers/replicationprotecteditems\"\r\n| where properties.providerSpecificDetails.dataSourceInfo.datasourceType == 'AzureVm' and isnull(properties.lastSuccessfulTestFailoverTime)\r\n| project recommendationId=\"17e877f7-3a89-4205-8a24-0670de54ddcd\" , name = properties.providerSpecificDetails.recoveryAzureVMName, id=properties.providerSpecificDetails.dataSourceInfo.resourceId\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "2912472d-0198-4bdc-aa90-37f145790edc",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Move to Azure monitor Alerts",
+ "url": "https://learn.microsoft.com/azure/backup/move-to-azure-monitor-alerts"
+ },
+ {
+ "name": "Classic alerts retirement announcement",
+ "url": "https://azure.microsoft.com/updates/transition-to-builtin-azure-monitor-alerts-for-recovery-services-vaults-in-azure-backup-by-31-march-2026/"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Classic alerts for Recovery Services vaults in Azure Backup will be retired on 31 March 2026.\n",
+ "pgVerified": true,
+ "description": "Migrate from classic alerts to built-in Azure Monitor alerts for Azure Recovery Services Vaults",
+ "potentialBenefits": "Enhanced, scalable, and consistent alerting.",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.RecoveryServices/vaults",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This Resource Graph query will return all Recovery services vault with Classic alerts enabled.\r\nresources\r\n| where type in~ ('microsoft.recoveryservices/vaults')\r\n| extend monitoringSettings = parse_json(properties).monitoringSettings\r\n| extend isUsingClassicAlerts = case(isnull(monitoringSettings),'Enabled',monitoringSettings.classicAlertSettings.alertsForCriticalOperations)\r\n| extend isUsingJobsAlerts = case(isnull(monitoringSettings), 'Enabled', monitoringSettings.azureMonitorAlertSettings.alertsForAllJobFailures)\r\n| where isUsingClassicAlerts == 'Enabled'\r\n| project recommendationId = \"2912472d-0198-4bdc-aa90-37f145790edc\", name, id, tags, param1=strcat(\"isUsingClassicAlerts: \", isUsingClassicAlerts), param2=strcat(\"isUsingJobsAlerts: \", isUsingJobsAlerts)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1549b91f-2ea0-4d4f-ba2a-4596becbe3de",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Set Cross Region Restore",
+ "url": "https://learn.microsoft.com/azure/backup/backup-create-recovery-services-vault#set-cross-region-restore"
+ },
+ {
+ "name": "Azure Backup Best Practices",
+ "url": "https://learn.microsoft.com/azure/backup/guidance-best-practices"
+ },
+ {
+ "name": "Minimum Role Requirements for Cross Region Restore",
+ "url": "https://learn.microsoft.com/azure/backup/backup-rbac-rs-vault#minimum-role-requirements-for-azure-vm-backup"
+ },
+ {
+ "name": "Recovery Services Vault",
+ "url": "https://learn.microsoft.com/azure/backup/backup-azure-arm-vms-prepare"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Cross Region Restore enables the restoration of Azure VMs in a secondary, Azure paired region, facilitating drills for audit or compliance and allowing recovery of VMs or disks in the event of a primary region disaster. It is an opt-in feature available exclusively for GRS vaults.\n",
+ "pgVerified": true,
+ "description": "Opt-in to Cross Region Restore for all Geo-Redundant Storage (GRS) Azure Recovery Services vaults",
+ "potentialBenefits": "Enhances disaster recovery capabilities",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.RecoveryServices/vaults",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Displays all recovery services vaults that do not have cross region restore enabled\r\nresources\r\n| where type =~ \"Microsoft.RecoveryServices/vaults\" and\r\n properties.redundancySettings.standardTierStorageRedundancy =~ \"GeoRedundant\" and\r\n properties.redundancySettings.crossRegionRestore !~ \"Enabled\"\r\n| extend\r\n param1 = strcat(\"CrossRegionRestore: \", properties.redundancySettings.crossRegionRestore),\r\n param2 = strcat(\"StorageReplicationType: \", properties.redundancySettings.standardTierStorageRedundancy)\r\n| project recommendationId = \"1549b91f-2ea0-4d4f-ba2a-4596becbe3de\", name, id, tags, param1, param2\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "9e39919b-78af-4a0b-b70f-c548dae97c25",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Soft Delete for Azure Backup",
+ "url": "https://learn.microsoft.com/azure/backup/backup-azure-security-feature-cloud?tabs=azure-portal"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "With soft delete, if backup data is deleted, the backup data is retained for 14 additional days, allowing the recovery of that backup item with no data loss with no cost to you. Soft delete is enabled by default. Disabling this feature isn't recommended.\n",
+ "pgVerified": false,
+ "description": "Enable Soft Delete for Recovery Services Vaults in Azure Backup",
+ "potentialBenefits": "Enhances disaster recovery capabilities",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.RecoveryServices/vaults",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Azure Recovery Services vaults that do not have soft delete enabled\r\nresources\r\n| where type == \"microsoft.recoveryservices/vaults\"\r\n| mv-expand issoftDelete=properties.securitySettings.softDeleteSettings.softDeleteState\r\n| where issoftDelete == 'Disabled'\r\n| project recommendationid = \"9e39919b-78af-4a0b-b70f-c548dae97c25\", name, id, tags, param1=strcat(\"Soft Delete: \",issoftDelete)\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "98bd7098-49d6-491b-86f1-b143d6b1a0ff",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Resource Manager Overview",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/overview#resource-group-location-alignment"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Ensure resource locations align with their resource group to manage resources during regional outages. ARM stores resource data, which if in an unavailable region, could halt updates, rendering resources read-only.\n",
+ "pgVerified": true,
+ "description": "Ensure Resource Group and its Resources are located in the same Region",
+ "potentialBenefits": "Improves outage management",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Resources/resourceGroups",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of Azure Resource Groups that have resources deployed in a region different than the Resource Group region\r\nresources\r\n| project id, name, tags, resourceGroup, location\r\n| where location != \"global\" // exclude global resources\r\n| where resourceGroup != \"networkwatcherrg\" // exclude networkwatcherrg\r\n| where split(id, \"/\", 3)[0] =~ \"resourceGroups\" // resource is in a resource group\r\n| extend resourceGroupId = strcat_array(array_slice(split(id, \"/\"),0,4), \"/\") // create resource group resource id\r\n| join (resourcecontainers | project containerid=id, containerlocation=location ) on $left.resourceGroupId == $right.['containerid'] // join to resourcecontainers table\r\n| where location != containerlocation\r\n| project recommendationId=\"98bd7098-49d6-491b-86f1-b143d6b1a0ff\", name, id, tags\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "20057905-262c-49fe-a9be-49f423afb359",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Service Bus and reliability",
+ "url": "https://learn.microsoft.com/en-us/azure/well-architected/services/messaging/service-bus/reliability"
+ },
+ {
+ "name": "Azure Service Bus Geo-disaster recovery",
+ "url": "https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-geo-dr#availability-zones"
+ },
+ {
+ "name": "Insulate Azure Service Bus applications against outages and disasters",
+ "url": "https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-outages-disasters"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Use Service Bus with zone redundancy for high availability. The Premium SKU supports availability zones, ensuring isolation within the same region. It manages 3 copies of the messaging store, kept in sync.\n",
+ "pgVerified": false,
+ "description": "Enable Availability Zones for Service Bus namespaces",
+ "potentialBenefits": "Enhances fault tolerance and uptime",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ServiceBus/namespaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Returns Service Bus namespaces that do not have any availability zones enabled\r\nresources\r\n| where type =~ 'Microsoft.ServiceBus/namespaces'\r\n| where properties.zoneRedundant == 'false'\r\n| project recommendationId = \"20057905-262c-49fe-a9be-49f423afb359\", name, id, tags, param1=strcat(\"zoneRedundant: \", properties.zoneRedundant), param2=strcat(\"SKU: \", sku.name), param3=iff(tolower(sku.name) == 'premium', 'Move Service Bus namespace to a region that supports Availability Zones', 'Migrate to Premium SKU in a region that supports Availability Zones')\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "d810e3a8-600f-4be1-895b-1a93e61d37fd",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Service Bus auto-scaling",
+ "url": "https://learn.microsoft.com/azure/service-bus-messaging/automate-update-messaging-units"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Use Service Bus with auto-scale for high availability. The Premium SKU supports auto-scale, ensuring that the resources are automatically scaled based on the load.\n",
+ "pgVerified": false,
+ "description": "Enable auto-scale for production workloads on Service Bus namespaces",
+ "potentialBenefits": "Ensures high availability and performance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.ServiceBus/namespaces",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "6a8b3db9-5773-413a-a127-4f7032f34bbd",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Availability zones support in Azure SignalR Service",
+ "url": "https://learn.microsoft.com/azure/azure-signalr/availability-zones"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Use SignalR with zone redundancy for production to improve uptime. This feature, available in the Premium tier, is activated upon creating or upgrading to Premium. Standard can upgrade to Premium without downtime.\n",
+ "pgVerified": false,
+ "description": "Enable zone redundancy for SignalR",
+ "potentialBenefits": "Enhances reliability and uptime",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.SignalRService/SignalR",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find SignalR instances that are not configured with the Premium tier\r\nresources\r\n| where type == \"microsoft.signalrservice/signalr\"\r\n| where sku.tier != \"Premium\"\r\n| project recommendationId = \"6a8b3db9-5773-413a-a127-4f7032f34bbd\", name, id, tags, param1 = \"AvailabilityZones: Single Zone\"\r\n| order by id asc\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "74c2491d-048b-0041-a140-935960220e20",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Active Geo Replication",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-sql/database/active-geo-replication-overview"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Active Geo Replication ensures business continuity by utilizing readable secondary database replicas. In case of primary database failure, manually failover to secondary database. Secondaries, up to four, can be in same/different regions, used for read-only access.\n",
+ "pgVerified": true,
+ "description": "Use Active Geo Replication to Create a Readable Secondary in Another Region",
+ "potentialBenefits": "Enhanced disaster recovery and read scalability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Sql/servers",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of SQL databases that are not part of Geo Replication.\r\nresources\r\n| where type == \"microsoft.sql/servers/databases\"\r\n| summarize secondaryTypeCount = countif(isnotempty(properties.secondaryType)) by name\r\n| where secondaryTypeCount == 0\r\n| join kind=inner (\r\n Resources\r\n | where type == \"microsoft.sql/servers/databases\"\r\n) on name\r\n| extend param1 = \"Not part of Geo Replication\"\r\n| project recommendationId = \"74c2491d-048b-0041-a140-935960220e20\", name, id, tags, param1\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "943c168a-2ec2-a94c-8015-85732a1b4859",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "AutoFailover Groups",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-sql/database/auto-failover-group-overview?tabs=azure-powershell"
+ },
+ {
+ "name": "DR Design",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-sql/database/designing-cloud-solutions-for-disaster-recovery"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Failover Groups facilitate disaster recovery by configuring databases on one logical server to replicate to another region's logical server. This streamlines geo-replicated database management, offering a single endpoint for connection routing to replicated databases if the primary server fails.\n",
+ "pgVerified": true,
+ "description": "Auto Failover Groups can encompass one or multiple databases, usually used by the same app.",
+ "potentialBenefits": "Improves load balancing and disaster recovery",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Sql/servers",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of SQL databases that are not configured to use a failover-group.\r\nresources\r\n| where type =~'microsoft.sql/servers/databases'\r\n| where isnull(properties['failoverGroupId'])\r\n| project recommendationId = \"943c168a-2ec2-a94c-8015-85732a1b4859\", name, id, tags, param1= strcat(\"databaseId=\", properties['databaseId'])\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "c0085c32-84c0-c247-bfa9-e70977cbf108",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Zone Redundant Databases",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-sql/database/high-availability-sla"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "By default, Azure SQL Database premium tier provisions multiple copies within the same region. For geo redundancy, databases can be set as Zone Redundant, distributing copies across Azure Availability Zones to maintain availability during regional outages.\n",
+ "pgVerified": true,
+ "description": "Use a Zone-Redundant Database",
+ "potentialBenefits": "Enhanced reliability, no extra cost",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Sql/servers",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Finds non-zone redundant SQL databases and lists them\r\nResources\r\n| where type =~ 'microsoft.sql/servers/databases'\r\n| where tolower(tostring(properties.zoneRedundant))=~'false'\r\n|project recommendationId = \"c0085c32-84c0-c247-bfa9-e70977cbf108\", name, id, tags\r\n\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "cbb17a29-64fb-c943-95d0-8df814a37c40",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "How to Implement Retry Logic",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-sql/database/troubleshoot-common-connectivity-issues"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "During transient failures, the application should handle connection retries effectively with Azure SQL Database. No Database layer configuration is needed; instead, the application must be set up for graceful retrying.\n",
+ "pgVerified": true,
+ "description": "Implement Retry Logic",
+ "potentialBenefits": "Enhanced connectivity stability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Sql/servers",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "7e7daec9-6a81-3546-a4cc-9aef72fec1f7",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Monitor",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-monitor/insights/azure-sql#analyze-data-and-create-alerts"
+ },
+ {
+ "name": "Azure SQL Database Monitoring",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-sql/database/monitoring-sql-database-azure-monitor"
+ },
+ {
+ "name": "Monitoring SQL Database Reference",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-sql/database/monitoring-sql-database-azure-monitor-reference"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Monitoring and alerting are an important part of database operations. When working with Azure SQL Database, make use of Azure Monitor and SQL Insights to ensure that you capture relevant database metrics.\n",
+ "pgVerified": true,
+ "description": "Monitor your Azure SQL Database in Near Real-Time to Detect Reliability Incidents",
+ "potentialBenefits": "Quick incident detection and response",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Sql/servers",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of SQL databases that are not configured for monitoring.\r\nresources\r\n| where type == \"microsoft.insights/metricalerts\"\r\n| mv-expand properties.scopes\r\n| mv-expand properties.criteria.allOf\r\n| project databaseid = properties_scopes, monitoredMetric = properties_criteria_allOf.metricName\r\n| where databaseid contains 'databases'\r\n| summarize monitoredMetrics=make_list(monitoredMetric) by tostring(databaseid)\r\n| join kind=fullouter (\r\n resources\r\n | where type =~ 'microsoft.sql/servers/databases'\r\n | project databaseid = tolower(id), name, tags\r\n) on databaseid\r\n|where isnull(monitoredMetrics)\r\n|project recommendationId = \"7e7daec9-6a81-3546-a4cc-9aef72fec1f7\", name, id=databaseid1, tags, param1=strcat(\"MonitoringMetrics=false\" )\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "d6ef87aa-574e-584e-a955-3e6bb8b5425b",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Key Vault",
+ "url": "https://learn.microsoft.com/en-us/azure/key-vault/general/overview"
+ },
+ {
+ "name": "Getting Started with Always Encrypted",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-sql/database/always-encrypted-landing?view=azuresql"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "It is highly recommended to use Azure Key Vault (AKV) to store encryption keys related to Always Encrypted configurations, however it is not required. If you are not using AKV, then ensure that your keys are properly backed up and stored in a secure manner.\n",
+ "pgVerified": true,
+ "description": "Back Up Your Keys",
+ "potentialBenefits": "Enhanced security and data recovery",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Sql/servers",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "e6c7e1cc-2f47-264d-aa50-1da421314472",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure Storage redundancy",
+ "url": "https://learn.microsoft.com/azure/storage/common/storage-redundancy"
+ },
+ {
+ "name": "Change the redundancy configuration for a storage account",
+ "url": "https://learn.microsoft.com/azure/storage/common/redundancy-migration"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Redundancy ensures storage accounts meet availability and durability targets amidst failures, weighing lower costs against higher availability. Locally redundant storage offers the least durability at the lowest cost.\n",
+ "pgVerified": true,
+ "description": "Ensure that storage accounts are zone or region redundant",
+ "potentialBenefits": "High availability and durability for storage",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Storage/storageAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This query will return all storage accounts that are not using Zone or Region replication\r\nResources\r\n| where type =~ \"Microsoft.Storage/storageAccounts\"\r\n| where sku.name in~ (\"Standard_LRS\", \"Premium_LRS\")\r\n| project recommendationId = \"e6c7e1cc-2f47-264d-aa50-1da421314472\", name, id, tags, param1 = strcat(\"sku: \", sku.name)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "63ad027e-611c-294b-acc5-8e3234db9a40",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Azure classic storage accounts retirement announcement",
+ "url": "https://azure.microsoft.com/updates/classic-azure-storage-accounts-will-be-retired-on-31-august-2024/"
+ },
+ {
+ "name": "Migrate your classic storage accounts to Azure Resource Manager",
+ "url": "https://learn.microsoft.com/azure/storage/common/classic-account-migration-overview"
+ }
+ ],
+ "recommendationControl": "Service Upgrade and Retirement",
+ "longDescription": "Classic storage accounts will be fully retired on August 31, 2024. If you have classic storage accounts, start planning your migration now.\n",
+ "pgVerified": true,
+ "description": "Do not use classic storage accounts",
+ "potentialBenefits": "Avoids service retirement issues",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Storage/storageAccounts",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Azure classic Storage Account\r\nresources\r\n| where type =~ 'microsoft.classicstorage/storageaccounts'\r\n| project recommendationId = '63ad027e-611c-294b-acc5-8e3234db9a40', name, id, tags, param1=type\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5587ef77-7a05-a74d-9c6e-449547a12f27",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Types of storage accounts",
+ "url": "https://learn.microsoft.com/azure/storage/common/storage-account-overview#types-of-storage-accounts"
+ },
+ {
+ "name": "Scalability and performance targets for standard storage accounts",
+ "url": "https://learn.microsoft.com/azure/storage/common/scalability-targets-standard-account"
+ },
+ {
+ "name": "Performance and scalability checklist for Blob storage",
+ "url": "https://learn.microsoft.com/azure/storage/blobs/storage-performance-checklist"
+ },
+ {
+ "name": "Scalability and performance targets for Blob storage",
+ "url": "https://learn.microsoft.com/azure/storage/blobs/scalability-targets"
+ },
+ {
+ "name": "Premium block blob storage accounts",
+ "url": "https://learn.microsoft.com/azure/storage/blobs/storage-blob-block-blob-premium"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Consider using the appropriate storage performance tier for workload scenarios. Each workload scenario requires appropriate performance tiers, and selecting the appropriate tiers based on storage usage is crucial.\n",
+ "pgVerified": true,
+ "description": "Ensure Performance tier is set as per workload",
+ "potentialBenefits": "Optimized cost and performance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Storage/storageAccounts",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "03263c57-c869-3841-9e0a-3dbb9ef3e28d",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Soft delete detail docs",
+ "url": "https://learn.microsoft.com//azure/storage/blobs/soft-delete-blob-enable?tabs=azure-portal "
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "The soft delete option enables data recovery if mistakenly deleted, while the Lock feature prevents the accidental deletion of the storage account itself, ensuring additional security and data integrity measures.\n",
+ "pgVerified": true,
+ "description": "Enable soft delete for recovery of data",
+ "potentialBenefits": "Prevents accidental data/account loss",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Storage/storageAccounts",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "8ebda7c0-e0e1-ed45-af59-2d7ea9a1c05d",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Blob versioning",
+ "url": "https://learn.microsoft.com/azure/storage/blobs/versioning-overview "
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Consider enabling versioning for Azure Storage Accounts to recover from accidental modifications or deletions and manage blob operation latency. Microsoft advises maintaining fewer than 1000 versions per blob to optimize performance. Lifecycle management can help delete old versions automatically.\n",
+ "pgVerified": true,
+ "description": "Enable versioning for accidental modification and keep the number of versions below 1000",
+ "potentialBenefits": "Recover data, manage latency",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Storage/storageAccounts",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "1b965cb9-7629-214e-b682-6bf6e450a100",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Point-in-time restore for block blobs",
+ "url": "https://learn.microsoft.com/azure/storage/blobs/point-in-time-restore-overview"
+ },
+ {
+ "name": "Perform a point-in-time restore on block blob data",
+ "url": "https://learn.microsoft.com/azure/storage/blobs/point-in-time-restore-manage?tabs=portal"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "Consider enabling point-in-time restore for standard general purpose v2 accounts with flat namespace to protect against accidental deletion or corruption by restoring block blob data to an earlier state.\n",
+ "pgVerified": true,
+ "description": "Enable point-in-time restore for GPv2 accounts to safeguard against data loss",
+ "potentialBenefits": "Protects data from loss/corruption",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Storage/storageAccounts",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "96cb8331-6b06-8242-8ce8-4e2f665dc679",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Monitor Azure Blob Storage",
+ "url": "https://learn.microsoft.com/azure/storage/blobs/monitor-blob-storage"
+ },
+ {
+ "name": "Best practices for monitoring Azure Blob Storage",
+ "url": "https://learn.microsoft.com/azure/storage/blobs/blob-storage-monitoring-scenarios"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "For critical applications and business processes relying on Azure, monitoring and alerts are crucial. Resource logs are only stored after creating a diagnostic setting to route logs to specified locations, requiring selection of log categories to collect.\n",
+ "pgVerified": true,
+ "description": "Monitor all blob storage accounts",
+ "potentialBenefits": "Enhanced alerting and log analysis",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Storage/storageAccounts",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "2ad78dec-5a4d-4a30-8fd1-8584335ad781",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Legacy storage account types",
+ "url": "https://learn.microsoft.com/azure/storage/common/storage-account-overview#legacy-storage-account-types"
+ },
+ {
+ "name": "Upgrade to a general-purpose v2 storage account",
+ "url": "https://learn.microsoft.com/azure/storage/common/storage-account-upgrade"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "General-purpose v2 accounts are recommended for most storage scenarios offering the latest features or the lowest per-gigabyte pricing. Legacy accounts like Standard general-purpose v1 and Blob Storage aren't advised by Microsoft but may fit specific scenarios.\n",
+ "pgVerified": true,
+ "description": "Consider upgrading legacy storage accounts to v2 storage accounts",
+ "potentialBenefits": "Latest features, lowest cost",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Storage/storageAccounts",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Find all Azure Storage Accounts, that upgradeable to General purpose v2.\r\nResources\r\n| where type =~ \"Microsoft.Storage/storageAccounts\" and kind in~ (\"Storage\", \"BlobStorage\")\r\n| extend\r\n param1 = strcat(\"AccountKind: \", case(kind =~ \"Storage\", \"Storage (general purpose v1)\", kind =~ \"BlobStorage\", \"BlobStorage\", kind)),\r\n param2 = strcat(\"Performance: \", sku.tier),\r\n param3 = strcat(\"Replication: \", sku.name)\r\n| project recommendationId = \"2ad78dec-5a4d-4a30-8fd1-8584335ad781\", name, id, tags, param1, param2, param3\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "dc55be60-6f8c-461e-a9d5-a3c7686ed94e",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Learn More",
+ "url": "https://learn.microsoft.com/en-us/azure/architecture/example-scenario/wvd/windows-virtual-desktop#azure-virtual-desktop-limitations"
+ },
+ {
+ "name": "Private Link",
+ "url": "https://learn.microsoft.com/en-us/azure/well-architected/azure-virtual-desktop/networking#private-endpoints-private-link"
+ }
+ ],
+ "recommendationControl": "Security",
+ "longDescription": "Leverage Azure Private Link Service for secure access to Azure Storage and services via Private Endpoint in your VNet. Eliminate the need for public IPs, ensuring data privacy. Enjoy granular access control for enhanced security.\n",
+ "pgVerified": true,
+ "description": "Enable Azure Private Link service for storage accounts",
+ "potentialBenefits": "Secure, private access to storage with no public IPs",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Storage/storageAccounts",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// This resource graph query will return all storage accounts that does not have a Private Endpoint Connection or where a private endpoint exists but public access is enabled\r\nresources\r\n| where type =~ \"Microsoft.Storage/StorageAccounts\"\r\n| where isnull(properties.privateEndpointConnections) or properties.privateEndpointConnections[0].properties.provisioningState != (\"Succeeded\") or (isnull(properties.networkAcls) and properties.publicNetworkAccess == 'Enabled')\r\n| extend param1 = strcat('Private Endpoint: ', iif(isnotnull(properties.privateEndpointConnections),split(properties.privateEndpointConnections[0].properties.privateEndpoint.id,'/')[8],'No Private Endpoint'))\r\n| extend param2 = strcat('Access: ', iif(properties.publicNetworkAccess == 'Disabled', 'Public Access Disabled', iif(isnotnull(properties.networkAcls), 'NetworkACLs in place','Public Access Enabled')))\r\n| project recommendationID = \"dc55be60-6f8c-461e-a9d5-a3c7686ed94e\", name, id, tags, param1, param2\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "c041d596-6c97-4c5f-b4b3-9cd37628f2e2",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Citrix Limits",
+ "url": "https://docs.citrix.com/en-us/citrix-daas-azure/limits"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "A Citrix Managed Azure subscription supports VMs with VDA for app/desktop delivery, excluding other machines like Cloud Connectors. When close to the limit, signaled by a dashboard notification, and with sufficient licenses, request another subscription. Can't exceed the given limits for catalogs.\n",
+ "pgVerified": true,
+ "description": "Do not create more than 2000 Citrix VDA servers per subscription",
+ "potentialBenefits": "Avoids hitting limit, ensures reliability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Subscription/Subscriptions",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Count VM instances with a tag that contains \"Citrix VDA\" and create output if that count is >2000 for each subscription.\r\n// The Citrix published limit is 2500. This query runs an 80% check.\r\n\r\nresources\r\n| where type == 'microsoft.compute/virtualmachines'\r\n| where tags contains 'Citrix VDA'\r\n| summarize VMs=count() by subscriptionId\r\n| where VMs > 2000\r\n| join (resourcecontainers| where type =='microsoft.resources/subscriptions' | project subname=name, subscriptionId) on subscriptionId\r\n| project recommendationId='c041d596-6c97-4c5f-b4b3-9cd37628f2e2', name= subname, id = subscriptionId, param1='Too many instances.', param2= VMs\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "5ada5ffa-7149-4e49-9fbf-e67be7c2594c",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Management group recommendations",
+ "url": "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/landing-zone/design-area/resource-org-management-groups#management-group-recommendations"
+ },
+ {
+ "name": "Root management group for each directory",
+ "url": "https://learn.microsoft.com/en-us/azure/governance/management-groups/overview#root-management-group-for-each-directory"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "The root management group in Azure is designed for organizational hierarchy, allowing for all management groups and subscriptions to fold into it.\n",
+ "pgVerified": true,
+ "description": "Subscriptions should not be placed under the Tenant Root Management Group",
+ "potentialBenefits": "Enhanced security, compliance, and management",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Subscription/Subscriptions",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of Azure Subscriptions that are placed under the Tenant Root Management Group\r\nresourcecontainers\r\n| where type == 'microsoft.resources/subscriptions'\r\n| extend mgParentSize = array_length(properties.managementGroupAncestorsChain)\r\n| where mgParentSize == 1\r\n| project recommendationId=\"5ada5ffa-7149-4e49-9fbf-e67be7c2594c\", name, id, tags\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "19b6df57-f6b5-3e4f-843a-273daa087cb0",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Generation 1 vs generation 2 virtual machines",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/generation-2#features-and-capabilities"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "When building Image Templates, use sources for gen 2 VMs. Gen 2 offers more memory, supports >2TB disks, uses UEFI for faster boot/installation, has Intel SGX, and virtualized persistent memory (vPMEM), unlike gen 1's BIOS-based architecture.\n",
+ "pgVerified": true,
+ "description": "Use Generation 2 virtual machine source image",
+ "potentialBenefits": "More memory, supports >2TB disks, faster boot",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.VirtualMachineImages/imageTemplates",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "21fb841b-ba70-1f4e-a460-1f72fb41aa51",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Image Template resiliency",
+ "url": "https://learn.microsoft.com/en-us/azure/reliability/reliability-image-builder?toc=%2Fazure%2Fvirtual-machines%2Ftoc.json&bc=%2Fazure%2Fvirtual-machines%2Fbreadcrumb%2Ftoc.json#capacity-and-proactive-disaster-recovery-resiliency"
+ },
+ {
+ "name": "Azure Image Builder Supported Regions",
+ "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/image-builder-overview?tabs=azure-powershell#regions"
+ }
+ ],
+ "recommendationControl": "Disaster Recovery",
+ "longDescription": "The Azure Image Builder service, used for deploying Image Templates, lacks availability zones support. By replicating Image Templates to a secondary, preferably paired, region, quick recovery from a region failure is enabled, ensuring continuous virtual machine deployment from these templates.\n",
+ "pgVerified": true,
+ "description": "Replicate your Image Templates to a secondary region",
+ "potentialBenefits": "Enhances disaster recovery capability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.VirtualMachineImages/imageTemplates",
+ "recommendationImpact": "Low",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// List all Image Templates that are not replicated to another region\r\nresources\r\n| where type =~ \"microsoft.virtualmachineimages/imagetemplates\"\r\n| mv-expand distribution=properties.distribute\r\n| where array_length(parse_json(distribution).replicationRegions) == 1\r\n| project recommendationId = \"21fb841b-ba70-1f4e-a460-1f72fb41aa51\", name, id, param1=strcat(\"replicationRegions:\",parse_json(distribution).replicationRegions)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "88cb90c2-3b99-814b-9820-821a63f600dd",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Migrate App Service to availability zone support",
+ "url": "https://learn.microsoft.com/en-us/azure/reliability/migrate-app-service"
+ },
+ {
+ "name": "High availability enterprise deployment using App Service Environment",
+ "url": "https://learn.microsoft.com/en-us/azure/architecture/reference-architectures/enterprise-integration/ase-high-availability-deployment"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Azure's feature of deploying App Service plans across availability zones enhances resiliency and reliability by ensuring operation during datacenter failures, providing redundancy without needing different regions, thus minimizing downtime and maintaining uninterrupted services.\n",
+ "pgVerified": false,
+ "description": "Migrate App Service to availability Zone Support",
+ "potentialBenefits": "Enhances app resiliency and reliability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Web/serverFarms",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// The query filters the qualified App Service Plans that do not have Zone Redundancy enabled.\r\n// Its important to check regions that support availability zones for Azure App Services running on multi-tenant and App Service Environments https://learn.microsoft.com/en-us/azure/reliability/reliability-app-service?tabs=graph%2Ccli#:~:text=The%20following%20regions%20support%20Azure%20App%20Services%20running%20on%20multi%2Dtenant%20environments%3A\r\n\r\nresources\r\n| where type =~ 'microsoft.web/serverfarms'\r\n| extend zoneRedundant = tobool(properties.zoneRedundant)\r\n| extend sku_tier = tostring(sku.tier)\r\n| where (tolower(sku_tier) contains \"isolated\" or tolower(sku_tier) contains \"premium\") and zoneRedundant == false\r\n| project recommendationid=\"88cb90c2-3b99-814b-9820-821a63f600dd\", name, id, tags, param1=sku_tier, param2=\"Not Zone Redundant\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "b2113023-a553-2e41-9789-597e2fb54c31",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Resiliency checklist for specific Azure services",
+ "url": "https://learn.microsoft.com/en-us/azure/architecture/checklist/resiliency-per-service#app-service"
+ }
+ ],
+ "recommendationControl": "High Availability",
+ "longDescription": "Choose Standard/Premium Azure App Service Plan for robust apps with advanced scaling, high availability, better performance, and multiple slots, ensuring resilience and continuous operation.\n",
+ "pgVerified": false,
+ "description": "Use Standard or Premium tier",
+ "potentialBenefits": "Enhanced scaling and reliability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Web/serverFarms",
+ "recommendationImpact": "High",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of Azure App Service Plans that are not in the \"Standard\", \"Premium\", or \"IsolatedV2\" SKU tiers.\r\n\r\nresources\r\n| where type =~ 'microsoft.web/serverfarms'\r\n| extend sku_tier = tostring(sku.tier)\r\n| where tolower(sku_tier) !contains \"standard\" and\r\n tolower(sku_tier) !contains \"premium\" and\r\n tolower(sku_tier) !contains \"isolatedv2\"\r\n| project recommendationid=\"b2113023-a553-2e41-9789-597e2fb54c31\", name, id, tags, param1= strcat(\"SKU=\",sku_tier)\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "07243659-4643-d44c-a1c6-07ac21635072",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Resiliency checklist for specific Azure services",
+ "url": "https://learn.microsoft.com/en-us/azure/architecture/checklist/resiliency-per-service#app-service"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Avoid frequent scaling up/down of Azure App Service instances to prevent service disruptions. Choose the right tier and size for the workload and scale out for traffic changes, as scaling adjustments can trigger application restarts.\n",
+ "pgVerified": false,
+ "description": "Avoid scaling up or down",
+ "potentialBenefits": "Minimizes restarts, enhances stability",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Web/serverFarms",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of Azure App Service Plans and the number of changes that was made to the pricing tier, if the count is higher that 3 it means you need to avoid scaling up and down that often\r\n\r\nresourcechanges\r\n| extend changeTime = todatetime(properties.changeAttributes.timestamp), targetResourceId = tostring(properties.targetResourceId),\r\nchangeType = tostring(properties.changeType), correlationId = properties.changeAttributes.correlationId,\r\nchangedProperties = properties.changes, changeCount = properties.changeAttributes.changesCount\r\n| where changeTime > ago(14d)\r\n| join kind=inner (resources | project resources_Name = name, resources_Type = type, resources_Subscription= subscriptionId, resources_ResourceGroup= resourceGroup, id) on $left.targetResourceId == $right.id\r\n| where resources_Type contains \"microsoft.web/serverfarms\"\r\n| where changedProperties['sku.name'].propertyChangeType == 'Update' or changedProperties['sku.tier'].propertyChangeType == 'Update'\r\n| summarize count() by targetResourceId, resources_Name ,tostring(changedProperties['sku.name'].previousValue), tostring(changedProperties['sku.tier'].newValue)\r\n| project recommendationid=\"07243659-4643-d44c-a1c6-07ac21635072\", name=resources_Name, id=targetResourceId, tags=\"\", param1=['changedProperties_sku.name_previousValue'], param2=['changedProperties_sku.tier_newValue'], param3=count_\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "dbe3fd66-fb2a-9d46-b162-1791e21da236",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Resiliency checklist for specific Azure services",
+ "url": "https://learn.microsoft.com/en-us/azure/architecture/checklist/resiliency-per-service#app-service"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "It is strongly recommended to create separate App Service plans for production and test environments to avoid using slots within your production deployment for testing purposes.\n",
+ "pgVerified": false,
+ "description": "Create separate App Service plans for production and test",
+ "potentialBenefits": "Protects prod performance; avoids test impact",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Web/serverFarms",
+ "recommendationImpact": "High",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "6320abf6-f917-1843-b2ae-4779c35985ae",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Automatic scaling in Azure App Service",
+ "url": "https://learn.microsoft.com/en-us/azure/app-service/manage-automatic-scaling?tabs=azure-portal"
+ },
+ {
+ "name": "Auto Scale Web Apps",
+ "url": "https://learn.microsoft.com/en-us/azure/azure-monitor/autoscale/autoscale-get-started"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Enabling Autoscale/Automatic Scaling for your Azure App Service ensures sufficient resources for incoming requests. Autoscaling is rule-based, whereas Automatic Scaling, a newer feature, automatically adjusts resources based on HTTP traffic.\n",
+ "pgVerified": false,
+ "description": "Enable Autoscale/Automatic scaling to ensure adequate resources are available to service requests",
+ "potentialBenefits": "Optimizes resources for traffic",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Web/serverFarms",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// under-development\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "493f6079-3bb6-4a56-96ba-ab3248474cb1",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Enable diagnostics logging for apps in Azure App Service",
+ "url": "https://learn.microsoft.com/azure/app-service/troubleshoot-diagnostic-logs"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Enabling diagnostics logging for your Azure App Service is crucial for monitoring and diagnostics, including both application logging and web server logging.\n",
+ "pgVerified": false,
+ "description": "Enable diagnostics logging",
+ "potentialBenefits": "Monitoring and Alerting",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Web/sites",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "a7e8bb3d-8ceb-442d-b26f-007cd63f9ffc",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Application Insights",
+ "url": "https://learn.microsoft.com/azure/application-insights/app-insights-overview"
+ },
+ {
+ "name": "Application monitoring for Azure App Service",
+ "url": "https://learn.microsoft.com/azure/azure-monitor/app/azure-web-apps"
+ }
+ ],
+ "recommendationControl": "Monitoring and Alerting",
+ "longDescription": "Use Application Insights to monitor app performance and load behavior, offering real-time insights, issue diagnosis, and root-cause analysis. It supports ASP.NET, ASP.NET Core, Java, and Node.js on Azure App Service, now with built-in monitoring.\n",
+ "pgVerified": false,
+ "description": "Monitor Performance",
+ "potentialBenefits": "Real-time insights and issue diagnosis",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Web/sites",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "78a5c033-ff51-4332-8a71-83464c34494b",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Resiliency checklist for specific Azure services",
+ "url": "https://learn.microsoft.com/azure/architecture/checklist/resiliency-per-service#app-service"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "If your solution includes both a web front end and a web API, decomposing them into separate App Service apps facilitates solution decomposition by workload, allowing for independent scaling. Initially, you can deploy both in the same plan and separate them for independent scaling when necessary.\n",
+ "pgVerified": false,
+ "description": "Separate web apps from web APIs",
+ "potentialBenefits": "Independent scaling, easier management",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Web/sites",
+ "recommendationImpact": "Low",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "3f9ddb59-0bb3-4acb-9c9b-99aa1776f0ab",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Resiliency checklist",
+ "url": "https://learn.microsoft.com/azure/architecture/checklist/resiliency-per-service#app-service"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "Creating a separate storage account for logs and not using the same one for application data prevents logging activities from reducing application performance by ensuring that the resources dedicated to handling application data are not burdened by logging processes.\n",
+ "pgVerified": false,
+ "description": "Create a separate storage account for logs",
+ "potentialBenefits": "Improves app performance",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Web/sites",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "no",
+ "query": "// cannot-be-validated-with-arg\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "a1d91661-32d4-430b-b3b6-5adeb0975df7",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Set up staging environments in Azure App Service",
+ "url": "https://learn.microsoft.com/azure/app-service-web/web-sites-staged-publishing"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Create a deployment slot for staging to deploy updates, verify them, and ensure all instances are warmed up before production swap, reducing bad update chances. An LKG slot allows easy rollback to a previous good deployment if issues arise later, enhancing reliability.\n",
+ "pgVerified": false,
+ "description": "Deploy to a staging slot",
+ "potentialBenefits": "Safer updates and easy rollback",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Web/sites",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Display App Service with the count of deployment slots for Apps under eligible App service plans and it shows if deployment slot is enabled or not\r\n\r\nresources\r\n| where type =~ 'microsoft.web/sites' or type =~ 'microsoft.web/sites/slots'\r\n| extend isSlot = iff(type =~ 'microsoft.web/sites/slots', 1, 0)\r\n| extend AspName = iff(isSlot == 1, split(name, '/')[0], name)\r\n| extend Sku = tostring(properties.sku)\r\n| where tolower(Sku) contains \"standard\" or tolower(Sku) contains \"premium\" or tolower(Sku) contains \"isolatedv2\"\r\n| project id, name, AspName, isSlot, Sku\r\n| summarize Slots = countif(isSlot == 1) by id, name, AspName, Sku\r\n| extend DeploymentSlotEnabled = iff(Slots > 1, true, false)\r\n| where DeploymentSlotEnabled = false\r\n| project recommendationId=\"a1d91661-32d4-430b-b3b6-5adeb0975df7\", name, id, tags=\"\", param1=Sku, param2=Slots, param3=\"DeploymentSlotEnabled=false\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "0b80b67c-afbe-4988-ad58-a85a146b681e",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Configure web apps in Azure App Service",
+ "url": "https://learn.microsoft.com/azure/app-service-web/web-sites-configure"
+ }
+ ],
+ "recommendationControl": "Other Best Practices",
+ "longDescription": "Use app settings for configuration and define them in Resource Manager templates or via PowerShell to facilitate part of an automated deployment/update process for improved reliability.\n",
+ "pgVerified": false,
+ "description": "Store configuration as app settings",
+ "potentialBenefits": "Enhanced reliability via automation",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Web/sites",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of Azure App Service resources that don't have App Settings configured\r\n\r\nappserviceresources\r\n| where type == \"microsoft.web/sites/config\"\r\n| extend AppSettings = iif(isempty(properties.AppSettings), true, false)\r\n| where AppSettings == false\r\n| project recommendationId=\"0b80b67c-afbe-4988-ad58-a85a146b681e\", id, name, tags=\"\", param1=\"AppSettings is not configured\"\r\n\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "fd049c28-ae6d-48f0-a641-cc3ba1a3fe1d",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Monitor the health of App Service instances",
+ "url": "https://learn.microsoft.com/en-us/azure/app-service/monitor-instances-health-check?tabs=dotnet#enable-health-check"
+ }
+ ],
+ "recommendationControl": "Other Best Practices",
+ "longDescription": "Use Health Check for production workloads. Health check increases your application's availability by rerouting requests away from unhealthy instances, and replacing instances if they remain unhealthy. The Health check path should check critical components of your application.\n",
+ "pgVerified": false,
+ "description": "Enable Health check for App Services",
+ "potentialBenefits": "Enhanced reliability via automation",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Web/sites",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Check if Health Check is enabled for App Service\r\n\r\nresources\r\n| where type =~ 'microsoft.web/sites'\r\n| where properties.kind has 'app'\r\n| join kind = inner\r\n (\r\n appserviceresources\r\n | where isnull(properties.HealthCheckPath) == true\r\n | project name\r\n ) on name\r\n| project recommendationId = \"fd049c28-ae6d-48f0-a641-cc3ba1a3fe1d\", name, id, tags, param1 = \"Healthcheckpath = not set\"\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "aab6b4a4-9981-43a4-8728-35c7ecbb746d",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Set up Azure App Service access restrictions",
+ "url": "https://learn.microsoft.com/en-us/azure/app-service/app-service-ip-restrictions?tabs=azurecli"
+ }
+ ],
+ "recommendationControl": "Governance",
+ "longDescription": "Use network access restrictions to define a priority-ordered allow/deny list that controls network access to your app. Web application firewalls, such as the one available in Application Gateway, are recommended for protection of public-facing web applications.\n",
+ "pgVerified": false,
+ "description": "Configure network access restrictions",
+ "potentialBenefits": "Enhanced security",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Web/sites",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Check if Network access restrictions defined for App service\r\n\r\nresources\r\n| where type =~ 'microsoft.web/sites'\r\n| where properties.kind has 'app'\r\n| join kind = inner\r\n (\r\n appserviceresources\r\n | mv-expand IpSecurityRestrictions = properties.IpSecurityRestrictions\r\n | where isnotnull(IpSecurityRestrictions) == true\r\n | project name\r\n ) on name\r\n| project recommendationId = \"aab6b4a4-9981-43a4-8728-35c7ecbb746d\", name, id, tags, param1 = \"No network restrictions set\"\r\n"
+ },
+ {
+ "publishedToAdvisor": false,
+ "aprlGuid": "9e6682ac-31bc-4635-9959-ab74b52454e6",
+ "recommendationTypeId": null,
+ "recommendationMetadataState": "Active",
+ "learnMoreLink": [
+ {
+ "name": "Ultimate guide to running healthy apps in the cloud",
+ "url": "https://azure.github.io/AppService/2020/05/15/Robust-Apps-for-the-cloud.html"
+ }
+ ],
+ "recommendationControl": "Scalability",
+ "longDescription": "App Service should be configured with a minimum of two instances for production workloads. If apps have a longer warmup time a minimum of three instances should be used.\n",
+ "pgVerified": false,
+ "description": "Set minimum instance count to 2 for app service",
+ "potentialBenefits": "Improves app performace",
+ "publishedToLearn": false,
+ "tags": null,
+ "recommendationResourceType": "Microsoft.Web/sites",
+ "recommendationImpact": "Medium",
+ "automationAvailable": "arg",
+ "query": "// Azure Resource Graph Query\r\n// Provides a list of App services that do not have minimum instance count of 2\r\n\r\nresources\r\n| where type =~ 'microsoft.web/sites'\r\n| where properties.kind has 'app'\r\n| join kind = inner\r\n (\r\n appserviceresources\r\n | where properties.PreWarmedInstanceCount < 2\r\n | project name\r\n ) on name\r\n| project recommendationId = \"9e6682ac-31bc-4635-9959-ab74b52454e6\", name, id, tags, param1 = \"PreWarmedInstanceCount is less than 2\"\r\n"
+ }
+]
diff --git a/src/tests/data/readme.md b/src/tests/data/readme.md
new file mode 100644
index 0000000..d78d572
--- /dev/null
+++ b/src/tests/data/readme.md
@@ -0,0 +1,2 @@
+# This is test data that contains the APRL queries in a json object.
+It is meant to facilitate the build out of the new structure without cloning the entire APRL repository.
\ No newline at end of file
diff --git a/src/tests/pester.ps1 b/src/tests/pester.ps1
new file mode 100644
index 0000000..a98debf
--- /dev/null
+++ b/src/tests/pester.ps1
@@ -0,0 +1,29 @@
+$config = New-PesterConfiguration
+$config.Run.Path = './'
+$config.CodeCoverage.Path = "$PSScriptRoot/../modules/wara/scope/scope.psm1"
+$config.CodeCoverage.Enabled = $true
+#$config.CodeCoverage.OutputFormat = 'JaCoCo'
+#$config.CodeCoverage.OutputPath = './coverage.xml'
+$config.Run.PassThru = $true
+
+# Run Pester with the configuration
+$result = Invoke-Pester -Configuration $config
+
+$resultOfRun = $($result.Result -eq 'Passed') ? "✅ Passed" : "❌Failed"
+$passedCount = $($result.PassedCount -eq $result.TotalCount) ? "✅ $($result.PassedCount)" : "❌ $($result.PassedCount)"
+$failedCount = $($result.FailedCount -gt 0) ? "❌ $($result.FailedCount)" : "✅ $($result.FailedCount)"
+$coveragePercent = $($result.CodeCoverage.CoveragePercent -ge $result.CodeCoverage.CoveragePercentTarget) ? "✅ $($result.CodeCoverage.CoveragePercent)" : "❌ $($result.CodeCoverage.CoveragePercent)"
+
+
+$markdown = @"
+# Code Coverage Report - Scope.psm1
+| Metric | Value |
+|-----------------|-------------|
+| Result | $resultofRun |
+| Passed Count | $passedCount |
+| Failed Count | $failedCount |
+| Coverage (%) | $coveragePercent |
+| Target Coverage (%) | $($result.CodeCoverage.CoveragePercentTarget) |
+"@
+
+$markdown | Out-File -FilePath './coverage.md'
\ No newline at end of file
diff --git a/src/tests/scope.tests.ps1 b/src/tests/scope.tests.ps1
new file mode 100644
index 0000000..4d0f3c6
--- /dev/null
+++ b/src/tests/scope.tests.ps1
@@ -0,0 +1,64 @@
+
+BeforeAll {
+ $modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\modules\wara\scope\scope.psm1'
+ Import-Module -Name "C:\dev\repos\Well-Architected-Reliability-Assessment\src\modules\wara\scope\scope.psm1" -Force
+ $ObjectList = @(
+ [PSCustomObject]@{ id = '/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/test1/providers/Microsoft.Compute/virtualMachines/TestVM1' },
+ [PSCustomObject]@{ id = '/subscriptions/22222222-2222-2222-2222-222222222222/resourceGroups/test2/providers/Microsoft.Compute/virtualMachines/TestVM2' },
+ [PSCustomObject]@{ id = '/subscriptions/33333333-3333-3333-3333-333333333333/resourceGroups/test3/providers/Microsoft.Compute/virtualMachines/TestVM3' },
+ [PSCustomObject]@{ id = '/subscriptions/44444444-4444-4444-4444-444444444444/resourceGroups/test4/providers/Microsoft.Compute/virtualMachines/TestVM4' },
+ [PSCustomObject]@{ id = '/subscriptions/55555555-5555-5555-5555-555555555555/resourceGroups/test5/providers/Microsoft.Compute/virtualMachines/TestVM5' },
+ [PSCustomObject]@{ id = '/subscriptions/66666666-6666-6666-6666-666666666666/resourceGroups/test6/providers/Microsoft.Compute/virtualMachines/TestVM6' },
+ [PSCustomObject]@{ id = '/subscriptions/77777777-7777-7777-7777-777777777777/resourceGroups/test7/providers/Microsoft.Compute/virtualMachines/TestVM7' },
+ [PSCustomObject]@{ id = '/subscriptions/88888888-8888-8888-8888-888888888888/resourceGroups/test8/providers/Microsoft.Compute/virtualMachines/TestVM8' },
+ [PSCustomObject]@{ id = '/subscriptions/99999999-9999-9999-9999-999999999999/resourceGroups/test9/providers/Microsoft.Compute/virtualMachines/TestVM9' }
+ )
+
+ $SubscriptionFilterList = @('/subscriptions/11111111-1111-1111-1111-111111111111', '/subscriptions/33333333-3333-3333-3333-333333333333')
+ $ResourceGroupFilterList = @('/subscriptions/22222222-2222-2222-2222-222222222222/resourceGroups/test2', '/subscriptions/44444444-4444-4444-4444-444444444444/resourceGroups/test4')
+ $ResourceFilterList = @('/subscriptions/77777777-7777-7777-7777-777777777777/resourceGroups/test7/providers/Microsoft.Compute/virtualMachines/TestVM7', '/subscriptions/66666666-6666-6666-6666-666666666666/resourceGroups/test6/providers/Microsoft.Compute/virtualMachines/TestVM6')
+ $KeyColumn = 'id'
+}
+Describe "Get-WAFSubscriptionsByList" {
+ Context "When given a valid list of subscriptions" {
+ It "Should return the corresponding subscriptions" {
+ $result = Get-WAFSubscriptionsByList -ObjectList $ObjectList -FilterList $SubscriptionFilterList -KeyColumn $keycolumn
+ $result | Should -HaveCount 2
+ $result.id | Should -Contain '/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/test1/providers/Microsoft.Compute/virtualMachines/TestVM1'
+ $result.id | Should -Contain '/subscriptions/33333333-3333-3333-3333-333333333333/resourceGroups/test3/providers/Microsoft.Compute/virtualMachines/TestVM3'
+ }
+ }
+}
+
+Describe "Get-WAFResourceGroupsByList" {
+ Context "When given a valid list of resource groups" {
+ It "Should return the corresponding resource ids that match the resource groups" {
+ $result = Get-WAFResourceGroupsByList -ObjectList $ObjectList -FilterList $ResourceGroupFilterList -KeyColumn $keycolumn
+ $result | Should -HaveCount 2
+ $result.id | Should -Contain '/subscriptions/22222222-2222-2222-2222-222222222222/resourceGroups/test2/providers/Microsoft.Compute/virtualMachines/TestVM2'
+ $result.id | Should -Contain '/subscriptions/44444444-4444-4444-4444-444444444444/resourceGroups/test4/providers/Microsoft.Compute/virtualMachines/TestVM4'
+ }
+ }
+}
+
+Describe "Get-WAFResourcesByList" {
+ Context "When given a valid list of resource ids" {
+ It "Should return the corresponding resource ids that match the resource ids in the filter" {
+ $result = Get-WAFResourcesByList -ObjectList $ObjectList -FilterList $ResourceFilterList -KeyColumn $keycolumn
+ $result | Should -HaveCount 2
+ $result.id | Should -Contain '/subscriptions/77777777-7777-7777-7777-777777777777/resourceGroups/test7/providers/Microsoft.Compute/virtualMachines/TestVM7'
+ $result.id | Should -Contain '/subscriptions/66666666-6666-6666-6666-666666666666/resourceGroups/test6/providers/Microsoft.Compute/virtualMachines/TestVM6'
+ }
+ }
+}
+
+<# Describe "Get-WAFFilteredResourceList" {
+ Context "When given a valid list of resource ids, resource groups, and subscriptions it should filter the list and only return resourceids that are in scope." {
+ It "Should return the corresponding resource ids that match the resource ids" {
+ $result = Get-WAFFilteredResourceList -ObjectList $ObjectList -FilterList $FilterList -KeyColumn $KeyColumn
+ $result | Should -HaveCount 2
+ $result.id | Should -Contain '/subscriptions/77777777-7777-7777-7777-777777777777/resourceGroups/test7/providers/Microsoft.Compute/virtualMachines/TestVM7'
+ $result.id | Should -Contain '/subscriptions/33333333-3333-3333-3333-333333333333/resourceGroups/test3/providers/Microsoft.Compute/virtualMachines/TestVM3'
+ }
+ }
+} #>
\ No newline at end of file