functions/Export-AzAdvertizerPolicy.ps1

function Export-AzAdvertizerPolicy {
<#
    Exports Azure Policy from https://www.azadvertizer.net/ and builds EPAC templates for deploying PolicySets and Assignments.
 
    Mandatory url of the policy or policy set from AzAdvertizer.
 
    Output Folder. Defaults to the path 'Output'.
     
    Automatically create parameters for Azure Policy Sets and Assignment Files.
 
    Default to using builtin policies rather than local versions.
 
    Used to set scope value on each assignment file.
 
    Used to set PacEnvironment for each assignment file.
 
    Used to Overwrite the contents of the output folder with each run. Helpful when running consecutively.
 
    "./Out-AzAdvertizerPolicy.ps1" -AzAdvertizerUrl "https://www.azadvertizer.net/azpolicyinitiativesadvertizer/Deny-PublicPaaSEndpoints.html" -AutoCreateParameters $True -UseBuiltIn $True
    Retrieves Policy from AzAdvertizer, auto creates parameters to be manipulated in the assignment and sets assignment and policy set to use built-in policies rather than self hosted.
 
    "./Out-AzAdvertizerPolicy.ps1" -AzAdvertizerUrl "https://www.azadvertizer.net/azpolicyinitiativesadvertizer/Deny-PublicPaaSEndpoints.html" -PacSelector "EPAC-Prod" -Scope "/providers/Microsoft.Management/managementGroups/4fb849a3-3ff3-4362-af8e-45174cd753dd"
    Retrieves Policy from AzAdvertizer, sets the PacSelector in the assignment files to "EPAC-Prod" and the scope to the management group path provided.
 
    https://azure.github.io/enterprise-azure-policy-as-code/policy-exemptions/
#>


[CmdletBinding()]
param (
    [Parameter(Mandatory = $true, HelpMessage = "Mandatory url of the policy or policy set from AzAdvertizer")]
    [string] $AzAdvertizerUrl,

    [Parameter(Mandatory = $false, HelpMessage = "Output Folder. Defaults to the path 'Output'")]
    [string] $OutputFolder,

    [Parameter(Mandatory = $false, HelpMessage = "Automatically create parameters for Azure Policy Sets and Assignment Files")]
    [bool] $AutoCreateParameters = $true,

    [Parameter(Mandatory = $false, HelpMessage = "Default to using builtin policies rather than local versions")]
    [bool] $UseBuiltIn = $true,

    [Parameter(Mandatory = $false, HelpMessage = "Used to set scope value on each assignment file")]
    [string] $Scope,

    [Parameter(Mandatory = $false, HelpMessage = "Used to set PacEnvironment for each assignment file")]
    [string] $PacSelector,

    [Parameter(Mandatory = $false, HelpMessage = "Used to Overwrite the contents of the output folder with each run. Helpful when running consecutively")]
    [bool] $OverwriteOutput = $true
)

#region Policy

# Validate session with Azure exists
if (-not (Get-AzContext)) {
    $null = Connect-AzAccount
}

# Pull HTML
try {
    $restResponse = Invoke-RestMethod -Uri "$AzAdvertizerUrl" -Method Get
}
catch {
    if ($_.Exception.Response.StatusCode -lt 200 -or $_.Exception.Response.StatusCode -ge 300) {
        Write-Error "Error gathering Policy Information, please validate AzAdvertizerUrl is valid." -ErrorAction Stop
    }
}

# Determine if PolicySet or Policy Assignment
if ($AzAdvertizerUrl -match "azpolicyadvertizer") {
    $policyType = "policyDefinitions"
    # Pull Built-In Policies and Policy Sets
    $builtInPolicies = Get-AzPolicyDefinition -Builtin
    $builtInPolicyNames = $builtInPolicies.name
}
elseif ($AzAdvertizerUrl -match "azpolicyinitiativesadvertizer") {
    $policyType = "policySetDefinitions"
    # Pull Built-In Policies and Policy Sets
    $builtInPolicies = Get-AzPolicyDefinition -Builtin
    $builtInPolicyNames = $builtInPolicies.name
    $builtInPolicySets = Get-AzPolicySetDefinition -Builtin
    $builtInPolicySetNames = $builtInPolicySets.name
}
else {
    Write-Error "AzAdvertizerUrl is not referencing Policy or Policy Initiative"
}

# Parse HTML response to find copyEPACDef
Write-Information "" -InformationAction Continue
$response = $restResponse.split("function copyEPACDef() ")[1]

try {
    $response = $response.split("const obj = ")[1]
    if ($null -eq $response) {
        throw "Split resulted in null"
    }
}
catch {
    try {
        $response = $restResponse.split("function copyEPACDef() ")[1]
        $response = $response.split("const objEPAC = ")[1]
        if ($null -eq $response) {
            throw "Both splits resulted in null"
        }
    }
    catch {
        Write-Information "Both splits failed and resulted in null." -InformationAction Continue
    }
}

$response = $response.split("};")[0] + "}"

# Convert from JSON to get object
$policyObject = $response | ConvertFrom-Json

# Use Object to get policy name
$policyName = $policyObject.name

# Get Display Name
$policyDisplayName = $policyObject.properties.displayName

# Get Description
$policyDescription = $policyObject.properties.description

# Export policy definition or policy set definition
$policyJson = $policyObject | ConvertTo-Json -Depth 100

# Overwrite Output folder
if ($OutputFolder -eq "") {
    $OutputFolder = "Output"
}
if ($OverwriteOutput) {
    if (Test-Path -Path "$OutputFolder/ALZ-Export") {
        Remove-Item -Path "$OutputFolder/ALZ-Export" -Recurse -Force
    }
}

# Export File
if ($policyType -eq "policyDefinitions") {
    if ($UseBuiltIn -and $builtInPolicyNames -contains $policyName) {
    }
    else {
        # Check Output folder exists
        if (-not (Test-Path -Path "$OutputFolder/ALZ-Export/$policyType")) {
            # Create folder if does not exist
            $null = New-Item -Path "$OutputFolder/ALZ-Export/$policyType" -ItemType Directory
        }
        Write-Information "Created Policy Definition - $policyName.jsonc" -InformationAction Continue
        $policyJson | Out-File -FilePath "$OutputFolder/ALZ-Export/$policyType/$policyName.jsonc"
    }
}
if ($policyType -eq "policySetDefinitions") {
    if ($UseBuiltIn -and $builtInPolicySetNames -contains $policyName) {
    }
    else {
        # Check Output folder exists
        if (-not (Test-Path -Path "$OutputFolder/ALZ-Export/$policyType")) {
            # Create folder if does not exist
            $null = New-Item -Path "$OutputFolder/ALZ-Export/$policyType" -ItemType Directory
        }
        Write-Information "Created Policy Set Definition - $policyName.jsonc" -InformationAction Continue
        $policyJson | Out-File -FilePath "$OutputFolder/ALZ-Export/$policyType/$policyName.jsonc"
    }
}

#region Policy Set individual Custom Definitions
if ($policyType -eq "policySetDefinitions") {
    # Get policy names that are used
    $policySetDefinitions = $policyObject.properties.policyDefinitions.policyDefinitionName

    foreach ($definition in $policySetDefinitions) {
        if ($UseBuiltIn -and $builtInPolicyNames -contains $definition) {
        }
        else {
            # Set URL
            $tempURL = "https://www.azadvertizer.net/azpolicyadvertizer/$definition.html"
            # Pull HTML
            $tempRestResponse = Invoke-RestMethod -Uri $tempURL

            # Parse HTML response to find copyEPACDef
            $tempResponse = $tempRestResponse.split("function copyEPACDef() ")[1]

            try {
                $tempResponse = $tempResponse.split("const obj = ")[1]
                if ($null -eq $tempResponse) {
                    throw "Split resulted in null"
                }
            }
            catch {
                try {
                    $tempResponse = $tempRestResponse.split("function copyEPACDef() ")[1]
                    $tempResponse = $tempResponse.split("const objEPAC = ")[1]
                    if ($null -eq $tempResponse) {
                        throw "Both splits resulted in null"
                    }
                }
                catch {
                    Write-Information "Error parsing response for $definition." -InformationAction Continue
                }
            }

            $tempResponse = $tempResponse.split("};")[0] + "}"

            # Convert from JSON to get object
            $tempPolicyObject = $tempResponse | ConvertFrom-Json

            # Use Object to get policy name
            $tempPolicyName = $tempPolicyObject.name

            # Export policy definition or policy set definition
            $tempPolicyJson = $tempPolicyObject | ConvertTo-Json -Depth 100

            # Check Output folder exists
            if (-not (Test-Path -Path "$OutputFolder/ALZ-Export/policyDefinitions")) {
                # Create folder if does not exist
                $null = New-Item -Path "$OutputFolder/ALZ-Export/policyDefinitions" -ItemType Directory
            }

            # Export File
            Write-Information " - Created Policy Definition - $tempPolicyName.jsonc" -InformationAction Continue
            $tempPolicyJson | Out-File -FilePath "$OutputFolder/ALZ-Export/policyDefinitions/$tempPolicyName.jsonc"
        }
    }
}

$assignmentTemplate = @"
{
    "`$schema" : "https://raw.githubusercontent.com/Azure/enterprise-azure-policy-as-code/main/Schemas/policy-assignment-schema.json",
    "nodeName": "/Security/",
    "definitionEntry": {
        "policyName": ""
    },
    "children": [
        {
            "nodeName": "EPAC-Dev",
            "assignment": {
                "name": "",
                "displayName": "",
                "description": ""
            },
            "enforcementMode": "Default",
            "parameters": {},
            "scope": {
                "EPAC-Dev": [
                    "/providers/Microsoft.Management/managementGroups/EPAC-Dev"
                ]
            }
        }
    ]
}
"@


#region Assignment

$assignmentObject = $assignmentTemplate | ConvertFrom-Json

# Set name
if ($policyType -eq "policyDefinitions" -and !$UseBuiltIn) {
    $assignmentObject.definitionEntry.policyName = "$policyName"
}
if ($policyType -eq "policyDefinitions" -and $UseBuiltIn) {
    if ($builtInPolicyNames -contains $policyName) {
        $assignmentObject.definitionEntry | Add-Member -MemberType NoteProperty -Name "policyId" -Value "/providers/Microsoft.Authorization/policyDefinitions/$policyName"
        $assignmentObject.definitionEntry.PSObject.Properties.Remove("policyName")
    }
    else {  
        $assignmentObject.definitionEntry.policyName = "$policyName"
    }
}
if ($policyType -eq "policySetDefinitions" -and !$UseBuiltIn) {
    $assignmentObject.definitionEntry | Add-Member -MemberType NoteProperty -Name "policySetName" -Value "$policyName"
    $assignmentObject.definitionEntry.PSObject.Properties.Remove("policyName")
}
if ($policyType -eq "policySetDefinitions" -and $UseBuiltIn) {
    if ($builtInPolicySetNames -contains $policyName) {
        $assignmentObject.definitionEntry | Add-Member -MemberType NoteProperty -Name "policySetId" -Value "/providers/Microsoft.Authorization/policySetDefinitions/$policyName"
        $assignmentObject.definitionEntry.PSObject.Properties.Remove("policyName")
    }
    else {  
        $assignmentObject.definitionEntry | Add-Member -MemberType NoteProperty -Name "policySetName" -Value "$policyName"
        $assignmentObject.definitionEntry.PSObject.Properties.Remove("policyName")
    }
}

# Set Assignment Properties
$tempGuid = New-Guid
$assignmentObject.children.assignment.name = $tempGuid.Guid.split("-")[-1]
$assignmentObject.children.assignment.displayName = "$policyDisplayName"
$assignmentObject.children.assignment.description = "$policyDescription"

# Overwrite PacSelector is given
if ($PacSelector -and $PacSelector -ne "EPAC-Dev") {
    $assignmentObject.children.scope | Add-Member -MemberType NoteProperty -Name "$PacSelector" -Value ""
    $assignmentObject.children.scope.$PacSelector = $assignmentObject.children.scope.'EPAC-Dev'
    $assignmentObject.children.scope.PSObject.Properties.Remove("EPAC-Dev")
}

# Overwrite Scope if given
if ($Scope -and $PacSelector) {
    $assignmentObject.children.scope.$PacSelector = "$Scope"
}
elseif ($Scope -and !$PacSelector) {
    $assignmentObject.children.scope.'EPAC-Dev' = "$Scope"

}

#region AutoParameter
if ($AutoCreateParameters) {
    if ($UseBuiltIn) {
        if ($policyType -eq "policySetDefinitions" -and $builtInPolicySetNames -contains $policyName) {
            $policyObject = $builtInPolicySets | Where-Object { $_.Name -eq $policyName }
            $policySetParameters = $policyObject.parameter
            $parameterNames = $policySetParameters | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
            foreach ($parameter in $parameterNames) {
                $defaultEffect = ""
                if ($null -eq $($policySetParameters).$($parameter).defaultValue) {
                    Write-Warning "Default Value not found for '$parameter' in PolicySet '$policyName'" -InformationAction Continue
                }
                else {
                    $defaultEffect = $($policySetParameters).$($parameter).defaultValue
                }
                $assignmentObject.children.parameters | Add-Member -MemberType NoteProperty -Name "$parameter" -Value $defaultEffect
            }
        }
        elseif ($policyType -eq "policyDefinitions" -and $builtInPolicyNames -contains $policyName) {
            $policyObject = $builtInPolicies | Where-Object { $_.Name -eq $policyName }
            $policyParameters = $policyObject.parameter
            $parameterNames = $policyParameters | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
            foreach ($parameter in $parameterNames) {
                $defaultEffect = ""
                if ($null -eq $($policyParameters).$($parameter).defaultValue) {
                    Write-Warning "Default Value not found for '$parameter' in PolicySet '$policyName'" -InformationAction Continue
                }
                else {
                    $defaultEffect = $($policyParameters).$($parameter).defaultValue
                }
                $assignmentObject.children.parameters | Add-Member -MemberType NoteProperty -Name "$parameter" -Value $defaultEffect
            }
        }
        else {
            $policySetParameters = $policyObject.properties.parameters
            $parameterNames = $policySetParameters | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
        
            foreach ($parameter in $parameterNames) {
                $defaultEffect = ""
                if ($null -eq $($policySetParameters).$($parameter).defaultValue) {
                    Write-Warning "Default Value not found for '$parameter' in PolicySet '$policyName'" -InformationAction Continue
                }
                else {
                    $defaultEffect = $($policySetParameters).$($parameter).defaultValue
                }
                $assignmentObject.children.parameters | Add-Member -MemberType NoteProperty -Name "$parameter" -Value $defaultEffect
            }
        }
    }
    else {
        $policySetParameters = $policyObject.properties.parameters
        $parameterNames = $policySetParameters | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
    
        foreach ($parameter in $parameterNames) {
            $defaultEffect = ""
            if ($null -eq $($policySetParameters).$($parameter).defaultValue) {
                Write-Warning "Default Value not found for '$parameter' in PolicySet '$policyName'" -InformationAction Continue
            }
            else {
                $defaultEffect = $($policySetParameters).$($parameter).defaultValue
            }
            $assignmentObject.children.parameters | Add-Member -MemberType NoteProperty -Name "$parameter" -Value $defaultEffect
        }
    }
}

# Convert from PSObject to Json
$assignmentJson = $assignmentObject | ConvertTo-Json -Depth 100

# Check Assignment Output folder exists
if (-not (Test-Path -Path "$OutputFolder/ALZ-Export/policyAssignments")) {
    # Create folder if does not exist
    $null = New-Item -Path "$OutputFolder/ALZ-Export/policyAssignments" -ItemType Directory
}

# Export File
Write-Information "Created Policy Assignment - $policyName.jsonc" -InformationAction Continue
$assignmentJson | Out-File -FilePath "$OutputFolder/ALZ-Export/policyAssignments/$policyName.jsonc"
}