forked from fawohlsc/azure-policy-testing
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPolicy.Utils.psm1
218 lines (189 loc) · 8.86 KB
/
Policy.Utils.psm1
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
Import-Module -Name Az.Resources
<#
.SYNOPSIS
Completes a policy compliance scan.
.DESCRIPTION
Starts a policy compliance scan and awaits it's completion. In case of a failure, the policy compliance scan is retried (Default: 3 times).
.PARAMETER ResourceGroup
The resource group to be scanned for policy compliance.
.PARAMETER MaxRetries
The maximum amount of retries in case of failures (Default: 3 times).
.EXAMPLE
$ResourceGroup | Complete-PolicyComplianceScan
#>
function Complete-PolicyComplianceScan {
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[ValidateNotNull()]
[Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceGroup]$ResourceGroup,
[Parameter()]
[ValidateRange(1, [ushort]::MaxValue)]
[ushort]$MaxRetries = 3
)
# Policy compliance scan might fail, hence retrying to avoid flaky tests.
$retries = 0
do {
$job = Start-AzPolicyComplianceScan -ResourceGroupName $ResourceGroup.ResourceGroupName -PassThru -AsJob
$succeeded = $job | Wait-Job | Receive-Job
if ($succeeded) {
break
}
# Failure: Retry policy compliance scan when still below maximum retries.
elseif ($retries -le $MaxRetries) {
Write-Host "Policy compliance scan for resource group '$($ResourceGroup.ResourceId)' failed. Retrying..."
$retries++
continue # Not required, just defensive programming.
}
# Failure: Policy compliance scan is still failing after maximum retries.
else {
throw "Policy compliance scan for resource group '$($ResourceGroup.ResourceId)' failed even after $($MaxRetries) retries."
}
} while ($retries -le $MaxRetries) # Prevent endless loop, just defensive programming.
}
<#
.SYNOPSIS
Completes a policy remediation.
.DESCRIPTION
Starts a remediation for a policy and awaits it's completion. In case of a failure, the policy remediation is retried (Default: 3 times).
.PARAMETER Resource
The resource to be remediated.
.PARAMETER PolicyDefinitionName
The name of the policy definition.
.PARAMETER CheckDeployment
The switch to determine if a deployment is expected. If a deployment is expected but did not happen during policy remediation, the policy remediation is retried.
.PARAMETER MaxRetries
The maximum amount of retries in case of failures (Default: 3 times).
.EXAMPLE
$routeTable | Complete-PolicyRemediation -PolicyDefinition "Modify-RouteTable-NextHopVirtualAppliance" -CheckDeployment
#>
function Complete-PolicyRemediation {
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[ValidateNotNull()]
[Microsoft.Azure.Commands.Network.Models.PSChildResource]$Resource,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$PolicyDefinitionName,
[Parameter()]
[switch]$CheckDeployment,
[Parameter()]
[ValidateRange(1, [ushort]::MaxValue)]
[ushort]$MaxRetries = 3
)
# Determine policy assignment id-
$scope = "/subscriptions/$((Get-AzContext).Subscription.Id)"
$policyAssignmentId = (Get-AzPolicyAssignment -Scope $scope
| Select-Object -Property PolicyAssignmentId -ExpandProperty Properties
| Where-Object { $_.PolicyDefinitionId.EndsWith($PolicyDefinitionName) }
| Select-Object -Property PolicyAssignmentId -First 1
).PolicyAssignmentId
if ($null -eq $policyAssignmentId) {
throw "Policy '$($PolicyDefinitionName)' is not assigned to scope '$($scope)'."
}
# Remediation might be started before all previous changes on the resource in scope are completed.
# This race condition could lead to a successful remediation without any deployment being triggered.
# When a deployment is expected, it might be required to retry remediation to avoid flaky tests.
$retries = 0
do {
# Trigger and wait for remediation.
$job = Start-AzPolicyRemediation `
-Name "$($Resource.Name)-$([DateTimeOffset]::Now.ToUnixTimeSeconds())" `
-Scope $Resource.Id `
-PolicyAssignmentId $policyAssignmentId `
-ResourceDiscoveryMode ReEvaluateCompliance `
-AsJob
$remediation = $job | Wait-Job | Receive-Job
# Check remediation provisioning state and deployment when required .
$succeeded = $remediation.ProvisioningState -eq "Succeeded"
if ($succeeded) {
if ($CheckDeployment) {
$deployed = $remediation.DeploymentSummary.TotalDeployments -gt 0
# Success: Deployment was triggered.
if ($deployed) {
break
}
# Failure: No deployment was triggered, so retry when still below maximum retries.
elseif ($retries -le $MaxRetries) {
Write-Host "Policy '$($PolicyDefinitionName)' succeeded to remediated resource '$($Resource.Id)', but no deployment was triggered. Retrying..."
$retries++
continue # Not required, just defensive programming.
}
# Failure: No deployment was triggered even after maximum retries.
else {
throw "Policy '$($PolicyDefinitionName)' succeeded to remediated resource '$($Resource.Id)', but no deployment was triggered even after $($MaxRetries) retries."
}
}
# Success: No deployment need to checked, hence no retry required.
else {
break
}
}
# Failure: Remediation failed, so retry when still below maximum retries.
elseif ($retries -le $MaxRetries) {
Write-Host "Policy '$($PolicyDefinitionName)' failed to remediate resource '$($Resource.Id)'. Retrying..."
$retries++
continue # Not required, just defensive programming.
}
# Failure: Remediation failed even after maximum retries.
else {
throw "Policy '$($PolicyDefinitionName)' failed to remediate resource '$($Resource.Id)' even after $($MaxRetries) retries."
}
} while ($retries -le $MaxRetries) # Prevent endless loop, just defensive programming.
}
<#
.SYNOPSIS
Gets the policy compliance state of a resource.
.DESCRIPTION
Gets the policy compliance state of a resource. In case of a failure, getting the policy compliance state is retried (Default: 30 times) after a few seconds of waiting (Default: 60s).
.PARAMETER Resource
The resource to get the policy compliance state for.
.PARAMETER PolicyDefinitionName
The name of the policy definition.
.PARAMETER WaitSeconds
The duration in seconds to wait between retries in case of failures (Default: 60s).
.PARAMETER MaxRetries
The maximum amount of retries in case of failures (Default: 3 times).
.EXAMPLE
$networkSecurityGroup | Get-PolicyComplianceState -PolicyDefinition "OP-Audit-NSGAny" | Should -BeFalse
#>
function Get-PolicyComplianceState {
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[ValidateNotNull()]
[Microsoft.Azure.Commands.Network.Models.PSChildResource]$Resource,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$PolicyDefinitionName,
[Parameter()]
[ValidateRange(1, [ushort]::MaxValue)]
[ushort]$WaitSeconds = 60,
[Parameter()]
[ValidateRange(1, [ushort]::MaxValue)]
[ushort]$MaxRetries = 30
)
# Policy compliance scan might be completed, but policy compliance state might still be null due to race conditions.
# Hence waiting a few seconds and retrying to get the policy compliance state to avoid flaky tests.
$retries = 0
do {
$isCompliant = (Get-AzPolicyState `
-PolicyDefinitionName $PolicyDefinitionName `
-Filter "ResourceId eq '$($Resource.Id)'" `
).IsCompliant
# Success: Policy compliance state is not null.
if ($null -ne $isCompliant) {
break
}
# Failure: Policy compliance state is null, so wait a few seconds and retry when still below maximum retries.
elseif ($retries -le $MaxRetries) {
Write-Host "Policy '$($PolicyDefinitionName)' completed compliance scan for resource '$($Resource.Id)', but policy compliance state is null. Retrying..."
Start-Sleep -Seconds $WaitSeconds
$retries++
continue # Not required, just defensive programming.
}
# Failure: Policy compliance state still null after maximum retries.
else {
throw "Policy '$($PolicyDefinitionName)' completed compliance scan for resource '$($Resource.Id)', but policy compliance state is null even after $($MaxRetries) retries."
}
} while ($retries -le $MaxRetries) # Prevent endless loop, just defensive programming.
return $isCompliant
}