Quickstart: Create a policy assignment to identify non-compliant resources by using a Bicep file

In this quickstart, you use a Bicep file to create a policy assignment that validates resource's compliance with an Azure policy. The policy is assigned to a resource group and audits virtual machines that don't use managed disks. After you create the policy assignment, you identify non-compliant virtual machines.

Bicep is a domain-specific language (DSL) that uses declarative syntax to deploy Azure resources. It provides concise syntax, reliable type safety, and support for code reuse. Bicep offers the best authoring experience for your infrastructure-as-code solutions in Azure.

Prerequisites

Review the Bicep file

The Bicep file creates a policy assignment for a resource group scope and assigns the built-in policy definition Audit VMs that do not use managed disks.

Create the following Bicep file as policy-assignment.bicep.

  1. Open Visual Studio Code and select File > New Text File.
  2. Copy and paste the Bicep file into Visual Studio Code.
  3. Select File > Save and use the filename policy-assignment.bicep.
param policyAssignmentName string = 'audit-vm-managed-disks'
param policyDefinitionID string = '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d'
param policyDisplayName string = 'Audit VM managed disks'

resource assignment 'Microsoft.Authorization/policyAssignments@2023-04-01' = {
  name: policyAssignmentName
  scope: resourceGroup()
  properties: {
    policyDefinitionId: policyDefinitionID
    description: 'Policy assignment to resource group scope created with Bicep file'
    displayName: policyDisplayName
    nonComplianceMessages: [
      {
        message: 'Virtual machines should use managed disks'
      }
    ]
  }
}

output assignmentId string = assignment.id

The resource type defined in the Bicep file is Microsoft.Authorization/policyAssignments.

The Bicep file uses three parameters to deploy the policy assignment:

  • policyAssignmentName creates the policy assignment named audit-vm-managed-disks.
  • policyDefinitionID uses the ID of the built-in policy definition. For reference, the commands to get the ID are in the section to deploy the template.
  • policyDisplayName creates a display name that's visible in Azure portal.

For more information about Bicep files:

Deploy the Bicep file

You can deploy the Bicep file with Azure PowerShell or Azure CLI.

From a Visual Studio Code terminal session, connect to Azure. If you have more than one subscription, run the commands to set context to your subscription. Replace <subscriptionID> with your Azure subscription ID.

Connect-AzAccount

# Run these commands if you have multiple subscriptions
Get-AzSubScription
Set-AzContext -Subscription <subscriptionID>

You can verify if Microsoft.PolicyInsights is registered. If it isn't, you can run a command to register the resource provider.

Get-AzResourceProvider -ProviderNamespace 'Microsoft.PolicyInsights' |
   Select-Object -Property ResourceTypes, RegistrationState

Register-AzResourceProvider -ProviderNamespace 'Microsoft.PolicyInsights'

For more information, go to Get-AzResourceProvider and Register-AzResourceProvider.

The following commands display the policyDefinitionID parameter's value:

(Get-AzPolicyDefinition |
  Where-Object { $_.Properties.DisplayName -eq 'Audit VMs that do not use managed disks' }).ResourceId

The following commands deploy the policy definition to your resource group. Replace <resourceGroupName> with your resource group name:

$rg = Get-AzResourceGroup -Name '<resourceGroupName>'

$deployparms = @{
Name = 'PolicyDeployment'
ResourceGroupName = $rg.ResourceGroupName
TemplateFile = 'policy-assignment.bicep'
}

New-AzResourceGroupDeployment @deployparms

The $rg variable stores properties for the resource group. The $deployparms variable uses splatting to create parameter values and improve readability. The New-AzResourceGroupDeployment command uses the parameter values defined in the $deployparms variable.

  • Name is the deployment name displayed in the output and in Azure for the resource group's deployments.
  • ResourceGroupName uses the $rg.ResourceGroupName property to get the name of your resource group where the policy is assigned.
  • TemplateFile specifies the Bicep file's name and location on your local computer.

You can verify the policy assignment's deployment with the following command:

The command uses the $rg.ResourceId property to get the resource group's ID.

Get-AzPolicyAssignment -Name 'audit-vm-managed-disks' -Scope $rg.ResourceId
Name               : audit-vm-managed-disks
ResourceId         : /subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Authorization/policyAssignments/audit-vm-managed-disks
ResourceName       : audit-vm-managed-disks
ResourceGroupName  : {resourceGroupName}
ResourceType       : Microsoft.Authorization/policyAssignments
SubscriptionId     : {subscriptionId}
PolicyAssignmentId : /subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Authorization/policyAssignments/audit-vm-managed-disks
Properties         : Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.Policy.PsPolicyAssignmentProperties

For more information, go to Get-AzPolicyAssignment.

Identify non-compliant resources

After the policy assignment is deployed, virtual machines that are deployed to the resource group are audited for compliance with the managed disk policy.

The compliance state for a new policy assignment takes a few minutes to become active and provide results about the policy's state.

$complianceparms = @{
ResourceGroupName = $rg.ResourceGroupName
PolicyAssignmentName = 'audit-vm-managed-disks'
Filter = 'IsCompliant eq false'
}

Get-AzPolicyState @complianceparms

The $complianceparms variable creates parameter values used in the Get-AzPolicyState command.

  • ResourceGroupName gets the resource group name from the $rg.ResourceGroupName property.
  • PolicyAssignmentName specifies the name used when the policy assignment was created.
  • Filter uses an expression to find resources that aren't compliant with the policy assignment.

Your results resemble the following example and ComplianceState shows NonCompliant:

Timestamp                : 2/20/2024 18:55:45
ResourceId               : /subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/microsoft.compute/virtualmachines/{vmId}
PolicyAssignmentId       : /subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/microsoft.authorization/policyassignments/audit-vm-managed-disks
PolicyDefinitionId       : /providers/microsoft.authorization/policydefinitions/06a78e20-9358-41c9-923c-fb736d382a4d
IsCompliant              : False
SubscriptionId           : {subscriptionId}
ResourceType             : Microsoft.Compute/virtualMachines
ResourceLocation         : {location}
ResourceGroup            : {resourceGroupName}
ResourceTags             : tbd
PolicyAssignmentName     : audit-vm-managed-disks
PolicyAssignmentOwner    : tbd
PolicyAssignmentScope    : /subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}
PolicyDefinitionName     : 06a78e20-9358-41c9-923c-fb736d382a4d
PolicyDefinitionAction   : audit
PolicyDefinitionCategory : tbd
ManagementGroupIds       : {managementGroupId}
ComplianceState          : NonCompliant
AdditionalProperties     : {[complianceReasonCode, ]}

For more information, go to Get-AzPolicyState.

Clean up resources

Remove-AzPolicyAssignment -Name 'audit-vm-managed-disks' -Scope $rg.ResourceId

To sign out of your Azure PowerShell session:

Disconnect-AzAccount

Next steps

In this quickstart, you assigned a policy definition to identify non-compliant resources in your Azure environment.

To learn more about how to assign policies that validate resource compliance, continue to the tutorial.