Search code examples
azureazure-powershellazure-automationazure-runbook

PS script to fetch the list non-compliance resource list with respect to policy from multiple subscriptions


I am trying to fetch a list of non-compliant resources via email for a particular policy across multiple subscriptions. However, I'm currently unable to achieve this. Here's what I've done so far:

  1. Set up an automation account.
  2. Created a PowerShell (PS) runbook and executed the PS script.
  3. Set up a Logic App to trigger an email with the non-compliant resource list.

The issue is that my current PS script only lists non-compliant resources from a single subscription. What modifications can be made to the PS script to fetch the non-compliant resource list across multiple subscriptions?

Connect-AzAccount -Identity

$policyDefinitionName = "policy-XXXXX"

$complianceState = Get-AzPolicyState -PolicyDefinitionName $policyDefinitionName

$nonCompliantResources = $complianceState | Where-Object { $_.ComplianceState -eq "NonCompliant" }

$csvString = $nonCompliantResources | Select-Object @{Name="SubscriptionName";Expression={(Get-AzSubscription -SubscriptionId $_.SubscriptionId).Name}}, 
                                        @{Name="ResourceGroup";Expression={($_.ResourceId -split '/')[4]}}, 
                                        @{Name="ResourceName";Expression={($_.ResourceId -split '/')[8]}}, 
                                        PolicyDefinitionName | ConvertTo-Csv -NoTypeInformation | Out-String

return $csvString

Solution

  • What modifications can be made to the PS script to fetch the non-compliant resource list across multiple subscriptions?

    There are two ways, If the same policy definition is assigned to two different subscriptions, we can filter for each subscription using foreach method in PowerShell, or you can assign the policy at the management group level. The policy will then apply to all subscriptions under the management group

    If it's assigned at the management group level, you can fetch using below cmdlet as suggested bywenbo follow the MS Doc for more details

    Note: Make sure to assign the required role at the management group level to the Automation account identity.

    Get-AzPolicyState -ManagementGroupName "xxxx" -PolicyDefinitionName $policyDefinitionName
    

    If assigned to a different subscription with the same policy definition name instead of the management group, you can use the approach below.

    
    # Get all subscriptions (you can filter this to a specific set of subscriptions if needed)
    $subscriptions = Get-AzSubscription
    
    
    $allNonCompliantResources = @()
    
    # Loop through each subscription
    foreach ($subscription in $subscriptions) {
        
        Set-AzContext -SubscriptionId $subscription.Id
    
        # Get the policy definitions with the specific name you're looking for
    
        $result = Get-AzPolicyDefinition | Where-Object { $_.DisplayName -like "*Allow resources only in North Europe*" }
    
        # Loop through each policy definition (in case there are multiple matching policies)
        foreach ($policy in $result) {
            # Get the policy state for the current subscription using the PolicyDefinition ID
            $complianceState = Get-AzPolicyState -PolicyDefinitionName $policy.Name
    
            # Filter for non-compliant resources and add them to the results array
            $nonCompliantResources = $complianceState | Where-Object { $_.ComplianceState -eq "NonCompliant" }
    
            $allNonCompliantResources += $nonCompliantResources
        }
    }
    
    # Now you have the list of non-compliant resources across all subscriptions
    $csvString = $allNonCompliantResources | Select-Object @{
                                                Name="SubscriptionName"; 
                                                Expression={(Get-AzSubscription -SubscriptionId $_.SubscriptionId).Name}}, 
                                            @{
                                                Name="ResourceGroup"; 
                                                Expression={($_.ResourceId -split '/')[4]}}, 
                                            @{
                                                Name="ResourceName"; 
                                                Expression={($_.ResourceId -split '/')[8]}}, 
                                            @{
                                                Name="PolicyDefinitionName";
                                                Expression={$_ | Select-Object -ExpandProperty Name}} | 
                                            ConvertTo-Csv -NoTypeInformation | Out-String
    
    # Return the CSV string (this can be used in the Logic App to send an email)
    return $csvString
    

    Output

    enter image description here

    Complaince result

    enter image description here