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:
- Clone the repository to your workstation.
- CD into AzNonComplianceReport
- 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:
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