Get Azure Policy Compliance State with PowerShell
Tags: azure, powershell
Published: 2024-04-25 18:40:38

Working with Azure Policy means that you will have to deal with non-compliant resources. You can see them by going to an Azure Policy assignment and clicking on “View Compliance”, or simply by going to the “Compliance” page in Azure Policy. This is a manual process, what if we can automate it a little?
To deal with non-compliant resources we need to use the Get-azPolicyState. With no parameters, you will get all resources evaluated by any policy in the current subscription for the last day. You can get resources from up to the last 90 days by using -From and -To.

```powershell
$toToday = Get-Date
$90DaysBefore = $toToday.AddDays(-90)
Get-AzPolicyState -from $90DaysBefore -to $toToday
```
You can filter by subscription ID 
```powershell
Get-AzPolicyState -from $90DaysBefore -to $toToday -SubscriptionId XXXX-XXXX-XXX
```
By Policy assignment name 

```powershell
Get-AzPolicyState -from $90DaysBefore -to $toToday -PolicyAssignmentName SecurityCenterBuiltIn
```
But be careful, the -PolicyAssignmentName only works if the policy is assigned to a subscription, you will get no result if the assignment is at the management group level. 
Or by a Management Group 

```powershell
Get-AzPolicyState -from $90DaysBefore -to $toToday -ManagementGroupName rootomiossec
```
In this case, you will not be able to use the -PolicyAssignmentName parameter.
Now, imagine you need to get only non-compliant resources from a given type, ex you want to list only non-compliant storage accounts. That is where you will need to use the -filter parameter. This parameter uses OData notation to query the result from Get-AzPolicyState. 

```powershell
Get-AzPolicyState -Filter "ComplianceState eq 'NonCompliant' and ResourceType eq 'Microsoft.Storage/storageAccounts'"
```

With the -filter parameter you can even query the policy state by assignment name even if the assignment is at the management group scope. 

```powershell
Get-AzPolicyState -Filter "ComplianceState eq 'NonCompliant' and ResourceType eq 'Microsoft.Storage/storageAccounts' and PolicyAssignmentName eq 'ca4ce57bdb00420b8a34b759'"
```

You can restrict the output by using the -select parameter, it works like select-object cmdlet. The parameter accepts a list of fields you want to see in the result.

```powershell
Get-AzPolicyState -Filter "ComplianceState eq 'NonCompliant' and ResourceType eq 'Microsoft.Storage/storageAccounts' and PolicyAssignmentName eq 'ca4ce57bdb00420b8a34b759'" -Select "ResourceId, ResourceLocation, ResourceGroup"
```

You can also apply aggregation functions to the result you get. 
For example, you can get the number of affected resources 

```powershell
Get-AzPolicyState -Filter "ComplianceState eq 'NonCompliant' and ResourceType eq 'Microsoft.Storage/storageAccounts' and PolicyAssignmentName eq 'ca4ce57bdb00420b8a34b759'" -Apply "aggregate(`$count as NumberOfResource)"
```

You will have something like 
`AdditionalProperties : {[NumberOfResource, 16]}`
You can directly have the value. 

```powershell
(Get-AzPolicyState -Filter "ComplianceState eq 'NonCompliant' and ResourceType eq 'Microsoft.Storage/storageAccounts' and PolicyAssignmentName eq 'ca4ce57bdb00420b8a34b759'" -Apply "aggregate(`$count as NumberOfRecords)").AdditionalProperties.NumberOfResource
```

The -apply parameter uses the OData notation to form the query. You can group results to perform some more calculations.

```powershell
Get-AzPolicyState -Filter "ResourceType eq 'Microsoft.Storage/storageAccounts' " -Apply "groupby((PolicyAssignmentName, ResourceId))/groupby((PolicyAssignmentName), aggregate(`$count as NumNonCompliantResources))"
```

Here, the cmdlet groups Resource by PolicyAssignmentID and then counts each resource and puts the count result into the $count variable.

The Get-AzPolicyState, is less intuitive than other Azure cmdlets, you need to use OData to perform some calculations. Fortunately, you can still use where-object with -filterscript to obtain the same result if you are not familiar. Also, remember that you will get no result if you try to use the -assignmentName when the assignment is at the management group level.