The following KQL query can be used in Azure Resource Graph Explorer to identify alert rules that have an invalid scope:
resources
| where type =~ 'microsoft.insights/metricAlerts' or type =~ 'microsoft.insights/scheduledQueryRules'
| extend scopes = properties.scopes
| mv-expand scopes
| project name, resourceGroup, subscriptionId, location, properties, scopeId = toupper(tostring(scopes))
| join kind=leftouter (
resources
| project scopeId = toupper(id)
) on scopeId
// Only return alerts that have no matching resource
| where isempty(scopeId1)
| project-away scopeId1
Please note that the above query will also return alerts that are scoped to subscription level. This is because the right-hand join in the query can only return resources (subscriptions do not count as resources). If you need to filter out subscription level alert definitions, use the following modified version:
resources
| where type =~ 'microsoft.insights/metricAlerts' or type =~ 'microsoft.insights/scheduledQueryRules'
| extend scopes = properties.scopes
| mv-expand scopes
| project name, resourceGroup, subscriptionId, location, properties, scopeId = toupper(tostring(scopes))
| join kind=leftouter (
resources
| project scopeId = toupper(id)
) on scopeId
// Only return alerts that have no matching resource and are not scoped to a subscription
| where isempty(scopeId1) and strlen(scopeId) > 51
| project-away scopeId1