Test/Private/CommonHealthChecks.Test.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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# Determine our script root
$parent = Split-Path $PSScriptRoot -Parent
$script:root = Split-Path $parent -Parent
# Load module via definition
Import-Module $script:root\ADFSDiagnostics.psd1 -Force

InModuleScope ADFSDiagnostics {
    # Shared constants
    $sharedError = "Error message"
    $sharedErrorException = "System.Management.Automation.RuntimeException: Error message"

    Describe "TestTLSMismatch" {
        It "should pass because all TLS versions are enabled" {
            # Arrange
            Mock -CommandName IsTlsVersionEnabled -MockWith { return $true }

            # Act
            $ret = TestTlsMismatch

            # Assert
            $ret.Result | should be Pass
        }

        Context "Specific TLS versions" {
            ("1.0", "1.1", "1.2") | ForEach-Object {
                It "should warn because only TLS $_ is enabled" {
                    # Arrange
                    $tlsVersion = $_
                    Mock -CommandName IsTlsVersionEnabled -MockWith { return $false } -ParameterFilter { $version -ne $tlsVersion }
                    Mock -CommandName IsTlsVersionEnabled -MockWith { return $true } -ParameterFilter { $version -eq $tlsVersion }

                    # We mock the warning function to avoid writing to console
                    Mock -CommandName Out-Warning -MockWith { }

                    # Act
                    $ret = TestTLSMismatch

                    # Assert
                    $ret.Result | should be Warning
                    $ret.Detail | should be "Detected that only TLS $tlsVersion is enabled. Ensure that this is also enabled on your other STS and Proxy servers."

                    Assert-MockCalled Out-Warning
                }
            }
        }

        It "should fail because all TLS versions are disabled" {
            # Arrange
            Mock -CommandName IsTlsVersionEnabled -MockWith { return $false }

            # Act
            $ret = TestTlsMismatch

            # Assert
            $ret.Result | should be Fail
            $ret.Detail | should be "Detected that all TLS versions are disabled. This will cause problems between your STS and Proxy servers. Fix this by enabling the correct TLS version."
        }

        It "should error" {
            # Arrange
            Mock -CommandName IsTlsVersionEnabled -MockWith { throw $sharedError }

            # Act
            $ret = TestTLSMismatch

            # Assert
            $ret.Result | should be Error
            $ret.ExceptionMessage | should be $sharedError
            $ret.Exception | should be $sharedErrorException
        }
    }

    Describe "TestAdfsEventLogs" {
        ($adfs2x, $adfs3) | ForEach-Object {
            Context "AD FS version $_" {
                $adfsVersionToTest = $_
                Mock -CommandName Get-AdfsVersion -MockWith { return $adfsVersionToTest }

                ($adfsRoleSTS, $adfsRoleProxy) | ForEach-Object {
                    Context "server role $_" {
                        $adfsRoleToTest = $_
                        Mock -CommandName Get-AdfsRole -MockWith { return "$adfsRoleToTest" }

                        It "should pass because Get-WinEvent returned null" {
                            # Arrange
                            Mock -CommandName Get-WinEvent -MockWith { return $null }

                            # Act
                            $ret = TestAdfsEventLogs

                            # Assert
                            $ret.Result | should beexactly Pass
                        }

                        It "should pass because Get-WinEvent returned an empty array" {
                            # Arrange
                            Mock -CommandName Get-WinEvent -MockWith { return @() }

                            # Act
                            $ret = TestAdfsEventLogs

                            # Assert
                            $ret.Result | should beexactly Pass
                        }

                        It "should fail" {
                            # Arrange
                            Mock -CommandName Get-WinEvent -MockWith {
                                return @(New-Object -TypeName PSObject -Property @{
                                        "TimeCreated"      = (Get-Date)
                                        "Id"               = 270
                                        "LevelDisplayName" = "Error"
                                        "Message"          = "The federation server proxy was not able to authenticate to the Federation Service."
                                    })
                            }

                            # Act
                            $ret = TestAdfsEventLogs

                            # Assert
                            $ret.Result | should beexactly Fail
                            $ret.Detail | should beexactly "There were events found in the AD FS event logs that may be causing issues with the AD FS and WAP trust. Check the output for more details."

                            $ret.Output.Events.Id | should beexactly 270
                            $ret.Output.Events.LevelDisplayName | should beexactly "Error"
                            $ret.Output.Events.Message | should beexactly "The federation server proxy was not able to authenticate to the Federation Service."
                        }
                    }
                }

                It "should error because of invalid server role." {
                    # Arrange
                    Mock -CommandName Get-AdfsRole -MockWith { return "none" }

                    # Act
                    $ret = TestAdfsEventLogs

                    # Assert
                    $ret.Result | should beexactly Error
                    $ret.Exceptionmessage | should beexactly "Unable to determine server role."
                    $ret.Exception | should beexactly "System.Management.Automation.RuntimeException: Unable to determine server role."
                }
            }
        }

        It "should error because invalid AD FS Version" {
            # Arrange
            Mock -CommandName Get-AdfsVersion -MockWith { return $null }

            # Act
            $ret = TestAdfsEventLogs

            # Assert
            $ret.Result | should beexactly Error
            $ret.ExceptionMessage | should beexactly "Unable to determine AD FS version."
            $ret.Exception | should beexactly "System.Management.Automation.RuntimeException: Unable to determine AD FS version."
        }
    }

    Describe "TestTimeSync" {
        Mock -CommandName Test-RunningRemotely -MockWith { return $false }

        Context "server role STS" {
            Mock -CommandName Get-AdfsRole -MockWith { return $adfsRoleSTS }

            Mock -CommandName Out-Warning -MockWith { }

            Context "only run locally" {
                It "should pass" {
                    # Arrange
                    Mock -CommandName IsServerTimeInSyncWithReliableTimeServer -MockWith { return $true }

                    # Act
                    $ret = TestTimeSync

                    # Assert
                    $ret.Result | should beexactly Pass
                }

                It "should fail" {
                    # Arrange
                    Mock -CommandName IsServerTimeInSyncWithReliableTimeServer -MockWith { return $false }

                    # Act
                    $ret = TestTimeSync

                    # Assert
                    $ret.Result | should beexactly Fail
                    $ret.Detail | should beexactly "This server's time is out of sync with reliable time server. Check and correct any time synchronization issues."
                }
            }

            Context "run farm-wide" {
                BeforeAll {
                    $adfsServers = @("sts1.contoso.com", "sts2.contoso.com", "sts3.contoso.com")

                    # since we have to mock out the remote PSSessions that gets created we just return the a PSSession to localhost
                    # we create these session before the actual test because once we mock New-PSSession we cannot unmock it
                    $localPSForPassTest = @()
                    $localPSForFailTest = @()

                    for ($i = 0; $i -lt $adfsServers.Count; $i++)
                    {
                        $localPSForPassTest += New-PSSession -ComputerName localhost -ErrorAction Stop
                        $localPSForFailTest += New-PSSession -ComputerName localhost -ErrorAction Stop
                    }
                }

                It "should pass" {
                    # Arrange
                    Mock -CommandName IsServerTimeInSyncWithReliableTimeServer -MockWith { return $true }
                    $script:itr = 0
                    Mock -CommandName New-PSSession -MockWith {
                        $session = $localPSForPassTest[$itr]
                        $script:itr += 1
                        return $session
                    }

                    # Since we get all of the functions from the private folder and run Invoke-Expression on them; that replaces the function's mock with the original function.
                    # We avoid this by setting the invoke expression within this script block to do nothing.
                    Mock Invoke-Command {
                        Return $ScriptBlock.InvokeWithContext(@{"Invoke-Expression" = {}}, @())
                    }

                    # Act
                    $ret = TestTimeSync -adfsServers $adfsServers

                    # Assert
                    $ret.Result | should beexactly Pass
                }

                It "should warn because it is unable to connect to remote server" {
                    # Arrange
                    Mock -CommandName IsServerTimeInSyncWithReliableTimeServer -MockWith { return $true }
                    Mock -CommandName New-PSSession -MockWith { return $null }

                    # Act
                    TestTimeSync -adfsServers $adfsServers

                    # Assert
                    Assert-MockCalled -CommandName Out-Warning -Times 3
                }

                It "should fail" {
                    # # Arrange
                    Mock -CommandName IsServerTimeInSyncWithReliableTimeServer -MockWith { return $false }
                    $script:itr = 0
                    Mock -CommandName New-PSSession -MockWith {
                        $session = $localPSForFailTest[$itr]
                        $script:itr += 1
                        return $session
                    }

                    # Since we get all of the functions from the private folder and run Invoke-Expression on them this replaces the function's mock with the original function.
                    # We avoid this by setting the invoke expression within this script block to do nothing.
                    Mock Invoke-Command {
                        Return $ScriptBlock.InvokeWithContext(@{"Invoke-Expression" = {}}, @())
                    }

                    # # Act
                    $ret = TestTimeSync -adfsServers $adfsServers

                    # Assert
                    $ret.Result | should beexactly Fail
                    ($adfsServers + "Localhost") | ForEach-Object {
                        $ret.Output.ServersOutOfSync | should contain $_
                    }
                }
            }
        }

        Context "server role Proxy" {
            Mock -CommandName Get-AdfsRole -MockWith {
                return $adfsRoleProxy
            }

            It "should pass" {
                # Arrange
                Mock -CommandName IsServerTimeInSyncWithReliableTimeServer -MockWith { return $true }

                # Act
                $ret = TestTimeSync

                # Assert
                $ret.Result | should beexactly Pass
            }

            It "should fail" {
                # Arrange
                Mock -CommandName IsServerTimeInSyncWithReliableTimeServer -MockWith { return $false }

                # Act
                $ret = TestTimeSync

                # Assert
                $ret.Result | should beexactly Fail
                $ret.Detail | should beexactly "This server's time is out of sync with reliable time server. Check and correct any time synchronization issues."
            }
        }

        It "should not run" {
            # Arrange
            Mock -CommandName Test-RunningRemotely -MockWith { return $true }

            # Act
            $ret = TestTimeSync

            # Assert
            $ret.Result | should beexactly NotRun
            $ret.Detail | should beexactly "This test does not need to run remotely."
        }

        It "should error" {
            # Arrange
            Mock -CommandName Get-AdfsRole -MockWith { return "none" }
            Mock -CommandName Test-RunningRemotely -MockWith { return $false }

            # Act
            $ret = TestTimeSync

            # Assert
            $ret.Result | should beexactly Error
            $ret.ExceptionMessage | should beexactly "Unable to determine server role."
            $ret.Exception | should beexactly "System.Management.Automation.RuntimeException: Unable to determine server role."
        }
    }
}