-
Notifications
You must be signed in to change notification settings - Fork 517
/
Invoke-PolicyToBicep.ps1
268 lines (220 loc) · 15 KB
/
Invoke-PolicyToBicep.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
<#
SUMMARY: This PowerShell script helps with the authoring of the policy definiton module by outputting information required for the variables within the module.
DESCRIPTION: This PowerShell script outputs the Name & Path to a Bicep structured .txt file named '_policyDefinitionsBicepInput.txt' ($defintionsTxtFileName) and '_policySetDefinitionsBicepInput.txt' ($defintionsSetTxtFileName) respectively. It also creates a parameters file for each of the policy set definitions. It also outputs the number of policy and policy set definition files to the console for easier reviewing as part of the PR process.
AUTHOR/S: jtracey93, seseicht
VERSION: 2.0.0
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "", Justification = "False Positive")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseBOMForUnicodeEncodedFile", "", Justification = "False Positive")]
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter()]
[string]
$rootPath = "./infra-as-code/bicep/modules/policy",
[string]
$definitionsRoot = "definitions",
[string]
$lineEnding = "unix",
[string]
$definitionsPath = "lib/policy_definitions",
[string]
$definitionsLongPath = "$definitionsRoot/$definitionsPath",
[string]
$definitionsSetPath = "lib/policy_set_definitions",
[string]
$definitionsSetLongPath = "$definitionsRoot/$definitionsSetPath",
[string]
$assignmentsRoot = "assignments",
[string]
$assignmentsPath = "lib/policy_assignments",
[string]
$assignmentsLongPath = "$assignmentsRoot/$assignmentsPath",
[string]
$defintionsTxtFileName = "_policyDefinitionsBicepInput.txt",
[string]
$defintionsSetTxtFileName = "_policySetDefinitionsBicepInput.txt",
[string]
$assignmentsTxtFileName = "_policyAssignmentsBicepInput.txt"
)
# This script relies on a custom set of classes and functions
# defined within the [ALZ-PowerShell-Module](https://github.com/Azure/Alz-powershell-module).
if (-not (Get-Module -ListAvailable -Name ALZ)) {
# Module doesn't exist, so install it
Write-Information "====> ALZ module isn't already installed. Installing..." -InformationAction Continue
Install-Module -Name ALZ -Force -Scope CurrentUser -ErrorAction Stop
Write-Information "====> ALZ module now installed." -InformationAction Continue
} else {
Write-Information "====> ALZ module is already installed." -InformationAction Continue
}
# Line Endings function to be used in three functions below
function Update-FileLineEndingType {
[CmdletBinding(SupportsShouldProcess)]
param(
[string]
$filePath
)
(Get-Content $filePath | Edit-LineEnding -LineEnding $LineEnding) | Out-File $filePath
}
#region Policy Definitions
function New-PolicyDefinitionsBicepInputTxtFile {
[CmdletBinding(SupportsShouldProcess)]
param()
Write-Information "====> Creating/Emptying '$defintionsTxtFileName'" -InformationAction Continue
Set-Content -Path "$rootPath/$definitionsLongPath/$defintionsTxtFileName" -Value $null -Encoding "utf8"
Write-Information "====> Looping Through Policy Definitions:" -InformationAction Continue
Get-ChildItem -Recurse -Path "$rootPath/$definitionsLongPath" -Filter "*.json" | ForEach-Object {
$policyDef = Get-Content $_.FullName | ConvertFrom-Json -Depth 100
$policyDefinitionName = $policyDef.name
$fileName = $_.Name
Write-Information "==> Adding '$policyDefinitionName' to '$PWD/$defintionsTxtFileName'" -InformationAction Continue
Add-Content -Path "$rootPath/$definitionsLongPath/$defintionsTxtFileName" -Encoding "utf8" -Value "{`r`n`tname: '$policyDefinitionName'`r`n`tlibDefinition: loadJsonContent('$definitionsPath/$fileName')`r`n}"
}
Write-Information "====> Running '$defintionsTxtFileName' through Line Endings" -InformationAction Continue
Update-FileLineEndingType -filePath "$rootPath/$definitionsLongPath/$defintionsTxtFileName"
$policyDefCount = Get-ChildItem -Recurse -Path "$rootPath/$definitionsLongPath" -Filter "*.json" | Measure-Object
$policyDefCountString = $policyDefCount.Count
Write-Information "====> Policy Definitions Total: $policyDefCountString" -InformationAction Continue
}
#endregion
#region Policy Set Definitions
function New-PolicySetDefinitionsBicepInputTxtFile {
[CmdletBinding(SupportsShouldProcess)]
param()
Write-Information "====> Creating/Emptying '$defintionsSetTxtFileName'" -InformationAction Continue
Set-Content -Path "$rootPath/$definitionsSetLongPath/$defintionsSetTxtFileName" -Value $null -Encoding "utf8"
Add-Content -Path "$rootPath/$definitionsSetLongPath/$defintionsSetTxtFileName" -Value "var varCustomPolicySetDefinitionsArray = [" -Encoding "utf8"
Write-Information "====> Looping Through Policy Set/Initiative Definition:" -InformationAction Continue
$policySetDefParamVarList = @()
Get-ChildItem -Recurse -Path "$rootPath/$definitionsSetLongPath" -Filter "*.json" -Exclude "*.parameters.json" | ForEach-Object {
$policyDef = Get-Content $_.FullName | ConvertFrom-Json -Depth 100
# Load child Policy Set/Initiative Definitions
$policyDefinitions = $policyDef.properties.policyDefinitions | Sort-Object -Property policyDefinitionReferenceId
$policyDefinitionName = $policyDef.name
$fileName = $_.Name
# Construct file name for Policy Set/Initiative Definitions parameters files
$parametersFileName = $fileName.Substring(0, $fileName.Length - 5) + ".parameters.json"
# Create Policy Set/Initiative Definitions parameter file
Write-Information "==> Creating/Emptying '$parametersFileName'" -InformationAction Continue
Set-Content -Path "$rootPath/$definitionsSetLongPath/$parametersFileName" -Value $null -Encoding "utf8"
# Loop through all Policy Set/Initiative Definitions Child Definitions and create parameters file for each of them
[System.Collections.Hashtable]$definitionParametersOutputJSONObject = [ordered]@{}
$policyDefinitions | Sort-Object | ForEach-Object {
$definitionReferenceId = $_.policyDefinitionReferenceId
$definitionParameters = $_.parameters
if ($definitionParameters) {
$definitionParameters | Sort-Object | ForEach-Object {
[System.Collections.Hashtable]$definitionParametersOutputArray = [ordered]@{}
$definitionParametersOutputArray.Add("parameters", $_)
}
}
else {
[System.Collections.Hashtable]$definitionParametersOutputArray = [ordered]@{}
$definitionParametersOutputArray.Add("parameters", @{})
}
$definitionParametersOutputJSONObject.Add("$definitionReferenceId", $definitionParametersOutputArray)
}
Write-Information "==> Adding parameters to '$parametersFileName'" -InformationAction Continue
Add-Content -Path "$rootPath/$definitionsSetLongPath/$parametersFileName" -Value ($definitionParametersOutputJSONObject | ConvertTo-Json -Depth 10) -Encoding "utf8"
# Sort parameters file alphabetically to remove false git diffs
Write-Information "==> Sorting parameters file '$parametersFileName' alphabetically" -InformationAction Continue
$definitionParametersOutputJSONObjectSorted = New-Object PSCustomObject
Get-Content -Raw -Path "$rootPath/$definitionsSetLongPath/$parametersFileName" | ConvertFrom-Json -pv fromPipe -Depth 10 |
Get-Member -Type NoteProperty | Sort-Object Name | ForEach-Object {
Add-Member -InputObject $definitionParametersOutputJSONObjectSorted -Type NoteProperty -Name $_.Name -Value $fromPipe.$($_.Name)
}
Set-Content -Path "$rootPath/$definitionsSetLongPath/$parametersFileName" -Value ($definitionParametersOutputJSONObjectSorted | ConvertTo-Json -Depth 10) -Encoding "utf8"
# Check if variable exists before trying to clear it
if ($policySetDefinitionsOutputForBicep) {
Clear-Variable -Name policySetDefinitionsOutputForBicep -ErrorAction Continue
}
# Create HashTable variable
[System.Collections.Hashtable]$policySetDefinitionsOutputForBicep = [ordered]@{}
# Loop through child Policy Set/Initiative Definitions if HashTable not == 0
if (($policyDefinitions.Count) -ne 0) {
$policyDefinitions | Sort-Object | ForEach-Object {
if ($null -ne $_.groupNames -and $_.groupNames.Count -ne 0) {
$joinedGroupNames = "'" + ($_.groupNames -join "','" ) + "'"
$policySetDefinitionsOutputForBicep.Add($_.policyDefinitionReferenceId, @($_.policyDefinitionId, $joinedGroupNames))
}
else {
$policySetDefinitionsOutputForBicep.Add($_.policyDefinitionReferenceId, @($_.policyDefinitionId, ""))
}
}
}
# Add Policy Set/Initiative Definition Parameter Variables to Bicep Input File
$policySetDefParamVarTrimJsonExt = $parametersFileName.TrimEnd("json").Replace('.', '_')
$policySetDefParamVarCreation = "var" + ($policySetDefParamVarTrimJsonExt -replace '(?:^|_|-)(\p{L})', { $_.Groups[1].Value.ToUpper() }).TrimEnd('_')
$policySetDefParamVar = "var " + $policySetDefParamVarCreation + " = " + "loadJsonContent('$definitionsSetPath/$parametersFileName')"
$policySetDefParamVarList += $policySetDefParamVar
# Start output file creation of Policy Set/Initiative Definitions for Bicep
Write-Information "==> Adding '$policyDefinitionName' to '$PWD/$defintionsSetTxtFileName'" -InformationAction Continue
Add-Content -Path "$rootPath/$definitionsSetLongPath/$defintionsSetTxtFileName" -Encoding "utf8" -Value "`t{`r`n`t`tname: '$policyDefinitionName'`r`n`t`tlibSetDefinition: loadJsonContent('$definitionsSetPath/$fileName')`r`n`t`tlibSetChildDefinitions: ["
# Loop through child Policy Set/Initiative Definitions for Bicep output if HashTable not == 0
if (($policySetDefinitionsOutputForBicep.Count) -ne 0) {
$policySetDefinitionsOutputForBicep.Keys | Sort-Object | ForEach-Object {
$definitionReferenceId = $_
$definitionReferenceIdForParameters = $_
$definitionId = $($policySetDefinitionsOutputForBicep[$_][0])
$groups = $($policySetDefinitionsOutputForBicep[$_][1])
# If definitionReferenceId or definitionReferenceIdForParameters contains apostrophes, replace that apostrophe with a backslash and an apostrohphe for Bicep string escaping
if ($definitionReferenceId.Contains("'")) {
$definitionReferenceId = $definitionReferenceId.Replace("'", "\'")
}
if ($definitionReferenceIdForParameters.Contains("'")) {
$definitionReferenceIdForParameters = $definitionReferenceIdForParameters.Replace("'", "\'")
}
# If definitionReferenceId contains, then wrap in definitionReferenceId value in [] to comply with bicep formatting
if ($definitionReferenceIdForParameters.Contains("-") -or $definitionReferenceIdForParameters.Contains(" ") -or $definitionReferenceIdForParameters.Contains("\'") -or $definitionReferenceIdForParameters -match '^[0-9].+') {
$definitionReferenceIdForParameters = "['$definitionReferenceIdForParameters']"
# Add nested array of objects to each Policy Set/Initiative Definition in the Bicep variable, without the '.' before the definitionReferenceId to make it an accessor
Add-Content -Path "$rootPath/$definitionsSetLongPath/$defintionsSetTxtFileName" -Encoding "utf8" -Value "`t`t`t{`r`n`t`t`t`tdefinitionReferenceId: '$definitionReferenceId'`r`n`t`t`t`tdefinitionId: '$definitionId'`r`n`t`t`t`tdefinitionParameters: $policySetDefParamVarCreation$definitionReferenceIdForParameters.parameters`r`n`t`t`t`tdefinitionGroups: [$groups]`r`n`t`t`t}"
}
else {
# Add nested array of objects to each Policy Set/Initiative Definition in the Bicep variable
Add-Content -Path "$rootPath/$definitionsSetLongPath/$defintionsSetTxtFileName" -Encoding "utf8" -Value "`t`t`t{`r`n`t`t`t`tdefinitionReferenceId: '$definitionReferenceId'`r`n`t`t`t`tdefinitionId: '$definitionId'`r`n`t`t`t`tdefinitionParameters: $policySetDefParamVarCreation.$definitionReferenceIdForParameters.parameters`r`n`t`t`t`tdefinitionGroups: [$groups]`r`n`t`t`t}"
}
}
}
# Finish output file creation of Policy Set/Initiative Definitions for Bicep
Add-Content -Path "$rootPath/$definitionsSetLongPath/$defintionsSetTxtFileName" -Encoding "utf8" -Value "`t`t]`r`n`t}"
}
Add-Content -Path "$rootPath/$definitionsSetLongPath/$defintionsSetTxtFileName" -Encoding "utf8" -Value "]`r`n"
# Add Policy Set/Initiative Definition Parameter Variables to Bicep Input File
Add-Content -Path "$rootPath/$definitionsSetLongPath/$defintionsSetTxtFileName" -Encoding "utf8" -Value "`r`n// Policy Set/Initiative Definition Parameter Variables`r`n"
$policySetDefParamVarList | ForEach-Object {
Add-Content -Path "$rootPath/$definitionsSetLongPath/$defintionsSetTxtFileName" -Encoding "utf8" -Value "$_`r`n"
}
Write-Information "====> Running '$defintionsSetTxtFileName' through Line Endings" -InformationAction Continue
Update-FileLineEndingType -filePath "$rootPath/$definitionsSetLongPath/$defintionsSetTxtFileName"
$policyDefCount = Get-ChildItem -Recurse -Path "$rootPath/$definitionsSetLongPath" -Filter "*.json" -Exclude "*.parameters.json" | Measure-Object
$policyDefCountString = $policyDefCount.Count
Write-Information "====> Policy Set/Initiative Definitions Total: $policyDefCountString" -InformationAction Continue
}
#endregion
#region Policy Asssignments
function New-PolicyAssignmentsBicepInputTxtFile {
[CmdletBinding(SupportsShouldProcess)]
param()
Write-Information "====> Creating/Emptying '$assignmentsTxtFileName'" -InformationAction Continue
Set-Content -Path "$rootPath/$assignmentsLongPath/$assignmentsTxtFileName" -Value $null -Encoding "utf8"
Write-Information "====> Looping Through Policy Assignments:" -InformationAction Continue
Get-ChildItem -Recurse -Path "$rootPath/$assignmentsLongPath" -Filter "*.json" | ForEach-Object {
$policyAssignment = Get-Content $_.FullName | ConvertFrom-Json -Depth 100
$policyAssignmentName = $policyAssignment.name
$policyAssignmentDefinitionID = $policyAssignment.properties.policyDefinitionId
$fileName = $_.Name
# Remove hyphens from Policy Assignment Name
$policyAssignmentNameNoHyphens = $policyAssignmentName.replace("-", "")
Write-Information "==> Adding '$policyAssignmentName' to '$PWD/$assignmentsTxtFileName'" -InformationAction Continue
Add-Content -Path "$rootPath/$assignmentsLongPath/$assignmentsTxtFileName" -Encoding "utf8" -Value "var varPolicyAssignment$policyAssignmentNameNoHyphens = {`r`n`tdefinitionId: '$policyAssignmentDefinitionID'`r`n`tlibDefinition: loadJsonContent('../../../policy/$assignmentsLongPath/$fileName')`r`n}`r`n"
}
Write-Information "====> Running '$assignmentsTxtFileName' through Line Endings" -InformationAction Continue
Update-FileLineEndingType -filePath "$rootPath/$assignmentsLongPath/$assignmentsTxtFileName"
$policyAssignmentCount = Get-ChildItem -Recurse -Path "$rootPath/$assignmentsLongPath" -Filter "*.json" | Measure-Object
$policyAssignmentCountString = $policyAssignmentCount.Count
Write-Information "====> Policy Assignments Total: $policyAssignmentCountString" -InformationAction Continue
}
#endregion
New-PolicyDefinitionsBicepInputTxtFile
New-PolicySetDefinitionsBicepInputTxtFile
New-PolicyAssignmentsBicepInputTxtFile