Skip to content

Latest commit

 

History

History
3815 lines (3815 loc) · 202 KB

APRL_QueriesBook.workbook

File metadata and controls

3815 lines (3815 loc) · 202 KB

{ "version": "Notebook/1.0", "items": [ { "type": 9, "content": { "version": "KqlParameterItem/1.0", "crossComponentResources": [ "{Subscriptions}" ], "parameters": [ { "id": "eff9152a-176c-4422-b5b9-b7dc184e5bee", "version": "KqlParameterItem/1.0", "name": "Subscriptions", "type": 6, "isRequired": true, "multiSelect": true, "quote": "'", "delimiter": ",", "typeSettings": { "additionalResourceOptions": [ "value::all" ], "includeAll": false, "showDefault": false }, "timeContext": { "durationMs": 86400000 }, "value": [ "value::all" ] } ], "style": "pills", "queryType": 1, "resourceType": "microsoft.resourcegraph/resources" }, "name": "パラメーター - 1" }, { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "tabStyle": "bigger", "links": [ { "id": "fabe59e1-b050-49c2-81fd-808149919385", "cellValue": "selectedCategory", "linkTarget": "parameter", "linkLabel": "AI and ML", "subTarget": "AIandML", "style": "link" }, { "id": "5e307ab4-6fba-4a7a-9d6c-c5e3d54d9f38", "cellValue": "selectedCategory", "linkTarget": "parameter", "linkLabel": "Compute", "subTarget": "Compute", "style": "link" }, { "id": "ee6d7ce2-77d1-4909-902a-e81bef9ecc7f", "cellValue": "selectedCategory", "linkTarget": "parameter", "linkLabel": "Containers", "subTarget": "Containers", "style": "link" }, { "id": "7f75be61-0ba1-4134-b735-f37a2ec1b577", "cellValue": "selectedCategory", "linkTarget": "parameter", "linkLabel": "Databases", "subTarget": "Databases", "style": "link" }, { "id": "7ba3e339-bfad-4d10-ac62-8266fd9998d8", "cellValue": "selectedCategory", "linkTarget": "parameter", "linkLabel": "Integration", "subTarget": "Integration", "style": "link" }, { "id": "49313fda-6e50-4fa5-8c95-2ac12b290514", "cellValue": "selectedCategory", "linkTarget": "parameter", "linkLabel": "Internet of Things", "subTarget": "InternetOfThings", "style": "link" }, { "id": "96b82891-5502-46cd-80b9-643115a32c6d", "cellValue": "selectedCategory", "linkTarget": "parameter", "linkLabel": "Management", "subTarget": "Management", "style": "link" }, { "id": "d1a3e9bc-eaf1-4bd6-9595-9d27436611fb", "cellValue": "selectedCategory", "linkTarget": "parameter", "linkLabel": "Migration", "subTarget": "Migration", "style": "link" }, { "id": "b0fb80cb-b967-4e45-abad-057b66c272ed", "cellValue": "selectedCategory", "linkTarget": "parameter", "linkLabel": "Monitoring", "subTarget": "Monitoring", "style": "link" }, { "id": "8d76b21e-0020-4054-a9cc-35558af51d27", "cellValue": "selectedCategory", "linkTarget": "parameter", "linkLabel": "Networking", "subTarget": "Networking", "style": "link" }, { "id": "cbbe04d7-739c-4a58-8798-460a9839bb9f", "cellValue": "selectedCategory", "linkTarget": "parameter", "linkLabel": "Security", "subTarget": "Security", "style": "link" }, { "id": "cf607f83-df97-4493-8ad5-477b898dc811", "cellValue": "selectedCategory", "linkTarget": "parameter", "linkLabel": "Specialized Workloads", "subTarget": "SpecializedWorkloads", "style": "link" }, { "id": "733928a5-2426-41b8-b454-3211651edbc7", "cellValue": "selectedCategory", "linkTarget": "parameter", "linkLabel": "Storage", "subTarget": "Storage", "style": "link" }, { "id": "8def5550-a8be-4db9-ba79-7e51cf2f95d9", "cellValue": "selectedCategory", "linkTarget": "parameter", "linkLabel": "Web", "subTarget": "Web", "style": "link" } ] }, "name": "Categories" }, { "type": 12, "content": { "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "links": [ { "id": "c53e52cd-7997-4442-ae5a-f643cc60adf6", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Azure Databricks", "subTarget": "databricks", "style": "link" } ] }, "name": "AI and ML Tabs" }, { "type": 1, "content": { "json": "Under-Development", "style": "info" }, "name": "テキスト - 1" } ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "AIandML" }, "name": "AI and ML" }, { "type": 12, "content": { "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "links": [ { "id": "e9df16e4-ccb7-4e40-8200-e0c9055cc1ea", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Azure Site Recovery", "subTarget": "ASR", "style": "link" }, { "id": "ae52ec72-9750-46a4-8941-57723fb9232e", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Compute Gallery", "subTarget": "ComputeGallery", "style": "link" }, { "id": "2f53ff41-40e0-4cf7-aa9e-dfe02436a839", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Image Templates", "subTarget": "ImageTemplates", "style": "link" }, { "id": "c1b95d43-fa6d-4bd4-8cbe-d401a6f8b819", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Virtual Machine Scale Sets", "subTarget": "VMSS", "style": "link" }, { "id": "c3916279-9d38-43db-b752-b5e39e3a41e0", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Virtual Machines", "subTarget": "VM", "style": "link" } ] }, "name": "Compute Tabs" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="asr-2" , name = properties.providerSpecificDetails.recoveryAzureVMName, id=properties.providerSpecificDetails.dataSourceInfo.resourceId", "size": 0, "showAnalytics": true, "title": "ASR-2 - Perform a test failover to validate the functionality and performance of the VMs in the target location", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ASR" }, "name": "ASR-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="cg-1",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", "size": 0, "showAnalytics": true, "title": "CG-1 - A minimum of three replicas should be kept for production image versions", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ComputeGallery" }, "name": "CG-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="cg-2",name,id,tags,param1=strcat("GalleryName: ",GalleryName),param2=strcat("ImageName: ",ImageName),param3=strcat("StorageAccountType: ",StorageAccountType)", "size": 0, "showAnalytics": true, "title": "CG-2 - Zone redundant storage should be used for image versions", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ComputeGallery" }, "name": "CG-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="cg-3",name,id,tags,param1=strcat("VMGeneration: ",VMGeneration)", "size": 0, "showAnalytics": true, "title": "CG-3 - Consider using hyper-V generation version 2 images where possible", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ComputeGallery" }, "name": "CG-3" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "it-2", name, id, param1=strcat("replicationRegions:",parse_json(distribution).replicationRegions)", "size": 0, "showAnalytics": true, "title": "IT-2 - Replicate your Image Templates to a secondary region", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ImageTemplates" }, "name": "IT-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vmss-1", name, id, tags, param1 = strcat("orchestrationMode: ", tostring(properties.orchestrationMode))", "size": 0, "showAnalytics": true, "title": "VMSS-1 - Deploy VMSS with Flex orchestration mode instead of Uniform", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VMSS" }, "name": "VMSS-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vmss-2", name, id, param1 = "extension: null"", "size": 0, "showAnalytics": true, "title": "VMSS-2 - Enable VMSS application health monitoring", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VMSS" }, "name": "VMSS-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vmss-3", name, id, param1 = "automaticRepairsPolicy: Disabled"", "size": 0, "showAnalytics": true, "title": "VMSS-3 - Enable Automatic Repair policy", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VMSS" }, "name": "VMSS-3" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vmss-4", name, id, tags, param1 = "autoscalesettings: Disabled"\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VMSS-4 - Configure VMSS Autoscale to custom and configure the scaling metrics", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VMSS" }, "name": "VMSS-4" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vmss-5", name, id, tags, param1 = "predictiveAutoscalePolicy_scaleMode: Disabled"\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VMSS-5 - Enable Predictive autoscale and configure at least for Forecast Only", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VMSS" }, "name": "VMSS-5" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vmss-6", name, id, tags, param1 = "strictly zoneBalance: Enabled"\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VMSS-6 - Disable Force strictly even balance across zones to avoid scale in and out fail attempts", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VMSS" }, "name": "VMSS-6" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Find VMSS instances where Spreading algorithm is set to Static\r\nresources\r\n| where type == "microsoft.compute/virtualmachinescalesets"\r\n| where properties.platformFaultDomainCount > 1\r\n| project recommendationId = "vmss-7", name, id, tags, param1 = "platformFaultDomainCount: Static"\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VMSS-7 - Configure Allocation Policy Spreading algorithm to Max Spreading", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VMSS" }, "name": "VMSS-7" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vmss-8", name, id, tags, param1 = "AvailabilityZones: Single Zone"\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VMSS-8 - Deploy VMSS across availability zones with VMSS Flex", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VMSS" }, "name": "VMSS-8" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "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 = "vmss-9", 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 = "vmss-9", 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)", "size": 0, "showAnalytics": true, "title": "VMSS-9 - Set Patch orchestration options to Azure-orchestrated", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VMSS" }, "name": "VMSS-9" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="vm-2", name, id, tags, param1="No Zone"", "size": 0, "showAnalytics": true, "title": "VM-2 - Deploy VMs across Availability Zones", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Find all VMs that do NOT have replication with ASR 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/replicationFabrics/replicationProtectionContainers/replicationProtectedItems'\r\n | where properties.providerSpecificDetails.dataSourceInfo.datasourceType =~ 'AzureVm'\r\n | project id=properties.providerSpecificDetails.dataSourceInfo.resourceId\r\n | extend name=strcat_array(array_slice(split(id, '/'), 8, -1), '/')\r\n) on name\r\n| where isnull(id1)\r\n| project-away id1\r\n| project-away name1\r\n| project recommendationId = "vm-4", name, id, tags\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VM-4 - Replicate VMs using Azure Site Recovery", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-4" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vm-5", name, id, tags", "size": 0, "showAnalytics": true, "title": "VM-5 - Use Managed Disks for VM disks", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-5" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Find all VMs that only have a single Disk\r\nResources\r\n| where type =~ 'Microsoft.Compute/virtualMachines'\r\n| where array_length(properties.storageProfile.dataDisks) < 1\r\n| project recommendationId = "vm-6", name, id, tags", "size": 0, "showAnalytics": true, "title": "VM-6 - Host application or database data on a data disk", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-6" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vm-7", name, id, tags\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VM-7 - Backup VMs with Azure Backup service", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-7" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Find all disks with StandardHDD sku attached to VMs\r\nResources\r\n| where type =~ 'Microsoft.Compute/disks'\r\n| where sku.name == 'Standard_LRS' and sku.tier == 'Standard'\r\n| where managedBy != ""\r\n| project recommendationId = "vm-8", name, id, tags, param1=strcat("managedBy: ", managedBy)", "size": 0, "showAnalytics": true, "title": "VM-8 - Production VMs should be using SSD disks", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-8" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vm-9", name, id, tags", "size": 0, "showAnalytics": true, "title": "VM-9 - Review VMs in stopped state", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-9" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vm-10", name, id, tags, param1, param2\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VM-10 - Enable Accelerated Networking (AccelNet)", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-10" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vm-12", name, id, tags\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VM-12 - VMs should not have a Public IP directly associated", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-12" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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, nicId = nic.id, nicName=split(nic.id, '/')[8]\r\n | extend nicId = tostring(nicId)\r\n) on nicId\r\n| project recommendationId = "vm-13", name=vmName, id = vmId, param1 = strcat("nic-name=", nicName)", "size": 0, "showAnalytics": true, "title": "VM-13 - VM network interfaces and associated subnets both have a Network Security Group (NSG) associated", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-13" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vm-14", name, id, tags\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VM-14 - IP Forwarding should only be enabled for Network Virtual Appliances", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-14" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vm-15", name, id, tags\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VM-15 - Customer DNS Servers should be configured in the Virtual Network level", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-15" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vm-16", name, id, tags, param1, param2\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VM-16 - Shared disks should only be enabled in clustered servers", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-16" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vm-17", name, id, tags, param1\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VM-17 - Network access to the VM disk should be set to “Disable public access and enable private access”", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-17" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Find all VMs in "NonCompliant" 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 policyAssignmentName = properties.policyAssignmentName,\r\n policyDefinitionName = properties.policyDefinitionName,\r\n lowerCasePolicyDefinitionIdOfPolicyState = tolower(properties.policyDefinitionId),\r\n lowerCaseVmIdOfPolicyState = tolower(properties.resourceId)\r\n| join kind = leftouter (\r\n PolicyResources\r\n | where type =~ "Microsoft.Authorization/policyDefinitions"\r\n | project lowerCasePolicyDefinitionId = tolower(id), policyDefinitionDisplayName = properties.displayName\r\n )\r\n on $left.lowerCasePolicyDefinitionIdOfPolicyState == $right.lowerCasePolicyDefinitionId\r\n| project policyAssignmentName, policyDefinitionName, policyDefinitionDisplayName, lowerCaseVmIdOfPolicyState\r\n| join kind = leftouter (\r\n Resources\r\n | where type =~ "Microsoft.Compute/virtualMachines"\r\n | project vmName = name, vmId = id, vmTags = tags, lowerCaseVmId = tolower(id)\r\n )\r\n on $left.lowerCaseVmIdOfPolicyState == $right.lowerCaseVmId\r\n| extend\r\n param1 = strcat("AssignmentName: ", policyAssignmentName),\r\n param2 = strcat("DefinitionName: ", policyDefinitionDisplayName), // Align to Azure portal's term.\r\n param3 = strcat("DefinitionID: ", policyDefinitionName) // Align to Azure portal's term.\r\n| project recommendationId = "vm-18", name = vmName, id = vmId, tags = vmTags, param1, param2, param3", "size": 0, "showAnalytics": true, "title": "VM-18 - Ensure that your VMs are compliant with Azure Policies", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-18" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Find all disks that are not encrypted\r\nresources\r\n| where type == "microsoft.compute/disks"\r\n| extend encryptionType = properties.encryption.type\r\n| extend diskState = properties.diskState\r\n| where encryptionType !in ("EncryptionAtRestWithCustomerKey", "EncryptionAtRestWithPlatformAndCustomerKeys", "EncryptionAtRestWithPlatformKey")\r\n| project recommendationId="vm-19", name, id, tags, param1=strcat("encryptionType: " , properties.encryption.type), param2= strcat ("diskstate: ", properties.diskState)", "size": 0, "showAnalytics": true, "title": "VM-19 - Enable disk encryption and data at rest encryption by default", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-19" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vm-21", name, id, tags, param1, param2, param3, param4, param5", "size": 0, "showAnalytics": true, "title": "VM-21 - Configure diagnostic settings for all Azure Virtual Machines", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-21" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vm-22",name, id, tags\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VM-22 - Use maintenance configurations for the VMs", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-22" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vm-24", name, id, tags, param1=strcat("AffectedDisks: ", disks)", "size": 0, "showAnalytics": true, "title": "VM-24 - Mission Critical Workloads should be using Premium or Ultra Disks", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VM" }, "name": "VM-24" } ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "Compute" }, "name": "Compute " }, { "type": 12, "content": { "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "links": [ { "id": "492da5ac-2294-4de5-9107-383962771e1e", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "AKS", "subTarget": "AKS", "style": "link" }, { "id": "29a42bef-4e07-4c82-a6de-ab01ce2053e9", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Container Registry", "subTarget": "ContainerRegistry", "style": "link" } ] }, "name": "Containers Tabs" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Returns AKS clusters that do not have any availability zones enabled\r\nresources\r\n| where type == 'microsoft.containerservice/managedclusters'\r\n| project id, name, location, properties.agentPoolProfiles\r\n| mv-expand properties_agentPoolProfiles\r\n| where isempty(array_length(properties_agentPoolProfiles.availabilityZones))\r\n| project recommendationId="aks-1", id, name, param1=strcat("nodePoolName: ", properties_agentPoolProfiles.name), param2=strcat("orchestratorVersion: ", properties_agentPoolProfiles.orchestratorVersion), param3=strcat("currentOrchestratorVersion: ", properties_agentPoolProfiles.currentOrchestratorVersion), param4=strcat("numberOfZones: ", iff(isempty(array_length(properties_agentPoolProfiles.availabilityZones)), 0, array_length(properties_agentPoolProfiles.availabilityZones)))", "size": 0, "showAnalytics": true, "title": "AKS-1 - Deploy AKS cluster across availability zones", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AKS" }, "name": "AKS-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Returns each AKS cluster with nodepools that do not have taints set\r\nresources\r\n| where type == "microsoft.containerservice/managedclusters"\r\n| mv-expand agentPoolProfile = properties.agentPoolProfiles\r\n| extend taint = tostring(parse_json(agentPoolProfile.nodeTaints))\r\n| extend nodePool = tostring(parse_json(agentPoolProfile.name))\r\n| where isempty(taint)\r\n| project recommendationid="aks-2", id, name, param1=strcat("nodepoolName: ", nodePool), param2=strcat("taint: ", iff(isempty(taint), "None", taint))", "size": 0, "showAnalytics": true, "title": "AKS-2 - Isolate system and application pods", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AKS" }, "name": "AKS-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="aks-3", name, id, tags, param1=strcat("aadProfile: ", aadProfile), param2=strcat("disablelocalAdmin: ",disablelocalAdmin), param3=strcat("RBAC: ", RBAC)", "size": 0, "showAnalytics": true, "title": "AKS-3 - Enable AKS-managed Azure AD integration", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AKS" }, "name": "AKS-3" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="aks-4", name, id, tags, param1=strcat("networkProfile :",networkProfile)", "size": 0, "showAnalytics": true, "title": "AKS-4 - Configure Azure CNI networking for dynamic allocation of IPs", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AKS" }, "name": "AKS-4" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="aks-5", name, id, tags, param1=strcat("autoScaling :", autoScaling)", "size": 0, "showAnalytics": true, "title": "AKS-5 - Enable the cluster auto-scaler on an existing cluster", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AKS" }, "name": "AKS-5" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "aks-6", name, id, tags, param1", "size": 0, "showAnalytics": true, "title": "AKS-6 - Back up Azure Kubernetes Service", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AKS" }, "name": "AKS-6" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="aks-12", id, name, param1=strcat("skuName: ", sku.name), param2=strcat("skuTier: ", sku.tier)", "size": 0, "showAnalytics": true, "title": "AKS-12 - Update AKS tier to Standard", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AKS" }, "name": "AKS-12" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="aks-13",id, name, param1=strcat("azureMonitorProfileEnabled: ", iff(isempty(azureMonitor), "false", azureMonitor)), param2=strcat("containerInsightsEnabled: ", iff(isempty(insights), "false", insights))", "size": 0, "showAnalytics": true, "title": "AKS-13 - Enable AKS Monitoring", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AKS" }, "name": "AKS-13" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="aks-16", id, name, param1=strcat("gitopsEnabled: ", "false")", "size": 0, "showAnalytics": true, "title": "AKS-16 - Enable GitOps when using DevOps frameworks", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AKS" }, "name": "AKS-16" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "cr-1", name, id, tags, param1=strcat("SkuName: ", tostring(sku.name))\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "CR-1 - Use Premium tier for critical production workloads", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ContainerRegistry" }, "name": "CR-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "cr-2", name, id, tags, param1=strcat("zoneRedundancy: ", tostring(properties.zoneRedundancy))\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "CR-2 - Enable zone redundancy", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ContainerRegistry" }, "name": "CR-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "cr-3", name=registryName, id=registryId, tags\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "CR-3 - Enable geo-replication", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ContainerRegistry" }, "name": "CR-3" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "cr-6", name=registryName, id=registryId, tags=registryTags, param1=strcat('resourceGroupName:',resourceGroup), param2=strcat('resourceGroupId:',resourceGroupId)", "size": 0, "showAnalytics": true, "title": "CR-6 - Move Container Registry to a dedicated resource group", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ContainerRegistry" }, "name": "CR-6" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "cr-7", name, id, tags, param1='retentionPolicy:disabled'\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "CR-7 - Manage registry size", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ContainerRegistry" }, "name": "CR-7" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "cr-8", name, id, tags\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "CR-8 - Disable anonymous pull access", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ContainerRegistry" }, "name": "CR-8" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "cr-12", name, id, tags\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "CR-12 - Enable soft delete policy", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ContainerRegistry" }, "name": "CR-12" } ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "Containers" }, "name": "Containers" }, { "type": 12, "content": { "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "links": [ { "id": "c684e237-a8e6-4b61-b3a6-55fd806a0459", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "SQL DB", "subTarget": "SQLDB", "style": "link" }, { "id": "f57b2cab-42e8-4ce3-bab4-84ad411bcbdb", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Cosmos DB", "subTarget": "CosmosDB", "style": "link" }, { "id": "df8169ed-9f41-444b-bf12-18cdb7b4d1e6", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "DB for MySQL", "subTarget": "MySQL", "style": "link" }, { "id": "59fceab4-4134-40ef-8b1c-a21a32900e0b", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "DB for PostgreSQL", "subTarget": "PostgreSQL", "style": "link" }, { "id": "2b51ca91-fa58-4e78-882f-9d13bb1a33bc", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Redis Cache", "subTarget": "Redis", "style": "link" } ] }, "name": "Databases Tabs" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Provides a list of SQL databases that are not configured for Geo-redundant storage.\r\nresources\r\n| where type == "microsoft.sql/servers/databases"\r\n| where (properties['currentBackupStorageRedundancy'] ) <> 'Geo'\r\n| project recommendationId = "sqldb-1", name, id, tags, param1=strcat("CurrentGeoRedudancy=", properties['currentBackupStorageRedundancy'] )", "size": 0, "showAnalytics": true, "title": "SQLDB-1 - Use Active Geo Replication to Create a Readable Secondary in Another Region", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "SQLDB" }, "name": "SQLDB-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "sqldb-2", name, id, tags, param1= strcat("databaseId=", properties['databaseId'])", "size": 0, "showAnalytics": true, "title": "SQLDB-2 - Use Auto Failover Groups that can include one or multiple databases, typically used by the same application", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "SQLDB" }, "name": "SQLDB-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "Resources\r\n| where type = 'microsoft.sql/servers/databases'\r\n| where tolower(tostring(properties.zoneRedundant))='false'\r\n|project recommendationId = "sqldb-3", name, id, tags", "size": 0, "showAnalytics": true, "title": "SQLDB-3 - Use a Zone-Redundant Database", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "SQLDB" }, "name": "SQLDB-3" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "sqldb-5", name, id=databaseid1, param1=strcat("MonitoringMetrics=false" ), tags", "size": 0, "showAnalytics": true, "title": "SQLDB-5 - Monitor your Azure SQL Database in Near Real-Time to Detect Reliability Incidents", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "SQLDB" }, "name": "SQLDB-5" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "Resources\r\n| where type =~ 'Microsoft.DocumentDb/databaseAccounts'\r\n| where array_length(properties.locations) < 2\r\n| project recommendationId='cosmos-1', name, id, tags", "size": 0, "showAnalytics": true, "title": "COSMOS-1 - Configure at least two regions for high availability", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "CosmosDB" }, "name": "CCOSMOS-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "Resources\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='cosmos-3', name, id, tags", "size": 0, "showAnalytics": true, "title": "COSMOS-3 - Evaluate multi-region write capability", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "CosmosDB" }, "name": "CCOSMOS-3" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "Resources\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='cosmos-5', name, id, tags", "size": 0, "showAnalytics": true, "title": "COSMOS-5 - Configure continuous backup mode", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "CosmosDB" }, "name": "CCOSMOS-5" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "psql-1", name, id, tags, param1 = "ZoneRedundant: False"", "size": 0, "showAnalytics": true, "title": "MYSQL-1 - Enable HA with zone redundancy", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "MySQL" }, "name": "MYSQL-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "psql-2", name, id, tags, param1 = strcat("customWindow:", properties['maintenanceWindow']['customWindow'])", "size": 0, "showAnalytics": true, "title": "MYSQL-2 - Enable custom maintenance schedule", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "MySQL" }, "name": "MYSQL-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "psql-1", name, id, tags, param1 = "ZoneRedundant: False"", "size": 0, "showAnalytics": true, "title": "PSQL-1 - Enable HA with zone redundancy", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "PostgreSQL" }, "name": "PSQL-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "psql-2", name, id, tags, param1 = strcat("customWindow:", properties['maintenanceWindow']['customWindow'])", "size": 0, "showAnalytics": true, "title": "PSQL-2 - Enable custom maintenance schedule", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "PostgreSQL" }, "name": "PSQL-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "redis-1", name, id, tags, param1 = "AvailabilityZones: Single Zone"\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "REDIS-1 - Enable zone redundancy for Azure Cache for Redis", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "Redis" }, "name": "REDIS-1" } ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "Databases" }, "name": "Databases" }, { "type": 12, "content": { "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "links": [ { "id": "5ce7d89e-f15d-4cf8-992d-f94b00b4e589", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "API Management", "subTarget": "APIM", "style": "link" }, { "id": "062e674d-639d-48b6-a3a4-e43042ae48c1", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Event Grid", "subTarget": "EventGrid", "style": "link" }, { "id": "37bcdd26-7b99-4985-92fb-3b122a315339", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Event Hub", "subTarget": "EventHub", "style": "link" }, { "id": "87078446-c8dd-472d-b27e-2e21af69d97e", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Service Bus", "subTarget": "ServiceBus", "style": "link" } ] }, "name": "Integration Tabs" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "apim-1", name, id, tags, param1=strcat("SKU: ", skuName)", "size": 0, "showAnalytics": true, "title": "APIM-1 - Migrate API Management services to Premium SKU to support Availability Zones", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "APIM" }, "name": "APIM-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "apim-2", name, id, tags, param1="Zones: No Zone or Zonal", param2=strcat("Zones value: ", zoneValue )", "size": 0, "showAnalytics": true, "title": "APIM-2 - Enable Availability Zones on Premium API Management instances", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "APIM" }, "name": "APIM-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "evg-3", name, id, tags\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "EVG-3 - Configure Private Endpoints", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "EventGrid" }, "name": "EVG-3" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Find Event Hub namespace instances that are not zone redundant\r\nresources\r\n| where type == "microsoft.eventhub/namespaces"\r\n| where properties.zoneRedundant == false\r\n| project recommendationId = "evhns-1", name, id, param1 = "ZoneRedundant: False", tags\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "EVHNS-1 - Enable zone redundancy for Event Hub namespace", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "EventHub" }, "name": "EVHNS-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "sbns-1", 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')", "size": 0, "showAnalytics": true, "title": "SBNS-1 - Enable Availability Zones for Service Bus namespaces", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ServiceBus" }, "name": "SBNS-1" } ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "Integration" }, "name": "Integration" }, { "type": 12, "content": { "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "links": [ { "id": "8f67a024-5e87-4f3a-968b-166635410ec6", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "IoT Hub", "subTarget": "IoTHub", "style": "link" } ] }, "name": "Internet of Things Tabs" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="ioth-2", name, id, tags, param1=strcat("tier:", tostring(sku.tier))", "size": 0, "showAnalytics": true, "title": "IOTH-2 - Do not use free tier", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "InternetOfThings" }, "name": "IOTH-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="ioth-4", name=iotHubName, id, tags, param1='DPS:none'", "size": 0, "showAnalytics": true, "title": "IOTH-4 - Use Device Provisioning Service", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "InternetOfThings" }, "name": "IOTH-4" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="ioth-6", name, id, tags, param1='FallbackRouteEnabled:false'", "size": 0, "showAnalytics": true, "title": "IOTH-6 - Disabled Fallback Route", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "InternetOfThings" }, "name": "IOTH-6" } ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "InternetOfThings" }, "name": "Internet of Things" }, { "type": 12, "content": { "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "links": [ { "id": "6457f4ab-66dd-4c4a-8adb-165d48d5031f", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Automation Account", "subTarget": "Automation", "style": "link" }, { "id": "a811b8ba-696a-4766-ab62-8c70463f0fe8", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Management Groups", "subTarget": "ManagementGroup", "style": "link" }, { "id": "f4d7252a-05a0-4f27-99aa-ba026b348ae2", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Resource Groups", "subTarget": "ResourceGroup", "style": "link" } ] }, "name": "Management Tabs" }, { "type": 1, "content": { "json": "No Queries", "style": "info" }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "Automation" }, "name": "Automation Account Info" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="mg-1", name, id, tags", "size": 0, "showAnalytics": true, "title": "MG-1 - Subscriptions should not be placed under the Tenant Root Management Group", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ManagementGroup" }, "name": "MG-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="rg-1", name, id, tags\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "RG-1 - Resource group location alignment", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ResourceGroup" }, "name": "RG-1" } ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "Management" }, "name": "Management" }, { "type": 12, "content": { "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "links": [ { "id": "5f8a618d-d4c1-4bf7-a71b-174bca7cfd0a", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Azure Backup", "subTarget": "AzureBackup", "style": "link" } ] }, "name": "Migration Tabs" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// 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 = "bk-1", name, id, tags, param1=strcat("isUsingClassicAlerts: ", isUsingClassicAlerts), param2=strcat("isUsingJobsAlerts: ", isUsingJobsAlerts)", "size": 0, "showAnalytics": true, "title": "BK-1 - Migrate from classic alerts to built-in Azure Monitor alerts for Recovery services vaults", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AzureBackup" }, "name": "BK-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Displays all recovery services vaults that do not have cross region restore enabled\r\n\r\nresources\r\n| where type == "microsoft.recoveryservices/vaults"\r\n| where properties.properties.enableCrossRegionRestore != true\r\n| project recommendationId = "bk-2", name, id, tags", "size": 0, "showAnalytics": true, "title": "BK-2 - Opt-in to Cross Region Restore for all Geo-Redundant Storage (GRS) Azure Recovery Services vaults", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AzureBackup" }, "name": "BK-2" } ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "Migration" }, "name": "Migration" }, { "type": 12, "content": { "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "links": [ { "id": "a1a9ec16-8975-46f4-a4be-6b24afb12194", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Application Insights", "subTarget": "AppInsights", "style": "link" }, { "id": "cd0dc5bc-9912-49e7-8619-4bd22f8bfd36", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Log Analytics", "subTarget": "LogAnalytics", "style": "link" } ] }, "name": "Monitoring Tabs" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "resources\r\n| where type =~ "microsoft.insights/components"\r\n| extend IngestionMode = properties.IngestionMode\r\n| where IngestionMode =~ 'ApplicationInsights'\r\n| project recommendationId= "appi-1", name, id, tags, param1="ApplicationInsightsDeploymentType: Classic"", "size": 0, "showAnalytics": true, "title": "APPI-1 - Convert Classic Deployments", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AppInsights" }, "name": "APPI-1" }, { "type": 1, "content": { "json": "Under-Development", "style": "info" }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "LogAnalytics" }, "name": "Log Analytics Info" } ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "Monitoring" }, "name": "Monitoring" }, { "type": 12, "content": { "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "links": [ { "id": "f10cb33a-6442-4461-be38-419e4b722f68", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Application Gateway", "subTarget": "AppGW", "style": "link" }, { "id": "9254cf7e-d064-4e4e-8990-1db356b06016", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "ExpressRoute Circuits", "subTarget": "ERCircuits", "style": "link" }, { "id": "8eaf294f-901d-4c3e-afae-9a1faa4996e1", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "ExpressRoute Gateway", "subTarget": "ERGW", "style": "link" }, { "id": "407bc7a5-ffc5-4946-9599-a77dc33a1dd9", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Firewall", "subTarget": "Firewall", "style": "link" }, { "id": "96284547-fe59-4fc4-8aa9-10d4e4dbaf18", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Front Door", "subTarget": "FrontDoor", "style": "link" }, { "id": "225e2b33-3b9a-4ac3-9a02-8f8393cb55a2", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Load Balancer", "subTarget": "LB", "style": "link" }, { "id": "59a56699-c31d-41c6-9fd6-6eeb4ca99974", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Network Security Group", "subTarget": "NSG", "style": "link" }, { "id": "074d9a99-249d-4282-af5d-7d4841bd6c5a", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Network Watcher", "subTarget": "NWW", "style": "link" }, { "id": "37e34bb1-fa9c-444a-ad7c-766aaf5943ef", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Private Endpoint", "subTarget": "PE", "style": "link" }, { "id": "45a51132-eb06-40b0-acae-0a4a2a8eecf3", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Public IP", "subTarget": "PublicIP", "style": "link" }, { "id": "b2a9720d-e244-4c12-b9f9-e0480638d84c", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Route Table", "subTarget": "UDR", "style": "link" }, { "id": "59cb909a-772a-42fc-8ff5-b74361d99e3b", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Traffic Manager", "subTarget": "TrafficManager", "style": "link" }, { "id": "be000935-2c31-4f5d-861e-c88ea1ce13f4", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Virtual Networks", "subTarget": "VNet", "style": "link" }, { "id": "7c95b7e6-e887-41cd-ad93-54e34f5191cd", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "VPN Gateway", "subTarget": "VPNGW", "style": "link" }, { "id": "8ee203ad-30c9-47bf-85f7-10dc00e1e0e1", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Web Application Firewall", "subTarget": "WAF", "style": "link" } ] }, "name": "Networking Tabs" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "agw-1", name, id, param1 = "autoScaleConfiguration: isNull or MinCapacity <= 1"\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "AGW-1 - Set a minimum instance count of 2", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AppGW" }, "name": "AGW-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "agw-3", name, id, param1 = "webApplicationFirewallConfiguration: isNull"\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "AGW-3 - Enable Web Application Firewall policies", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AppGW" }, "name": "AGW-3" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "agw-8", name, id, tags, param1 = strcat('AppGW subnet prefix: ', addressprefix)", "size": 0, "showAnalytics": true, "title": "AGW-9 - Ensure Application Gateway Subnet is using a /24 subnet mask", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AppGW" }, "name": "AGW-9" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Goal: Show any ExpressRoute circuit where one of the connections is not configured (i.e. no IP)\r\nResources\r\n| where type =~ 'Microsoft.Network/expressRouteCircuits'\r\n| where properties.value[0].provisioningState != 'Succeeded' or properties.value[1].provisioningState != 'Succeeded'\r\n| where not(properties.peerings[0].properties.primaryPeerAddressPrefix != "null" and properties.peerings[0].properties.secondaryPeerAddressPrefix != "null")\r\n| project recommendationId = "erc-1", name, id, tags, param1 = strcat("Peer1_IP: ",properties.peerings[0].properties.primaryPeerAddressPrefix), param2=strcat("Peer2_IP: ", properties.peerings[0].properties.secondaryPeerAddressPrefix)\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "ERC-3 - Ensure both connections of an ExpressRoute circuit are configured in active-active mode", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ERCircuits" }, "name": "ERC-3" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "ergw-1", name, id, tags, param1= strcat("sku-tier: " , properties.sku.tier), param2=location\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "ERGW-1 - Use Zone-redundant gateway SKUs", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ERGW" }, "name": "ERGW-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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| project recommendationId = "afw-1", name, id, param1="multipleZones:false"", "size": 0, "showAnalytics": true, "title": "AFW-1 - Deploy Azure Firewall across multiple availability zones", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "Firewall" }, "name": "AFW-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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\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\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 = "afw-3", name, id = firewallId, param1 = strcat("MetricsAlerts:", monitoredMetrics)", "size": 0, "showAnalytics": true, "title": "AFW-2 - Monitor Azure Firewall metrics", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "Firewall" }, "name": "AFW-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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| mv-expand properties.ipConfigurations\r\n| project name, firewallId = id, vNet = substring(properties_ipConfigurations.properties.subnet.id, 0, indexof(properties_ipConfigurations.properties.subnet, "/subnet") - 7)\r\n| join kind=fullouter (\r\n resources\r\n | where type == "microsoft.network/ddosprotectionplans"\r\n | mv-expand properties.virtualNetworks\r\n | extend vNet = tostring(properties_virtualNetworks.id)\r\n | project ddosProtectionPlan = id, vNet\r\n )\r\n on $left.vNet == $right.vNet\r\n| where ddosProtectionPlan == ''\r\n| project recommendationId = "afw-5", name, id = firewallId, param1 = "ddosProtectionPlan:false"", "size": 0, "showAnalytics": true, "title": "AFW-3 - Configure DDoS Protection on the Azure Firewall VNet", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "Firewall" }, "name": "AFW-3" }, { "type": 1, "content": { "json": "Under-Development", "style": "info" }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "FrontDoor" }, "name": "Front Door Info" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "lb-1", name, id, Param1=strcat("sku-tier: basic")", "size": 0, "showAnalytics": true, "title": "LB-1 - Use Standard Load Balancer SKU", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "LB" }, "name": "LB-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "lb-2", name, id, Param1=BackEndPools, Param2=0\r\n| union (resources\r\n | where type =~ 'Microsoft.Network/loadBalancers'\r\n | extend bep = properties.backendAddressPools\r\n | extend BackEndPools = array_length(bep)\r\n | mv-expand bip = properties.backendAddressPools\r\n | extend BackendAddresses = array_length(bip.properties.loadBalancerBackendAddresses)\r\n | where BackendAddresses <= 1\r\n | project recommendationId = "lb-2", name, id, Param1=BackEndPools, Param2=BackendAddresses)", "size": 0, "showAnalytics": true, "title": "LB-2 - Ensure the Backend Pool contains at least two instances", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "LB" }, "name": "LB-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "lb-3", name, id, Param1 = "outboundRules: >=1"", "size": 0, "showAnalytics": true, "title": "LB-3 - Use NAT Gateway instead of Outbound Rules for Production Workloads", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "LB" }, "name": "LB-3" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "lb-4", name, id, param1="Zones: No Zone or Zonal", param2=strcat("Frontend IP Configuration:", " ", feConfigName)", "size": 0, "showAnalytics": true, "title": "LB-4 - Ensure Standard Load Balancer is zone-redundant", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "LB" }, "name": "LB-4" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "nsg-2", name, id, tags, param1 = "ModificationAlert: Not configured/Disabled"", "size": 0, "showAnalytics": true, "title": "NSG-2 - Monitor changes in Network Security Groups with Azure Monitor", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "NSG" }, "name": "NSG-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "nsg-4", name, id, tags, param1 = "NSGFlowLog: Not configured/Disabled"", "size": 0, "showAnalytics": true, "title": "NSG-4 - Configure NSG Flow Logs", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "NSG" }, "name": "NSG-4" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "nsg-5", name, id", "size": 0, "showAnalytics": true, "title": "NSG-5 - The NSG only has Default Security Rules, make sure to configure the necessary rules", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "NSG" }, "name": "NSG-5" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "nw-1", name=location, id="n/a", param1 = strcat("LocationMisingNetworkWatcher:", location)", "size": 0, "showAnalytics": true, "title": "NW-1 - Deploy Network Watcher in all regions where you have networking services", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "NWW" }, "name": "NW-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "nw-2", name, id, param1 = strcat("provisioningState:", provisioningState), param2=strcat("Status:", status), param3=strcat("targetResourceId:",targetResourceId), param4=strcat("flowLogType:",flowLogType)", "size": 0, "showAnalytics": true, "title": "NW-2 - Fix Flow Log configurations in Failed state or Disabled Status", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "NWW" }, "name": "NW-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "pep-1", name, id, param1 = strcat("provisioningState: ", tostring(properties.provisioningState)), param2 = strcat("provisioningState: ", tostring(properties.privateLinkServiceConnections[0].properties.provisioningState))", "size": 0, "showAnalytics": true, "title": "PEP-1 - Resolve issues with Private Endpoints in non Succeeded connection state", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "PE" }, "name": "PEP-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="pip-2", name, id, param1=strcat("Migrate from instance IP to NAT Gateway")", "size": 0, "showAnalytics": true, "title": "PIP-2 - Use NAT gateway for outbound connectivity to avoid SNAT Exhaustion", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "PublicIP" }, "name": "PIP-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "rt-1", name, id, tags, param1 = "ModificationAlert: Not configured/Disabled"", "size": 0, "showAnalytics": true, "title": "RT-1 - Monitor changes in Route Tables with Azure Monitor", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "UDR" }, "name": "RT-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "Resources\r\n| where type == 'microsoft.network/trafficmanagerprofiles'\r\n| extend endpoints = properties.endpoints\r\n| mv-expand endpoint = endpoints\r\n| extend endpointName = endpoint.name\r\n| extend endpointLocation = endpoint.properties.endpointLocation\r\n| extend ttl = toint(properties.dnsConfig.ttl)\r\n| project recommendationId="traf-5", name, id, endpointName, properties.trafficRoutingMethod, endpointLocation,ttl,GeoMapping = tostring(endpoint.properties.geoMapping)", "size": 0, "showAnalytics": true, "title": "TRAF-5 - Ensure endpoint configured to “(All World)” for geographic profiles", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "TrafficManager" }, "name": "TRAF-5" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vnet-1", name, id, tags, param1 = strcat("SubnetName: ", subnets.name), param2 = "NSG: False"", "size": 0, "showAnalytics": true, "title": "VNET-1 - All Subnets should have a Network Security Group associated", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VNet" }, "name": "VNET-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vnet-2", name, id, tags, param1 = strcat("EnableDdosProtection: ", properties.enableDdosProtection)", "size": 0, "showAnalytics": true, "title": "VNET-2 - Use Azure DDoS Standard Protection Plans to protect all public endpoints hosted within customer Virtual Networks", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VNet" }, "name": "VNET-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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, subnets, serviceEndpoints=todynamic(subnets.properties.serviceEndpoints)\r\n| mv-expand serviceEndpoints\r\n| project name, id, 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 = "vnet-3", name, id, param1 = strcat("subnet=", subnetName), param2=strcat("serviceName=",serviceName), param3="ServiceEndpoints=true"", "size": 0, "showAnalytics": true, "title": "VNET-3 - When available, use Private Endpoints instead of Service Endpoints for PaaS Services", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VNet" }, "name": "VNET-3" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "vpng-1", name, id, param1= strcat("sku-tier: " , properties.sku.tier), param2=location\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "VPNG-1 - Choose a Zone-redundant gateway", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VPNGW" }, "name": "VPNG-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "resources\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 = "vpng-2", name, id", "size": 0, "showAnalytics": true, "title": "VPNG-2 - Plan for Active-Active mode", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "VPNGW" }, "name": "VPNG-2" }, { "type": 1, "content": { "json": "Under-Development", "style": "info" }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "WAF" }, "name": "WAF Info" } ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "Networking" }, "name": "Networking" }, { "type": 12, "content": { "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "links": [ { "id": "7a3208fd-fe77-4797-9abc-4697488fedfd", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Key Vault", "subTarget": "KV", "style": "link" } ] }, "name": "Security" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "kv-1", name, id, tags, param1 = "EnableSoftDelete: Disabled"", "size": 0, "showAnalytics": true, "title": "KV-1 - Key vaults should have soft delete enabled", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "KV" }, "name": "KV-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "kv-2", name, id, tags, param1 = "EnablePurgeProtection: Disabled"", "size": 0, "showAnalytics": true, "title": "KV-2 - Key vaults should have purge protection enabled", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "KV" }, "name": "KV-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "kv-3", name, id, param1, param2", "size": 0, "showAnalytics": true, "title": "KV-3 - Enable Azure Private Link Service for Key vault", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "KV" }, "name": "KV-3" } ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "Security" }, "name": "Security" }, { "type": 12, "content": { "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "links": [ { "id": "b4c0d4ff-e887-4fd2-baa2-c02474cc3541", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Azure Virtual Desktop", "subTarget": "AVD", "style": "link" }, { "id": "878de4c3-ebcd-4b7d-8cd2-5921bb6deb45", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Azure VMware Solution", "subTarget": "AVS", "style": "link" } ] }, "name": "Security - コピー" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Provides a list of Host Pools that do not have a Private endpoint associated with them.\r\nresources\r\n| where type == "microsoft.desktopvirtualization/hostpools"\r\n| where properties['privateEndpointConnections'][0]['properties']['provisioningState'] != "Succeeded"\r\n| project recommendationId = "avd-1", name, id, tags, param1="Private Endpoint: No Endpoint"", "size": 0, "showAnalytics": true, "title": "AVD-1 - Use Private link when connecting to File Share or Key Vault", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AVD" }, "name": "AVD-1" }, { "type": 1, "content": { "json": "" }, "name": "テキスト - 2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = 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 = 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 = "avs-3", 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 = 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 = 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 = "avs-3", name, id, tags, param1 = "vsanCapacityWarningAlert: isNull or threshold != 70"\r\n)", "size": 0, "showAnalytics": true, "title": "AVS-3 - Configure Azure Monitor Alert warning thresholds for vSAN datastore utilization", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AVS" }, "name": "AVS-3" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "avs-4", name, id, tags, param1 = "stretchClusters: Disabled"", "size": 0, "showAnalytics": true, "title": "AVS-4 - Enable Stretched Clusters for Multi-AZ Availability of the vSAN Datastore", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AVS" }, "name": "AVS-4" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = 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 = 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 = "avs-5", name, id, tags, param1 = "hostCpuCriticalAlert: isNull or threshold != 95"", "size": 0, "showAnalytics": true, "title": "AVS-5 - Monitor CPU Utilization to ensure sufficient resources for workloads", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AVS" }, "name": "AVS-5" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = 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 = 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 = "avs-6", name, id, tags, param1 = "hostMemoryCriticalAlert: isNull or threshold != 95"", "size": 0, "showAnalytics": true, "title": "AVS-6 - Monitor Memory Utilization to ensure sufficient resources for workloads", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AVS" }, "name": "AVS-6" } ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "SpecializedWorkloads" }, "name": "Specialized Workloads" }, { "type": 12, "content": { "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "links": [ { "id": "831d54be-136e-4704-81a6-ecb9c38df5c9", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Azure Netapp Files", "subTarget": "ANF", "style": "link" }, { "id": "6eca9dee-9396-44de-8588-0c5671755c52", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Storage Account", "subTarget": "StorageAccount", "style": "link" } ] }, "name": "Storage Tabs" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// This Resource Graph query will return all NetApp Volumes without Network Feature Standard.\r\nresources\r\n| where type =~ "microsoft.netapp/netappaccounts/capacitypools/volumes"\r\n| where properties.networkFeatures != "Standard"\r\n| project recommendationId = "ANF-2", name, id, tags", "size": 0, "showAnalytics": true, "title": "ANF-2 - Use standard network feature for Production in Azure NetApp Files", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ANF" }, "name": "ANF-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// This Resource Graph query will return all NetApp Volumes without AVzone defined.\r\nresources\r\n| where type =~ "microsoft.netapp/netappaccounts/capacitypools/volumes"\r\n| where zones == "[]"\r\n| project recommendationId = "ANF-3", name, id, tags", "size": 0, "showAnalytics": true, "title": "ANF-3 - Use availability zones for high availability in Azure NetApp Files", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ANF" }, "name": "ANF-3" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// This Resource Graph query will return all NetApp Volumes without Cross-Region Replication.\r\nresources\r\n| where type =~ "microsoft.netapp/netappaccounts/capacitypools/volumes"\r\n| extend NetAC0 = tostring(split(name,'/')[0])\r\n| join kind=leftouter (resources\r\n | where type =~ "microsoft.netapp/netappaccounts/capacitypools/volumes"\r\n | extend NetAC1 = tostring(split(name,'/')[0])\r\n | project id,NetAC1,remid=tostring(properties.dataProtection.replication.remoteVolumeResourceId)) on $left.id == $right.remid\r\n| where properties.volumeType != 'DataProtection' and NetAC0 == NetAC1\r\n| project recommendationId = "ANF-5", name, id, tags", "size": 0, "showAnalytics": true, "title": "ANF-5 - Enable Cross-region replication of Azure NetApp Files volumes", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ANF" }, "name": "ANF-5" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// This Resource Graph query will return all NetApp Volumes without Cross-Zone Replication.\r\nresources\r\n| where type =~ "microsoft.netapp/netappaccounts/capacitypools/volumes"\r\n| extend NetAC0 = tostring(split(name,'/')[0])\r\n| join kind=leftouter (resources\r\n | where type =~ "microsoft.netapp/netappaccounts/capacitypools/volumes"\r\n | extend NetAC1 = tostring(split(name,'/')[0])\r\n | project id,NetAC1,remid=tostring(properties.dataProtection.replication.remoteVolumeResourceId)) on $left.id == $right.remid\r\n| where properties.volumeType != 'DataProtection' and NetAC0 != NetAC1\r\n| project recommendationId = "ANF-6", name, id, tags", "size": 0, "showAnalytics": true, "title": "ANF-6 - Enable Cross-zone replication of Azure NetApp Files volumes", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "ANF" }, "name": "ANF-6" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// This query will return all storage accounts that are not using at least Zone replication\r\nResources\r\n| where type ='microsoft.storage/storageaccounts'\r\n| where sku.name ='Standard_LRS'\r\n| project recommendationId = 'st-1', name, id, param1=sku.name", "size": 0, "showAnalytics": true, "title": "ST-1 - Ensure that Storage Account configuration is at least Zone redundant", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "StorageAccount" }, "name": "ST-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = 'st-2', name, id, param1=type", "size": 0, "showAnalytics": true, "title": "ST-2 - Do not use classic Storage Account", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "StorageAccount" }, "name": "ST-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Find all Azure Storage Accounts, that do not have an access tier set\r\nresources\r\n| where type ='microsoft.storage/storageaccounts'\r\n| where isnull(properties.accessTier)\r\n| project recommendationId = 'st-3', name, id, param1="not defined - GeneralPurpose V1"", "size": 0, "showAnalytics": true, "title": "ST-3 - Ensure Performance tier is set as per workload", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "StorageAccount" }, "name": "ST-3" } ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "Storage" }, "name": "Storage" }, { "type": 12, "content": { "version": "NotebookGroup/1.0", "groupType": "editable", "items": [ { "type": 11, "content": { "version": "LinkItem/1.0", "style": "tabs", "links": [ { "id": "c3c3c83a-9ea2-4877-8e33-bbbe6e66b101", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "App Service Plan", "subTarget": "AppServicePlan", "style": "link" }, { "id": "386f0075-7a39-4528-bbcb-373cd09d4fcb", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "SignalR", "subTarget": "SignalR", "style": "link" }, { "id": "bc6b1e82-ca67-4312-bd22-150721a9df20", "cellValue": "selectedService", "linkTarget": "parameter", "linkLabel": "Web App", "subTarget": "WebApp", "style": "link" } ] }, "name": "Web Tabs" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="asp-1", name, id, sku_tier, zoneRedundant", "size": 0, "showAnalytics": true, "title": "ASP-1 - Migrate App Service to availability Zone Support", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AppServicePlan" }, "name": "ASP-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="asp-2", name, id, sku_tier", "size": 0, "showAnalytics": true, "title": "ASP-2 - Use Standard or Premium tier", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AppServicePlan" }, "name": "ASP-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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="asp-3", ["id"]=targetResourceId, resources_Name, ['changedProperties_sku.name_previousValue'], ['changedProperties_sku.tier_newValue'], count_", "size": 0, "showAnalytics": true, "title": "ASP-3 - Avoid scaling up or down", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AppServicePlan" }, "name": "ASP-3" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n//// Provides a list of Azure App Service Plans that are in the “PremiumV2”, “PremiumV3”, “Premium0V3”, “PremiumMV3”, or “Standard” tier, and checks if they have Elastic Scale or Autoscale enabled.\r\n\r\n resources\r\n | where type =~ 'microsoft.web/serverfarms'\r\n | extend tier = sku.tier, elasticScaleEnabled = coalesce(properties.elasticScaleEnabled, false)\r\n | where tier in ('PremiumV2', 'PremiumV3', 'Premium0V3', 'PremiumMV3', 'Standard')\r\n | extend id = tostring(id)\r\n | project id, name, tier, ['Elastic Scale'] = iff(elasticScaleEnabled, 'Enabled', 'Disabled')\r\n | join kind=leftouter (\r\n resources\r\n | where type =~ 'microsoft.insights/autoscalesettings'\r\n | extend autoscaleEnabled = coalesce(properties.enabled, false), metricResourceUri = tostring(properties.profiles[0].rules[0].metricTrigger.metricResourceUri)\r\n | project autoscaleEnabled, metricResourceUri\r\n ) on $left.id == $right.metricResourceUri\r\n | project recommendationid="asp-5",name, id, ['Tier'] = tier, ['Elastic Scale'] = ['Elastic Scale'], ['Autoscale'] = iff(isnull(autoscaleEnabled), 'Disabled', iff(autoscaleEnabled, 'Enabled', 'Disabled'))", "size": 0, "showAnalytics": true, "title": "ASP-5 - Enable Autoscale/Automatic scaling to ensure adequate resources are available to service requests", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "AppServicePlan" }, "name": "ASP-5" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 = "sigr-1", name, id, param1 = "AvailabilityZones: Single Zone"\r\n| order by id asc", "size": 0, "showAnalytics": true, "title": "SIGR-1 - Enable zone redundancy for SignalR", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "SignalR" }, "name": "SIGR-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n// Provides a list of Azure App Service resources and checks if they have Diagnostic logs Categories turned on or now, list settings with on or off indicator\r\n\r\nappserviceresources\r\n| where ['type'] == "microsoft.web/sites/config"\r\n| where not(name contains "/slots/")\r\n| project id, properties.AzureMonitorLogCategories, name\r\n| extend AzureMonitorLogCategories = iif(isempty(properties_AzureMonitorLogCategories), false, true)\r\n, AppServiceHTTPLogs = iif(set_has_element(properties_AzureMonitorLogCategories, "AppServiceHTTPLogs"), 1, 0)\r\n, AppServiceConsoleLogs = iif(set_has_element(properties_AzureMonitorLogCategories, "AppServiceConsoleLogs"), 1, 0)\r\n, AppServiceAppLogs = iif(set_has_element(properties_AzureMonitorLogCategories, "AppServiceAppLogs"), 1, 0)\r\n, AppServiceFileAuditLogs = iif(set_has_element(properties_AzureMonitorLogCategories, "AppServiceFileAuditLogs"), 1, 0)\r\n, AppServiceAuditLogs = iif(set_has_element(properties_AzureMonitorLogCategories, "AppServiceAuditLogs"), 1, 0)\r\n, AppServiceIPSecAuditLogs = iif(set_has_element(properties_AzureMonitorLogCategories, "AppServiceIPSecAuditLogs"), 1, 0)\r\n, AppServicePlatformLogs = iif(set_has_element(properties_AzureMonitorLogCategories, "AppServicePlatformLogs"), 1, 0)\r\n, AppServiceAntivirusScanAuditLogs = iif(set_has_element(properties_AzureMonitorLogCategories, "AppServiceAntivirusScanAuditLogs"), 1, 0)\r\n| project recommendationId="app-1" , ['id'], ["Azure Monitor Logs"]= AzureMonitorLogCategories , AppServiceAntivirusScanAuditLogs, AppServiceAppLogs, AppServiceAuditLogs, AppServiceConsoleLogs, AppServiceFileAuditLogs, AppServiceHTTPLogs, AppServiceIPSecAuditLogs, AppServicePlatformLogs", "size": 0, "showAnalytics": true, "title": "APP-1 - Enable diagnostics logging", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "WebApp" }, "name": "APP-1" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "\r\n// Azure Resource Graph Query\r\n// Provides a list of Azure App Service resources and checks if they have Application Insights enabled by looking for the “APPINSIGHTS_INSTRUMENTATIONKEY” or “APPLICATIONINSIGHTS_CONNECTION_STRING” settings in their configuration.\r\n\r\nappserviceresources\r\n| where type == "microsoft.web/sites/config"\r\n| extend appSettings = properties.AppSettings\r\n| mv-expand appSettings\r\n| extend settingName = tostring(appSettings.Name)\r\n| extend isAppInsightsInstrumentationKey = iif(settingName == "APPINSIGHTS_INSTRUMENTATIONKEY", true, false)\r\n| extend isApplicationInsightsConnectionString = iif(settingName == "APPLICATIONINSIGHTS_CONNECTION_STRING", true, false)\r\n| extend isAppInsightsEnabled = iif(isAppInsightsInstrumentationKey or isApplicationInsightsConnectionString, true, false)\r\n| project recommendationId="app-2", name, id, settingName, isAppInsightsInstrumentationKey, isApplicationInsightsConnectionString, isAppInsightsEnabled", "size": 0, "showAnalytics": true, "title": "APP-2 - Monitor Performance", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "WebApp" }, "name": "APP-2" }, { "type": 3, "content": { "version": "KqlItem/1.0", "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 recommendationid = "app-5"\r\n| extend DeploymentSlotEnabled = iff(Slots > 1, true, false)\r\n| project recommendationid, name, id, Sku, Slots, DeploymentSlotEnabled", "size": 0, "showAnalytics": true, "title": "APP-5 - Deploy to a staging slot", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "WebApp" }, "name": "APP-5" }, { "type": 3, "content": { "version": "KqlItem/1.0", "query": "// Azure Resource Graph Query\r\n//Provides a list of Azure App Service resources and checks if they is any App Setting configured under this App\r\n\r\nappserviceresources\r\n| where ['type'] == "microsoft.web/sites/config"\r\n| project recommendationId="app-5", id, name, AppSettings = iif(isempty(properties.AppSettings), 0, 1) , properties", "size": 0, "showAnalytics": true, "title": "APP-6 - Store configuration as app settings", "noDataMessageStyle": 3, "showExportToExcel": true, "queryType": 1, "resourceType": "microsoft.resourcegraph/resources", "crossComponentResources": [ "{Subscriptions}" ] }, "conditionalVisibility": { "parameterName": "selectedService", "comparison": "isEqualTo", "value": "WebApp" }, "name": "APP-6" } ] }, "conditionalVisibility": { "parameterName": "selectedCategory", "comparison": "isEqualTo", "value": "Web" }, "name": "Web" } ], "fallbackResourceIds": [ "Azure Monitor" ], "$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json" }