AzNonComplianceReport.ps1: A PowerShell Script to Output a CSV of Non-compliant Resources From Azure Policy

AzNonComplianceReport.ps1: A PowerShell Script to Output a CSV of Non-compliant Resources From Azure Policy

In Azure, you often have to deal with non-compliant resources and their associated details. You might think Microsoft would provide a native export tool to do this through the Azure portal. However, there is no direct way to export a list of non-compliant resources for a specific tenant, subscription, or policy assignment. That’s why I created the AzNonComplianceReport PowerShell script to generate a list of non-compliant resources that need remediation.

How to Use

The AzNonComplianceReport is simple to use:

  1. Clone the repository to your workstation.
  2. CD into AzNonComplianceReport
  3. Run AzNonComplianceReport.ps1.
git clone https://github.com/RCFromCLE/azure-powershell-csv/tree/main/AzNonComplianceReport

cd .\AzNonComplianceReport\

.\AzNonComplianceReport.ps1

Once you have started the script, it will prompt you with a series of questions:

  • “Do you want to get all noncompliant resources for your tenant? (Y/N)”
  • “Before we begin, do you need to authenticate to Azure? (Y/N)”
  • “Please select the management group for the noncompliance report:”
  • “Please select the subscription for the noncompliance report:”

For each prompt, answer accordingly based on your requirements. Once you’ve answered all the prompts, the script will check the specified management group or subscription for non-compliant resources and output a CSV report with a list of non-compliant resources.

See the GIF below for a quick demonstration of how to use it:

gif of AzNonCompliance running

Why this is Helpful

Having a list of non-compliant resources can be very helpful. You can use it to:

  • Feed resources into another script for remediation
  • Put together a report of resources and contacts for remediation
  • See a cumulative list of all non-compliant resources for an entire tenant

Diving into the Code

The Duplicate Problem and its Solution

The script navigates through your Azure environment at different levels – management groups and subscriptions. Occasionally, the same resource may be found more than once, resulting in duplicates in the final report. Our script addresses this with a clever use of a hashtable. Here’s how it works:

# Hashtable to track duplicate resources
$duplicateResourcesTable = @{}

# Update the hashtable with duplicate resources
$resources | ForEach-Object {
    $resourceId = $_.ResourceId
    if ($duplicateResourcesTable.ContainsKey($resourceId)) {
        $duplicateResourcesTable[$resourceId]++
    }
    else {
        $duplicateResourcesTable[$resourceId] = 1
    }
}

In this part of the code, I created a hashtable that acts like a counter. It checks if a resource already exists (is a duplicate) and increments the count for that resource. So, even if a resource appears multiple times, it gets represented only once in our final report.

The Use of Functions

To keep things tidy and easy to follow, the script is divided into smaller chunks or functions. Each function is like a mini-script with a specific task. Let’s look at the PromptForAuth function as an example:

function PromptForAuth {
    # Check if already authenticated
    if ((Get-AzContext).Account -eq $null) {
        # Command to authenticate to Azure
        Connect-AzAccount
    }
}

This function checks if you’re already authenticated to Azure. If not, it prompts you to log in. It’s a small but integral part of the overall script.

Why I Used PSCustomObject

Sometimes, you need to tailor the data to fit your specific needs. For this, PowerShell offers a feature called PSCustomObject. It’s a way of creating your unique data ‘containers’. For example, in the code below, I created a PSCustomObject to store details about non-compliant resources:

# Create a custom object with the modified properties
[PSCustomObject]@{
    SubscriptionId       = $_.SubscriptionId
    PolicyAssignmentName = $_.PolicyAssignmentName
    PolicyDefinitionName = $policyDefinition.Properties.DisplayName
    ResourceGroup        = $_.ResourceGroup
    ResourceType         = $_.ResourceType
    ResourceId           = $modifiedResourceId
    ResourceName         = $resourceName
}

Here, I’m creating a custom object for each non-compliant resource, with properties like SubscriptionId, PolicyAssignmentName, etc. This way, we can easily manipulate or access any specific property of a resource in the future.

Future Improvements

  • The addition of the subscription name field would be beneficial for better visibility
  • Using management group name and subscription name in the console output instead of ID for better readability
  • Giving a description of duplicates and how they impact the final count of non-compliant resources
  • Better error handling is being considered for more robustness
  • SQL integration is not currently supported, but it is a planned feature for a future version of the script

Thanks for reading this far. Feel free to reach out to me with any feedback :).

-Rudy

Relevant Links:

https://github.com/RCFromCLE/azure-powershell-csv
https://learn.microsoft.com/en-us/azure/governance/policy/how-to/get-compliance-data
https://feedback.azure.com/d365community/idea/a3f63bb8-f324-ec11-b6e6-000d3a4f0da0


Leave a Reply