Problem:

In Azure Policy, there are many built-in initiative definitions. To jumpstart your adoption of Azure and to apply guard rails to your cloud environment, you may want to use one of these built-in initiatives. However, you may want to customize an initiative to meet your organization’s requirements or needs. This PowerShell solution will allow you duplicate a built-in initiative so you can customize which policy definitions are included in the initative.

Solution:

  1. Download the script.ps1 file from my Github repo.
  2. Open PowerShell.
  3. Ensure the AZ module is installed:
Get-Module -ListAvailable
  1. Change your working directory to the folder where you downloaded the script.
  2. Connect to Azure:
Connect-AzAccount
  1. Set the context to target the appropriate subscription. For example:
Set-AzContext -Subscription 'Visual Studio Enterprise Subscription'
  1. Find the name of the built-in initiative to duplicate:
(Get-AzPolicySetDefinition).Properties.DisplayName | Sort-Object
  1. Call the script using all the parameters. For example:
.\script.ps1 -Initiative 'NIST SP 800-53 R4' -NewName 'NIST SP 800-53 R4 Custom' -NewDisplayName 'NIST SP 800-53 R4 Custom'

Explanation:

This solution was created before the option to duplicate an initiative existed in the portal. Below I will explain the different sections of the code:

  1. Param block
Param(
    [parameter(Mandatory=$true)][string]$Initiative,
    [parameter(Mandatory=$true)][string]$NewName,
    [parameter(Mandatory=$true)][string]$NewDisplayName
)

The parameters listed below are used in calling the script:

  • Initiative: input the name of the desired initiative. The allowed names of built-in initiatives will be in the output from step 7 in the Solution section.
  • NewName: input the name of the new initiative that will be created.
  • NewDisplayName: input the display name of the new initiative that will be created.
  1. Collect data
# Get subscription ID
$Id = (Get-AzContext).Subscription.Id 

# Get the initiative's properties using the name
$Initiative = Get-AzPolicySetDefinition | Where-Object {$_.Properties.DisplayName -eq $Name}
  • Id: this variable collects your subscription ID which will be used later to create the new initiative definition.
  • Initiative: this variable collects all the details of the desired built-in initiative and used to build out the details of the new initiative.
  1. Create new initiative definition
# Create a custom initiative
New-AzPolicySetDefinition `
    -Name $NewName `
    -DisplayName $NewDisplayName `
    -Description $Initiative.Properties.Description `
    -PolicyDefinition $([System.Text.RegularExpressions.Regex]::Unescape($($Initiative.Properties.PolicyDefinitions | ConvertTo-Json -Depth 100))) `
    -Metadata $($Initiative.Properties.Metadata | ConvertTo-Json  -Depth 100)  `
    -Parameter $($Initiative.Properties.Parameters | ConvertTo-Json  -Depth 100)  `
    -SubscriptionId $Id `
    -GroupDefinition $($Initiative.Properties.PolicyDefinitionGroups | ConvertTo-Json  -Depth 100)

In this last chunk of code, the “New-AzPolicySetDefinition” cmdlet is used to duplicate a built-in definition. For the Policy Definition parameter, the Unescape method is used to escape single quotes that might exist in the JSON. In many of the parameter values, the ConverTo-Json cmdlet is used since the values are required to be in JSON format. Though the JSON depth is usually shallow in policy initiatives, I used 100 to prevent any issues in the conversion.