Skip to content

Commit

Permalink
Merge pull request #392 from arjenhuitema/workflow-check-policy-build
Browse files Browse the repository at this point in the history
Adding workflow to check for policy build
  • Loading branch information
Brunoga-MS authored Oct 30, 2024
2 parents 2f09711 + ff8ee8f commit f7d15b0
Show file tree
Hide file tree
Showing 15 changed files with 431 additions and 49 deletions.
133 changes: 133 additions & 0 deletions .github/actions-pester/PolicyPesterTestHelper.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<#
.DESCRIPTION
Uses git diff to return a list of policy definitions and policy set definition file paths.
#>

function Get-PolicyFiles
{
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter()]
[String]$DiffFilter,

[Parameter()]
[String]$PolicyDir = "$($env:POLICY_DIR)",

[Parameter()]
[String]$PolicySetDir = "$($env:POLICYSET_DIR)",

[Parameter()]
[String]$PRBranch = "$($env:GITHUB_HEAD_REF)",

[Parameter()]
[String]$BaseBranch = "$($env:GITHUB_BASE_REF)"
)

$PolicyFiles = @(git diff --diff-filter=$DiffFilter --name-only origin/main $PRBranch -- $PolicyDir)
$PolicySetsFiles = @(git diff --diff-filter=$DiffFilter --name-only origin/main $PRBranch -- $PolicySetDir)

$PolicyAndSetFiles = $PolicyFiles + $PolicySetsFiles

$PolicyAndSetFiles | ForEach-Object {
return $_
}
}

function Remove-JSONMetadata {

[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[hashtable] $TemplateObject
)
$TemplateObject.Remove('metadata')

# Differantiate case: With user defined types (resources property is hashtable) vs without user defined types (resources property is array)
if ($TemplateObject.resources.GetType().BaseType.Name -eq 'Hashtable') {
# Case: Hashtable
$resourceIdentifiers = $TemplateObject.resources.Keys
for ($index = 0; $index -lt $resourceIdentifiers.Count; $index++) {
if ($TemplateObject.resources[$resourceIdentifiers[$index]].type -eq 'Microsoft.Resources/deployments' -and $TemplateObject.resources[$resourceIdentifiers[$index]].properties.template.GetType().BaseType.Name -eq 'Hashtable') {
$TemplateObject.resources[$resourceIdentifiers[$index]] = Remove-JSONMetadata -TemplateObject $TemplateObject.resources[$resourceIdentifiers[$index]].properties.template
}
}
} else {
# Case: Array
for ($index = 0; $index -lt $TemplateObject.resources.Count; $index++) {
if ($TemplateObject.resources[$index].type -eq 'Microsoft.Resources/deployments' -and $TemplateObject.resources[$index].properties.template.GetType().BaseType.Name -eq 'Hashtable') {
$TemplateObject.resources[$index] = Remove-JSONMetadata -TemplateObject $TemplateObject.resources[$index].properties.template
}
}
}

return $TemplateObject
}

function ConvertTo-OrderedHashtable {

[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string] $JSONInputObject # Must be string to workaround auto-conversion
)

$JSONObject = ConvertFrom-Json $JSONInputObject -AsHashtable -Depth 99 -NoEnumerate
$orderedLevel = [ordered]@{}

if (-not ($JSONObject.GetType().BaseType.Name -eq 'Hashtable')) {
return $JSONObject # E.g. in primitive data types [1,2,3]
}

foreach ($currentLevelKey in ($JSONObject.Keys | Sort-Object -Culture 'en-US')) {

if ($null -eq $JSONObject[$currentLevelKey]) {
# Handle case in which the value is 'null' and hence has no type
$orderedLevel[$currentLevelKey] = $null
continue
}

switch ($JSONObject[$currentLevelKey].GetType().BaseType.Name) {
{ $PSItem -in @('Hashtable') } {
$orderedLevel[$currentLevelKey] = ConvertTo-OrderedHashtable -JSONInputObject ($JSONObject[$currentLevelKey] | ConvertTo-Json -Depth 99)
}
'Array' {
$arrayOutput = @()

# Case: Array of arrays
$arrayElements = $JSONObject[$currentLevelKey] | Where-Object { $_.GetType().BaseType.Name -eq 'Array' }
foreach ($array in $arrayElements) {
if ($array.Count -gt 1) {
# Only sort for arrays with more than one item. Otherwise single-item arrays are casted
$array = $array | Sort-Object -Culture 'en-US'
}
$arrayOutput += , (ConvertTo-OrderedHashtable -JSONInputObject ($array | ConvertTo-Json -Depth 99))
}

# Case: Array of objects
$hashTableElements = $JSONObject[$currentLevelKey] | Where-Object { $_.GetType().BaseType.Name -eq 'Hashtable' }
foreach ($hashTable in $hashTableElements) {
$arrayOutput += , (ConvertTo-OrderedHashtable -JSONInputObject ($hashTable | ConvertTo-Json -Depth 99))
}

# Case: Primitive data types
$primitiveElements = $JSONObject[$currentLevelKey] | Where-Object { $_.GetType().BaseType.Name -notin @('Array', 'Hashtable') } | ConvertTo-Json -Depth 99 | ConvertFrom-Json -AsHashtable -NoEnumerate -Depth 99
if ($primitiveElements.Count -gt 1) {
$primitiveElements = $primitiveElements | Sort-Object -Culture 'en-US'
}
$arrayOutput += $primitiveElements

if ($array.Count -gt 1) {
# Only sort for arrays with more than one item. Otherwise single-item arrays are casted
$arrayOutput = $arrayOutput | Sort-Object -Culture 'en-US'
}
$orderedLevel[$currentLevelKey] = $arrayOutput
}
Default {
# string/int/etc.
$orderedLevel[$currentLevelKey] = $JSONObject[$currentLevelKey]
}
}
}

return $orderedLevel
}
198 changes: 198 additions & 0 deletions .github/actions-pester/Test-BuildPolicies.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
Describe 'UnitTest-BuildPolicies' {

BeforeAll {
Import-Module -Name $PSScriptRoot\PolicyPesterTestHelper.psm1 -Force -Verbose

New-Item -Name "buildout" -Type Directory

# Build the PR policies, and initiatives to a temp folder
bicep build ./patterns/alz/templates/policies-Automation.bicep --outfile ./buildout/policies-Automation.json
bicep build ./patterns/alz/templates/policies-Compute.bicep --outfile ./buildout/policies-Compute.json
bicep build ./patterns/alz/templates/policies-Hybrid.bicep --outfile ./buildout/policies-Hybrid.json
bicep build ./patterns/alz/templates/policies-KeyManagement.bicep --outfile ./buildout/policies-KeyManagement.json
bicep build ./patterns/alz/templates/policies-Monitoring.bicep --outfile ./buildout/policies-Monitoring.json
bicep build ./patterns/alz/templates/policies-Network.bicep --outfile ./buildout/policies-Network.json
bicep build ./patterns/alz/templates/policies-NotificationAssets.bicep --outfile ./buildout/policies-NotificationAssets.json
bicep build ./patterns/alz/templates/policies-RecoveryServices.bicep --outfile ./buildout/policies-RecoveryServices.json
bicep build ./patterns/alz/templates/policies-ServiceHealth.bicep --outfile ./buildout/policies-ServiceHealth.json
bicep build ./patterns/alz/templates/policies-Storage.bicep --outfile ./buildout/policies-Storage.json
bicep build ./patterns/alz/templates/policies-Web.bicep --outfile ./buildout/policies-Web.json
bicep build ./patterns/alz/templates/policySets.bicep --outfile ./buildout/policySets.json
}

Context "Check Policy Builds" {

It "Check Automation policies build done" {
$prFile = "./patterns/alz/policyDefinitions/policies-Automation.json"
$buildFile = "./buildout/policies-Automation.json"

$buildJson = Remove-JSONMetadata -TemplateObject (Get-Content $buildFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$buildJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $buildJson -Depth 99)

$prJson = Remove-JSONMetadata -TemplateObject (Get-Content $prFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$prJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $prJson -Depth 99)

# Compare files we built to the PR files
(ConvertTo-Json $buildJson -Depth 99) | Should -Be (ConvertTo-Json $prJson -Depth 99) -Because "the [policies-Automation.json] should be based on the latest [policies-Automation.bicep] file. Please run [` bicep build ./patterns/alz/templates/policies-Automation.bicep --outfile ./patterns/alz/policyDefinitions/policies-Automation.json `] using the latest Bicep CLI version."
}

It "Check Compute policies build done" {
$prFile = "./patterns/alz/policyDefinitions/policies-Compute.json"
$buildFile = "./buildout/policies-Compute.json"

$buildJson = Remove-JSONMetadata -TemplateObject (Get-Content $buildFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$buildJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $buildJson -Depth 99)

$prJson = Remove-JSONMetadata -TemplateObject (Get-Content $prFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$prJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $prJson -Depth 99)

# Compare files we built to the PR files
(ConvertTo-Json $buildJson -Depth 99) | Should -Be (ConvertTo-Json $prJson -Depth 99) -Because "the [policies-Compute.json] should be based on the latest [policies-Compute.bicep] file. Please run [` bicep build ./patterns/alz/templates/policies-Compute.bicep --outfile ./patterns/alz/policyDefinitions/policies-Compute.json `] using the latest Bicep CLI version."
}

It "Check Hybrid policies build done" {
$prFile = "./patterns/alz/policyDefinitions/policies-Hybrid.json"
$buildFile = "./buildout/policies-Hybrid.json"

$buildJson = Remove-JSONMetadata -TemplateObject (Get-Content $buildFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$buildJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $buildJson -Depth 99)

$prJson = Remove-JSONMetadata -TemplateObject (Get-Content $prFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$prJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $prJson -Depth 99)

# Compare files we built to the PR files
(ConvertTo-Json $buildJson -Depth 99) | Should -Be (ConvertTo-Json $prJson -Depth 99) -Because "the [policies-Hybrid.json] should be based on the latest [policies-Hybrid.bicep] file. Please run [` bicep build ./patterns/alz/templates/policies-Hybrid.bicep --outfile ./patterns/alz/policyDefinitions/policies-Hybrid.json `] using the latest Bicep CLI version."
}

It "Check KeyManagement policies build done" {
$prFile = "./patterns/alz/policyDefinitions/policies-KeyManagement.json"
$buildFile = "./buildout/policies-KeyManagement.json"

$buildJson = Remove-JSONMetadata -TemplateObject (Get-Content $buildFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$buildJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $buildJson -Depth 99)

$prJson = Remove-JSONMetadata -TemplateObject (Get-Content $prFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$prJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $prJson -Depth 99)

# Compare files we built to the PR files
(ConvertTo-Json $buildJson -Depth 99) | Should -Be (ConvertTo-Json $prJson -Depth 99) -Because "the [policies-KeyManagement.json] should be based on the latest [policies-KeyManagement.bicep] file. Please run [` bicep build ./patterns/alz/templates/policies-KeyManagement.bicep --outfile ./patterns/alz/policyDefinitions/policies-KeyManagement.json `] using the latest Bicep CLI version."
}

It "Check Monitoring policies build done" {
$prFile = "./patterns/alz/policyDefinitions/policies-Monitoring.json"
$buildFile = "./buildout/policies-Monitoring.json"

$buildJson = Remove-JSONMetadata -TemplateObject (Get-Content $buildFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$buildJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $buildJson -Depth 99)

$prJson = Remove-JSONMetadata -TemplateObject (Get-Content $prFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$prJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $prJson -Depth 99)

# Compare files we built to the PR files
(ConvertTo-Json $buildJson -Depth 99) | Should -Be (ConvertTo-Json $prJson -Depth 99) -Because "the [policies-Monitoring.json] should be based on the latest [policies-Monitoring.bicep] file. Please run [` bicep build ./patterns/alz/templates/policies-Monitoring.bicep --outfile ./patterns/alz/policyDefinitions/policies-Monitoring.json `] using the latest Bicep CLI version."
}

It "Check Network policies build done" {
$prFile = "./patterns/alz/policyDefinitions/policies-Network.json"
$buildFile = "./buildout/policies-Network.json"

$buildJson = Remove-JSONMetadata -TemplateObject (Get-Content $buildFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$buildJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $buildJson -Depth 99)

$prJson = Remove-JSONMetadata -TemplateObject (Get-Content $prFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$prJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $prJson -Depth 99)

# Compare files we built to the PR files
(ConvertTo-Json $buildJson -Depth 99) | Should -Be (ConvertTo-Json $prJson -Depth 99) -Because "the [policies-Network.json] should be based on the latest [policies-Network.bicep] file. Please run [` bicep build ./patterns/alz/templates/policies-Network.bicep --outfile ./patterns/alz/policyDefinitions/policies-Network.json `] using the latest Bicep CLI version."
}

It "Check NotificationAssets policies build done" {
$prFile = "./patterns/alz/policyDefinitions/policies-NotificationAssets.json"
$buildFile = "./buildout/policies-NotificationAssets.json"

$buildJson = Remove-JSONMetadata -TemplateObject (Get-Content $buildFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$buildJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $buildJson -Depth 99)

$prJson = Remove-JSONMetadata -TemplateObject (Get-Content $prFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$prJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $prJson -Depth 99)

# Compare files we built to the PR files
(ConvertTo-Json $buildJson -Depth 99) | Should -Be (ConvertTo-Json $prJson -Depth 99) -Because "the [policies-NotificationAssets.json] should be based on the latest [policies-NotificationAssets.bicep] file. Please run [` bicep build ./patterns/alz/templates/policies-NotificationAssets.bicep --outfile ./patterns/alz/policyDefinitions/policies-NotificationAssets.json `] using the latest Bicep CLI version."
}

It "Check RecoveryServices policies build done" {
$prFile = "./patterns/alz/policyDefinitions/policies-RecoveryServices.json"
$buildFile = "./buildout/policies-RecoveryServices.json"

$buildJson = Remove-JSONMetadata -TemplateObject (Get-Content $buildFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$buildJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $buildJson -Depth 99)

$prJson = Remove-JSONMetadata -TemplateObject (Get-Content $prFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$prJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $prJson -Depth 99)

# Compare files we built to the PR files
(ConvertTo-Json $buildJson -Depth 99) | Should -Be (ConvertTo-Json $prJson -Depth 99) -Because "the [policies-RecoveryServices.json] should be based on the latest [policies-RecoveryServices.bicep] file. Please run [` bicep build ./patterns/alz/templates/policies-RecoveryServices.bicep --outfile ./patterns/alz/policyDefinitions/policies-RecoveryServices.json `] using the latest Bicep CLI version."
}

It "Check ServiceHealth policies build done" {
$prFile = "./patterns/alz/policyDefinitions/policies-ServiceHealth.json"
$buildFile = "./buildout/policies-ServiceHealth.json"

$buildJson = Remove-JSONMetadata -TemplateObject (Get-Content $buildFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$buildJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $buildJson -Depth 99)

$prJson = Remove-JSONMetadata -TemplateObject (Get-Content $prFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$prJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $prJson -Depth 99)

# Compare files we built to the PR files
(ConvertTo-Json $buildJson -Depth 99) | Should -Be (ConvertTo-Json $prJson -Depth 99) -Because "the [policies-ServiceHealth.json] should be based on the latest [policies-ServiceHealth.bicep] file. Please run [` bicep build ./patterns/alz/templates/policies-ServiceHealth.bicep --outfile ./patterns/alz/policyDefinitions/policies-ServiceHealth.json `] using the latest Bicep CLI version."
}

It "Check Storage policies build done" {
$prFile = "./patterns/alz/policyDefinitions/policies-Storage.json"
$buildFile = "./buildout/policies-Storage.json"

$buildJson = Remove-JSONMetadata -TemplateObject (Get-Content $buildFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$buildJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $buildJson -Depth 99)

$prJson = Remove-JSONMetadata -TemplateObject (Get-Content $prFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$prJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $prJson -Depth 99)

# Compare files we built to the PR files
(ConvertTo-Json $buildJson -Depth 99) | Should -Be (ConvertTo-Json $prJson -Depth 99) -Because "the [policies-Storage.json] should be based on the latest [policies-Storage.bicep] file. Please run [` bicep build ./patterns/alz/templates/policies-Storage.bicep --outfile ./patterns/alz/policyDefinitions/policies-Storage.json `] using the latest Bicep CLI version."
}

It "Check Web policies build done" {
$prFile = "./patterns/alz/policyDefinitions/policies-Web.json"
$buildFile = "./buildout/policies-Web.json"

$buildJson = Remove-JSONMetadata -TemplateObject (Get-Content $buildFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$buildJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $buildJson -Depth 99)

$prJson = Remove-JSONMetadata -TemplateObject (Get-Content $prFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$prJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $prJson -Depth 99)

# Compare files we built to the PR files
(ConvertTo-Json $buildJson -Depth 99) | Should -Be (ConvertTo-Json $prJson -Depth 99) -Because "the [policies-Web.json] should be based on the latest [policies-Web.bicep] file. Please run [` bicep build ./patterns/alz/templates/policies-Web.bicep --outfile ./patterns/alz/policyDefinitions/policies-Web.json `] using the latest Bicep CLI version."
}

It "Check PolicySets build done" {
$prFile = "./patterns/alz/policyDefinitions/policySets.json"
$buildFile = "./buildout/policySets.json"

$buildJson = Remove-JSONMetadata -TemplateObject (Get-Content $buildFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$buildJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $buildJson -Depth 99)

$prJson = Remove-JSONMetadata -TemplateObject (Get-Content $prFile -Raw | ConvertFrom-Json -Depth 99 -AsHashtable)
$prJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $prJson -Depth 99)

# Compare files we built to the PR files
(ConvertTo-Json $buildJson -Depth 99) | Should -Be (ConvertTo-Json $prJson -Depth 99) -Because "the [policySets.json] should be based on the latest [policySets.bicep] file. Please run [` bicep build ./patterns/alz/templates/policySets.bicep --outfile ./patterns/alz/policyDefinitions/policySets.json `] using the latest Bicep CLI version."
}

}

AfterAll {
# These are not the droids you are looking for...
}
}
Loading

0 comments on commit f7d15b0

Please sign in to comment.