diagnosticsModule/Test/Private/AdfsHealthChecks.Test.ps1
# Determine our script root $parent = Split-Path $PSScriptRoot -Parent $script:root = Split-Path $parent -Parent # Load module via definition Import-Module $root\ADFSDiagnosticsModule.psm1 -Force InModuleScope ADFSDiagnosticsModule { # Shared constants $sharedError = "Error message" $sharedErrorException = "System.Management.Automation.RuntimeException: Error message" Describe "TestTrustedDevicesCertificateStore" { It "should pass" { # Arrange Mock -CommandName Get-Item -MockWith { return New-Object PSObject -Property @{ "StoreNames" = @{"AdfsTrustedDevices" = $true} }} # Act $ret = TestTrustedDevicesCertificateStore # Assert $ret.Result | should beexactly Pass } It "should fail" { # Arrange Mock -CommandName Get-Item -MockWith { return New-Object PSObject -Property @{ "StoreNames" = @{} }} # Act $ret = TestTrustedDevicesCertificateStore # Assert $ret.Result | should beexactly Fail $ret.Detail | should beexactly "The AdfsTrustedDevices certificate store does not exist." } It "should error" { Mock -CommandName Get-Item -MockWith { throw $sharedError } # Act $ret = TestTrustedDevicesCertificateStore # Assert $ret.Result | should beexactly Error $ret.ExceptionMessage | should beexactly $sharedError $ret.Exception | should beexactly $sharedErrorException } } Describe "TestAdfsPatches" { It "should pass" { # Arrange Mock -CommandName Get-OsVersion -MockWith { return [OSVersion]::WS2012R2 } Mock -CommandName Get-HotFix -MockWith { return $true } # Act $ret = TestAdfsPatches # Assert $ret.Result | should beexactly Pass } It "should fail" { # Arrange Mock -CommandName Get-OsVersion -MockWith { return [OSVersion]::WS2012R2 } Mock -CommandName Get-HotFix -MockWith { return $false } # Act $ret = TestAdfsPatches # Assert $ret.Result | should beexactly Fail $ret.Detail | should beexactly "There were missing patches that are not installed." $ret.Output.MissingAdfsPatches | should not benullorempty } It "should not run" { # Arrange Mock -CommandName Get-OsVersion -MockWith { return [OSVersion]::WS2016 } # Act $ret = TestAdfsPatches # Assert $ret.Result | should beexactly NotRun } It "should error" { # Arrange Mock -CommandName Get-OsVersion -MockWith { throw $sharedError } # Act $ret = TestAdfsPatches # Assert $ret.Result | should beexactly Error $ret.ExceptionMessage | should beexactly $sharedError $ret.Exception | should beexactly $sharedErrorException } } Describe "TestServicePrincipalName" { BeforeAll { $_upnServiceAccount = "aadcsvc@contoso.com" $_samServiceAccount = "contoso\aadcsvc" $_path = "CN=aadcsvc,CN=Managed Service Accounts,DC=contoso,DC=com" $_fullPath = "LDAP://$_path" $_incorrectLdapPath = "LDAP://CN=badAccount,CN=Managed Service Accounts,DC=contoso,DC=com" $_hostname = "sts.contoso.com" } Context "should pass" { BeforeAll { Mock -CommandName Test-RunningOnAdfsSecondaryServer -MockWith { return $false } Mock -CommandName IsLocalUser -MockWith { return $false } Mock -CommandName IsAdfsServiceRunning -MockWith { return $true } Mock -CommandName GetObjectsFromAD -MockWith { return New-Object PSObject -Property @{ "Path" = $_fullPath } } Mock -CommandName Retrieve-AdfsProperties -MockWith { return New-Object PSObject -Property @{ "Hostname" = $_hostname }} Mock -CommandName Invoke-Expression -MockWith { return @("Existing SPN found!", $_path) } -ParameterFilter { $Command -eq "setspn -f -q HOST/$_hostname"} Mock -CommandName Invoke-Expression -MockWith { return @("Existing SPN found!", $_path) } -ParameterFilter { $Command -eq "setspn -f -q HTTP/$_hostname"} } It "should pass when service account is in UPN format" { # Arrange Mock -CommandName Get-WmiObject -MockWith { return New-Object PSObject -Property @{ "StartName" = $_upnServiceAccount; "Name" = $adfsServiceName } } # Act $ret = TestServicePrincipalName # Assert $ret.Result | should beexactly Pass } It "should pass when service account is in SAM format" { # Arrange Mock -CommandName Get-WmiObject -MockWith { return New-Object PSObject -Property @{ "StartName" = $_samServiceAccount; "Name" = $adfsServiceName } } # Act $ret = TestServicePrincipalName # Assert $ret.Result | should beexactly Pass } It "should pass when no HTTP SPN is found" { # Arrange Mock -CommandName Invoke-Expression -MockWith { return @("No such SPN found.") } -ParameterFilter { $Command -eq "setspn -f -q HTTP/$_hostname"} Mock -CommandName Get-WmiObject -MockWith { return New-Object PSObject -Property @{ "StartName" = $_upnServiceAccount; "Name" = $adfsServiceName } } # Act $ret = TestServicePrincipalName # Assert $ret.Result | should beexactly Pass } } Context "should fail" { BeforeAll { Mock -CommandName Test-RunningOnAdfsSecondaryServer -MockWith { return $false } Mock -CommandName IsLocalUser -MockWith { return $false } Mock -CommandName IsAdfsServiceRunning -MockWith { return $true } Mock -CommandName GetObjectsFromAD -MockWith { return New-Object PSObject -Property @{ "Path" = $_fullPath } } Mock -CommandName Retrieve-AdfsProperties -MockWith { return New-Object PSObject -Property @{ "Hostname" = $_hostname }} Mock -CommandName Get-WmiObject -MockWith { return New-Object PSObject -Property @{ "StartName" = $_upnServiceAccount; "Name" = $adfsServiceName } } } It "when no HOST SPN is found" { # Arrange Mock -CommandName Invoke-Expression -MockWith { return ("No such SPN found.") } -ParameterFilter { $Command -eq "setspn -f -q HOST/$_hostname"} # Act $ret = TestServicePrincipalName # Assert $ret.Result | should beexactly Fail $ret.Detail | should beexactly "No such SPN was found for $_hostname" } It "when HOST SPN resolved service account does not match" { # Arrange Mock -CommandName Invoke-Expression -MockWith { return ("Existing SPN found!" + [Environment]::NewLine + "$_incorrectLdapPath") } -ParameterFilter { $Command -eq "setspn -f -q HOST/$_hostname"} # Act $ret = TestServicePrincipalName # Assert $ret.Result | should beexactly Fail $ret.Detail | should beexactly "An existing SPN was found for HOST/$_hostname but it did not resolve to the ADFS service account." } It "when HTTP SPN resolved service account does not match" { # Arrange Mock -CommandName Invoke-Expression -MockWith { return ("Existing SPN found!" + [Environment]::NewLine + "$_path") } -ParameterFilter { $Command -eq "setspn -f -q HOST/$_hostname"} Mock -CommandName Invoke-Expression -MockWith { return ("Existing SPN found!" + [Environment]::NewLine + "$_incorrectLdapPath") } -ParameterFilter { $Command -eq "setspn -f -q HTTP/$_hostname"} # Act $ret = TestServicePrincipalName # Assert $ret.Result | should beexactly Fail $ret.Detail | should beexactly "An existing SPN was found for HTTP/$_hostname but it did not resolve to the ADFS service account." } } Context "should not run" { It "when on secondary server" { # Arrange Mock -CommandName Test-RunningOnAdfsSecondaryServer -MockWith { return $true } # Act $ret = TestServicePrincipalName # Assert $ret.Result | should beexactly NotRun $ret.Detail | should beexactly "This check runs only on Primary Nodes." } It "when local user" { # Arrange Mock -CommandName Test-RunningOnAdfsSecondaryServer -MockWith { return $false } Mock -CommandName IsLocalUser -MockWith { return $true } # Act $ret = TestServicePrincipalName # Assert $ret.Result | should beexactly NotRun $ret.Detail | should beexactly "Current user $env:USERNAME is not a domain account. Cannot execute this test" } It "when AD FS is not running" { # Arrange Mock -CommandName Test-RunningOnAdfsSecondaryServer -MockWith { return $false } Mock -CommandName IsLocalUser -MockWith { return $false } Mock -CommandName IsAdfsServiceRunning -MockWith { return $false } # Act $ret = TestServicePrincipalName # Assert $ret.Result | should beexactly NotRun $ret.Detail | should beexactly "AD FS service is not running" } } Context "should error" { BeforeAll { Mock -CommandName Test-RunningOnAdfsSecondaryServer -MockWith { return $false } Mock -CommandName IsLocalUser -MockWith { return $false } Mock -CommandName IsAdfsServiceRunning -MockWith { return $true } } It "when service account is empty" { # Arrange Mock -CommandName Get-WmiObject -MockWith { return New-Object PSObject -Property @{"Name" = $adfsServiceName; "StartName" = $null}} # Act $ret = TestServicePrincipalName # Assert $ret.Result | should beexactly Error $ret.ExceptionMessage | should beexactly "ADFS Service account is null or empty. The WMI configuration is in an inconsistent state" $ret.Exception | should beexactly "System.Management.Automation.RuntimeException: ADFS Service account is null or empty. The WMI configuration is in an inconsistent state" } It "when service account is not in expected SAM format" { # Arrange Mock -CommandName Get-WmiObject -MockWith { return New-Object PSObject -Property @{"Name" = $adfsServiceName; "StartName" = "badAccount"}} # Act $ret = TestServicePrincipalName # Assert $ret.Result | should beexactly Error $ret.ExceptionMessage | should beexactly "Unexpected value of the service account badAccount. Expected in DOMAIN\\User format" $ret.Exception | should beexactly "System.Management.Automation.RuntimeException: Unexpected value of the service account badAccount. Expected in DOMAIN\\User format" } } } Describe "TestProxyTrustPropagation" { BeforeAll { $_adfsServers = @("sts1.contoso.com", "sts2.contoso.com", "sts3.contoso.com") $_primaryCertificates = @("Cert1", "Cert2", "Cert3") $_missingCertificates = @("Cert2", "Cert3") # 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 Test-RunningOnAdfsSecondaryServer -MockWith { return $false } Mock -CommandName GetCertificatesFromAdfsTrustedDevices -MockWith { return $_primaryCertificates } $script:itr = 0 Mock -CommandName New-PSSession -MockWith { $session = $localPSForPassTest[$script: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" = {}; "VerifyCertificatesArePresent" = { return @() }}, @()) } # Act $ret = TestProxyTrustPropagation $_adfsServers # Assert $ret.Result | should beexactly Pass } It "should warn because no AD FS farm information was provided" { # Arrange Mock -CommandName Test-RunningOnAdfsSecondaryServer -MockWith { return $false } Mock -CommandName Out-Warning -MockWith { } # Act $ret = TestProxyTrustPropagation # Assert Assert-MockCalled Out-Warning $ret.Result | should beexactly Warning $ret.Detail | should beexactly "No AD FS farm information was provided. Specify the list of servers in your farm using the -adfsServers flag." } It "should warn because it cannot connect to an AD FS server" { # Arrange Mock -CommandName Test-RunningOnAdfsSecondaryServer -MockWith { return $false } Mock -CommandName GetCertificatesFromAdfsTrustedDevices -MockWith { return $_primaryCertificates } Mock -CommandName New-PSSession -MockWith { $null } Mock -CommandName Out-Warning -MockWith { } # Act $ret = TestProxyTrustPropagation $_adfsServers # Assert Assert-MockCalled Out-Warning 3 } It "should fail" { # Arrange Mock -CommandName Test-RunningOnAdfsSecondaryServer -MockWith { return $false } Mock -CommandName GetCertificatesFromAdfsTrustedDevices -MockWith { return $_primaryCertificates } $script:itr = 0 Mock -CommandName New-PSSession -MockWith { $session = $localPSForPassTest[$script: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" = {}; "VerifyCertificatesArePresent" = { return $_missingCertificates }}, @()) } # Act $ret = TestProxyTrustPropagation $_adfsServers # Assert $ret.Result | should beexactly Fail $ret.Detail | should beexactly "There were missing certificates on some of the secondary servers. There may be an issue with proxy trust propogation."\ $_adfsServers | ForEach-Object { $server = $_ $_missingCertificates | ForEach-Object { $ret.Output.ErroneousCertificates[$server] | should contain $_ } } } It "should not run when on secondary server" { # Arrange Mock -CommandName Test-RunningOnAdfsSecondaryServer -MockWith { return $true } # Act $ret = TestProxyTrustPropagation # Assert $ret.Result | should beexactly NotRun $ret.Detail | should beexactly "This check runs only on Primary Nodes." } It "should error" { # Arrange Mock -CommandName Test-RunningOnAdfsSecondaryServer -MockWith { throw $sharedError } # Act $ret = TestProxyTrustPropagation # Assert $ret.Result | should beexactly Error $ret.ExceptionMessage | should beexactly $sharedError $ret.Exception | should beexactly $sharedErrorException } } } |