From 5cfe41c456c3db69172c4b2b8e9621bcd1fa9218 Mon Sep 17 00:00:00 2001 From: Rob Sewell Date: Wed, 10 May 2023 17:47:27 +0000 Subject: [PATCH 1/6] because we cant use if in the tests file --- containers/JessAndBeard.psm1 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/containers/JessAndBeard.psm1 b/containers/JessAndBeard.psm1 index 6c613dde..e37f5eec 100644 --- a/containers/JessAndBeard.psm1 +++ b/containers/JessAndBeard.psm1 @@ -2343,13 +2343,21 @@ The Tags are the same" PassedChange = 0 # + or - the number of tests passed for v5 FailedChange = -12 # + or - the number of tests failed for v5 SkippedChange = 0 # + or - the number of tests skipped for v5 - } + }, @{ Name = 'LinkedServerConnection' RunChange = -3 # + or - the number of tests for v5 PassedChange = -3 # + or - the number of tests passed for v5 FailedChange = 0 # + or - the number of tests failed for v5 SkippedChange = 0 # + or - the number of tests skipped for v5 + }, + @{ + Name = 'SupportedBuild' + RunChange = -3 # + or - the number of tests run for v5 + PassedChange = -3 # + or - the number of tests passed for v5 + FailedChange = 0 # + or - the number of tests failed for v5 + SkippedChange = 0 # + or - the number of tests skipped for v5 + } ) $runchange = 0 From be468acf27ede7571898928eb2bb86729de610db Mon Sep 17 00:00:00 2001 From: Rob Sewell Date: Wed, 10 May 2023 17:47:53 +0000 Subject: [PATCH 2/6] so we can check supported buildand suspectpagelimit #882 --- developing/Robs-Instance.ps1 | 2 + source/checks/Instancev5.Tests.ps1 | 23 +++++ .../internal/configurations/configuration.ps1 | 1 + .../functions/NewGet-AllInstanceInfo.ps1 | 97 ++++++++++++++----- 4 files changed, 98 insertions(+), 25 deletions(-) diff --git a/developing/Robs-Instance.ps1 b/developing/Robs-Instance.ps1 index ce81fba5..39777bf5 100644 --- a/developing/Robs-Instance.ps1 +++ b/developing/Robs-Instance.ps1 @@ -14,6 +14,8 @@ $Checks = 'HideInstance' $Checks = 'LoginAuditFailed' $Checks = 'LoginAuditSuccessful' $Checks = 'LoginCheckPolicy' +$Checks = 'SuspectPageLimit' +$Checks = 'SupportedBuild' $Checks = 'LoginAuditSuccessful', 'LoginAuditFailed' Invoke-PerfAndValidateCheck -Checks $Checks diff --git a/source/checks/Instancev5.Tests.ps1 b/source/checks/Instancev5.Tests.ps1 index 749f4fbf..e27554e2 100644 --- a/source/checks/Instancev5.Tests.ps1 +++ b/source/checks/Instancev5.Tests.ps1 @@ -375,6 +375,29 @@ Describe "SQL Mail XPs Disabled" -Tag SQLMailXPsDisabled, Security, CIS, Low, In } } +Describe "Supported Build" -Tag SupportedBuild, DISA, High, Instance -ForEach $InstancesToTest { + $skip = ($__dbcconfig | Where-Object { $_.Name -eq 'skip.instance.SupportedBuild' }).Value + Context "Checking that build is still supported by Microsoft for <_.Name>" -Skip:$skip { + It "The build is not behind the latest build by more than <_.SupportedBuild.BuildBehind> for <_.Name>" { + $psItem.SupportedBuild.Compliant | Should -BeTrue -Because "this build $($psItem.SupportedBuild.Build) should not be behind the required build" + } + It "The build is supported by Microsoft for <_.Name>" { + $psItem.SupportedBuild.InsideMicrosoftSupport | Should -BeTrue -Because "this build $($psItem.SupportedBuild.Build) is now unsupported by Microsoft" + } + It "The build is supported by Microsoft within the warning window of <_.SupportedBuild.BuildWarning> months for <_.Name>" { + $psItem.SupportedBuild.InsideBuildWarning | Should -BeTrue -Because "this build $($psItem.SupportedBuild.Build) will be unsupported by Microsoft on $($psItem.SupportedBuild.SupportedUntil) which is less than $($psItem.SupportedBuild.BuildWarning) months away" + } + } +} + +Describe "Suspect Page Limit Nearing" -Tag SuspectPageLimit, Medium, Instance -ForEach $InstancesToTest { + $skip = ($__dbcconfig | Where-Object { $_.Name -eq 'skip.instance.suspectpagelimit' }).Value + Context "Testing if the suspect_pages table is nearing the limit of 1000 rows on on <_.Name>" { + It "The suspect_pages table in msdb shouldn't be nearing the limit of 1000 rows on on <_.Name>" -Skip:$skip { + $PSItem.SuspectPageCountResult | Should -BeTrue -Because "The suspect_pages table in msdb shouldn't be nearing the limit of 1000 rows" + } + } +} Describe "Trace Flags Expected" -Tag TraceFlagsExpected, TraceFlag, High, Instance -ForEach $InstancesToTest { $skip = ($__dbcconfig | Where-Object { $_.Name -eq 'skip.instance.TraceFlagsExpected' }).Value Context "Testing Expected Trace Flags on <_.Name>" { diff --git a/source/internal/configurations/configuration.ps1 b/source/internal/configurations/configuration.ps1 index 7c62a398..09453b0f 100644 --- a/source/internal/configurations/configuration.ps1 +++ b/source/internal/configurations/configuration.ps1 @@ -288,6 +288,7 @@ Set-PSFConfig -Module dbachecks -Name skip.instance.linkedserverconnection -Vali Set-PSFConfig -Module dbachecks -Name skip.instance.maxmemory -Validation bool -Value $false -Initialize -Description "Skip the check for max memory" Set-PSFConfig -Module dbachecks -Name skip.instance.orphanedfile -Validation bool -Value $false -Initialize -Description "Skip the check for orphaned file" Set-PSFConfig -Module dbachecks -Name skip.instance.servernamematch -Validation bool -Value $false -Initialize -Description "Skip the check for server name match" +Set-PSFConfig -Module dbachecks -Name skip.instance.supportedbuild -Validation bool -Value $false -Initialize -Description "Skip the checks for supported build" diff --git a/source/internal/functions/NewGet-AllInstanceInfo.ps1 b/source/internal/functions/NewGet-AllInstanceInfo.ps1 index f525ef8b..0f10de12 100644 --- a/source/internal/functions/NewGet-AllInstanceInfo.ps1 +++ b/source/internal/functions/NewGet-AllInstanceInfo.ps1 @@ -416,26 +416,71 @@ function NewGet-AllInstanceInfo { $Instance.SetDefaultInitFields([Microsoft.SqlServer.Management.Smo.Settings], $LoginInitFields) } + 'SuspectPageLimit' { + $sql = "Select + COUNT(file_id) as 'SuspectPageCount' + from msdb.dbo.suspect_pages" + $SuspectPageCountResult = (($Instance.Query($sql).SuspectPageCount / 1000) * 100 ) -lt ($__dbcconfig | Where-Object { $_.Name -eq 'policy.suspectpage.threshold' }).Value + } + + 'SupportedBuild' { + $BuildWarning = ($__dbcconfig | Where-Object { $_.Name -eq 'policy.build.warningwindow' }).Value + $BuildBehind = ($__dbcconfig | Where-Object { $_.Name -eq 'policy.build.behind' }).Value + $Date = Get-Date + #If $BuildBehind check against SP/CU parameter to determine validity of the build + if ($BuildBehind) { + $buildBehindResults = Test-DbaBuild -SqlInstance $Instance -SqlCredential $sqlcredential -MaxBehind $BuildBehind + $Compliant = $buildBehindResults.Compliant + + #If no $BuildBehind only check against support dates + } else { + $Compliant = $true + } + + $Results = Test-DbaBuild -SqlInstance $Instance -SqlCredential $sqlcredential -Latest + [DateTime]$SupportedUntil = Get-Date $results.SupportedUntil -Format O + $Build = $results.build + #If $BuildWarning, check for support date within the warning window + if ($BuildWarning) { + [DateTime]$expected = Get-Date ($Date).AddMonths($BuildWarning) -Format O + $SupportedUntil | Should -BeGreaterThan $expected -Because "this build $Build will be unsupported by Microsoft on $(Get-Date $SupportedUntil -Format O) which is less than $BuildWarning months away" + } else { + #If neither, check for Microsoft support date + $SupportedUntil | Should -BeGreaterThan $Date -Because "this build $Build is now unsupported by Microsoft" + } + + $SupportedBuild = [pscustomobject]@{ + BuildBehind = $BuildBehind + Compliant = $Compliant + Build = $Build + SupportedUntil = $SupportedUntil + Expected = $expected + BuildWarning = $BuildWarning + InsideBuildWarning = $SupportedUntil -gt $expected + InsideMicrosoftSupport = $SupportedUntil -gt $Date + } + } + Default { } } #build the object $testInstanceObject = [PSCustomObject]@{ - ComputerName = $Instance.ComputerName - InstanceName = $Instance.DbaInstanceName - Name = $Instance.Name - ConfigValues = $ConfigValues - VersionMajor = $Instance.VersionMajor - Configuration = if ($configurations) { $Instance.Configuration } else { $null } - Settings = $Instance.Settings - Logins = $Instance.Logins - Databases = $Instance.Databases - NumberOfLogFiles = $Instance.NumberOfLogFiles - MaxDopSettings = $MaxDopSettings - ExpectedTraceFlags = $ExpectedTraceFlags - NotExpectedTraceFlags = $NotExpectedTraceFlags - XESessions = [pscustomobject]@{ + ComputerName = $Instance.ComputerName + InstanceName = $Instance.DbaInstanceName + Name = $Instance.Name + ConfigValues = $ConfigValues + VersionMajor = $Instance.VersionMajor + Configuration = if ($configurations) { $Instance.Configuration } else { $null } + Settings = $Instance.Settings + Logins = $Instance.Logins + Databases = $Instance.Databases + NumberOfLogFiles = $Instance.NumberOfLogFiles + MaxDopSettings = $MaxDopSettings + ExpectedTraceFlags = $ExpectedTraceFlags + NotExpectedTraceFlags = $NotExpectedTraceFlags + XESessions = [pscustomobject]@{ RequiredStopped = $RequiredStopped.ForEach{ [pscustomobject]@{ Name = $Instance.Name @@ -470,23 +515,23 @@ function NewGet-AllInstanceInfo { Sessions = $Sessions Running = $RunningSessions } - ErrorLogEntries = [pscustomobject]@{ + ErrorLogEntries = [pscustomobject]@{ errorLogCount = $ErrorLogCount logWindow = $logWindow } - InstanceConnection = $InstanceConnection - BackupPathAccess = [pscustomobject]@{ + InstanceConnection = $InstanceConnection + BackupPathAccess = [pscustomobject]@{ Result = $BackupPathAccess BackupPath = $BackupPath } - LatestBuild = [PSCustomObject]@{ + LatestBuild = [PSCustomObject]@{ Compliant = $LatestBuild.Compliant } - NetworkLatency = [PSCustomObject]@{ + NetworkLatency = [PSCustomObject]@{ Latency = $Latency Threshold = $NetworkThreshold } - LinkedServerResults = if ($LinkedServerResults) { + LinkedServerResults = if ($LinkedServerResults) { $LinkedServerResults.ForEach{ [pscustomobject]@{ InstanceName = $Instance.Name @@ -505,17 +550,19 @@ function NewGet-AllInstanceInfo { Result = 'None' } } - MaxMemory = $MaxMemory - OrphanedFile = [pscustomobject]@{ + MaxMemory = $MaxMemory + OrphanedFile = [pscustomobject]@{ FileCount = $FileCount } - ServerNameMatch = [pscustomobject]@{ + ServerNameMatch = [pscustomobject]@{ configuredServerName = $ServerNameMatchconfiguredServerName netName = $ServerNameMatchnetName renamerequired = $ServerNameMatchrenamerequired } - MemoryDump = $Dump - HideInstance = $HideInstance + MemoryDump = $Dump + HideInstance = $HideInstance + SuspectPageCountResult = $SuspectPageCountResult + SupportedBuild = $SupportedBuild # TempDbConfig = [PSCustomObject]@{ # TF118EnabledCurrent = $tempDBTest[0].CurrentSetting # TF118EnabledRecommended = $tempDBTest[0].Recommended From a46e6b2e59a146741659260312d3ac5db1e05940 Mon Sep 17 00:00:00 2001 From: Rob Sewell Date: Fri, 12 May 2023 09:17:34 +0000 Subject: [PATCH 3/6] needs to be the right reference #882 --- source/checks/Instancev5.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/checks/Instancev5.Tests.ps1 b/source/checks/Instancev5.Tests.ps1 index e27554e2..fb1a2f82 100644 --- a/source/checks/Instancev5.Tests.ps1 +++ b/source/checks/Instancev5.Tests.ps1 @@ -220,8 +220,8 @@ Describe "Successful Login Auditing" -Tag LoginAuditSuccessful, Security, CIS, M Describe "Login Check Policy" -Tag LoginCheckPolicy, Security, CIS, Medium, Instance -ForEach $InstancesToTest { $skip = ($__dbcconfig | Where-Object { $_.Name -eq 'skip.security.LoginCheckPolicy' }).Value - Context "Testing if the CHECK_POLICY is enabled on all logins on $psitem" { - It "All logins should have the CHECK_POLICY option set to ON on $psitem" -Skip:$skip { + Context "Testing if the CHECK_POLICY is enabled on all logins on <_.Name>" { + It "All logins should have the CHECK_POLICY option set to ON on <_.Name>" -Skip:$skip { ($psitem.logins | Where-Object { $_.LoginType -eq 'SqlLogin' -and $_.PasswordPolicyEnforced -eq $false -and $_.IsDisabled -eq $false }).Count | Should -Be 0 -Because "We expected the CHECK_POLICY for the all logins to be enabled" } } From 2b6c37d23c7319a91331ece13470bc5413f38c7f Mon Sep 17 00:00:00 2001 From: Rob Sewell Date: Fri, 12 May 2023 09:32:56 +0000 Subject: [PATCH 4/6] that was never going to work --- .../assertions/Instance.Assertions.ps1 | 220 ++++++------------ 1 file changed, 77 insertions(+), 143 deletions(-) diff --git a/source/internal/assertions/Instance.Assertions.ps1 b/source/internal/assertions/Instance.Assertions.ps1 index 35deaf4d..9e63d64a 100644 --- a/source/internal/assertions/Instance.Assertions.ps1 +++ b/source/internal/assertions/Instance.Assertions.ps1 @@ -112,8 +112,7 @@ function Get-AllInstanceInfo { Text = $Psitem.Text } | Where-Object { $psitem.LogDate -gt (Get-Date).AddDays( - $LogWindow) } } - } - catch { + } catch { $There = $false $ErrorLog = [PSCustomObject]@{ LogDate = 'Do not know the Date' @@ -122,8 +121,7 @@ function Get-AllInstanceInfo { InstanceName = 'An Error occurred ' + $Instance } } - } - else { + } else { $There = $false $ErrorLog = [PSCustomObject]@{ LogDate = 'Do not know the Date' @@ -140,15 +138,13 @@ function Get-AllInstanceInfo { $DefaultTrace = [pscustomobject] @{ ConfiguredValue = $SpConfig.ConfiguredValue } - } - catch { + } catch { $There = $false $DefaultTrace = [pscustomobject] @{ ConfiguredValue = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $DefaultTrace = [pscustomobject] @{ ConfiguredValue = 'We Could not Connect to $Instance' @@ -163,15 +159,13 @@ function Get-AllInstanceInfo { $OleAutomationProceduresDisabled = [pscustomobject] @{ ConfiguredValue = $SpConfig.ConfiguredValue } - } - catch { + } catch { $There = $false $OleAutomationProceduresDisabled = [pscustomobject] @{ ConfiguredValue = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $OleAutomationProceduresDisabled = [pscustomobject] @{ ConfiguredValue = 'We Could not Connect to $Instance' @@ -186,15 +180,13 @@ function Get-AllInstanceInfo { $CrossDBOwnershipChaining = [pscustomobject] @{ ConfiguredValue = $SpConfig.ConfiguredValue } - } - catch { + } catch { $There = $false $CrossDBOwnershipChaining = [pscustomobject] @{ ConfiguredValue = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $CrossDBOwnershipChaining = [pscustomobject] @{ ConfiguredValue = 'We Could not Connect to $Instance' @@ -216,23 +208,20 @@ function Get-AllInstanceInfo { if ($null -eq $results) { $Value = 0 - } - else { + } else { $Value = $SpConfig.ConfiguredValue } $ScanForStartupProceduresDisabled = [pscustomobject] @{ ConfiguredValue = $Value } - } - catch { + } catch { $There = $false $ScanForStartupProceduresDisabled = [pscustomobject] @{ ConfiguredValue = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $ScanForStartupProceduresDisabled = [pscustomobject] @{ ConfiguredValue = 'We Could not Connect to $Instance' @@ -245,8 +234,7 @@ function Get-AllInstanceInfo { $daystocheck = Get-DbcConfigValue policy.instance.memorydumpsdaystocheck if ($null -eq $daystocheck) { $datetocheckfrom = '0001-01-01' - } - else { + } else { $datetocheckfrom = (Get-Date).ToUniversalTime().AddDays( - $daystocheck ) } $MaxDump = [pscustomobject] @{ @@ -254,15 +242,13 @@ function Get-AllInstanceInfo { # Skip on the it will show in the results Count = (@(Get-DbaDump -SqlInstance $Instance -WarningAction SilentlyContinue).Where{ $_.CreationTime -gt $datetocheckfrom }).Count } - } - catch { + } catch { $There = $false $MaxDump = [pscustomobject] @{ Count = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $MaxDump = [pscustomobject] @{ Count = 'We Could not Connect to $Instance' @@ -277,15 +263,13 @@ function Get-AllInstanceInfo { $RemoteAccessDisabled = [pscustomobject] @{ ConfiguredValue = $SpConfig.ConfiguredValue } - } - catch { + } catch { $There = $false $RemoteAccessDisabled = [pscustomobject] @{ ConfiguredValue = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $RemoteAccessDisabled = [pscustomobject] @{ ConfiguredValue = 'We Could not Connect to $Instance' @@ -300,15 +284,13 @@ function Get-AllInstanceInfo { $LatestBuild = [pscustomobject] @{ Compliant = $results.Compliant } - } - catch { + } catch { $There = $false $LatestBuild = [pscustomobject] @{ Compliant = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $LatestBuild = [pscustomobject] @{ Compliant = 'We Could not Connect to $Instance' @@ -319,19 +301,17 @@ function Get-AllInstanceInfo { if ($There) { try { #This needs to be done in query just in case the account had already been renamed - $login = Get-DbaLogin -SqlInstance $Instance | Where-Object Id -eq 1 + $login = Get-DbaLogin -SqlInstance $Instance | Where-Object Id -EQ 1 $SaDisabled = [pscustomobject] @{ Disabled = $login.IsDisabled } - } - catch { + } catch { $There = $false $SaDisabled = [pscustomobject] @{ Disabled = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $SaDisabled = [pscustomobject] @{ Disabled = 'We Could not Connect to $Instance' @@ -344,15 +324,13 @@ function Get-AllInstanceInfo { $SaExist = [pscustomobject] @{ Exist = @(Get-DbaLogin -SqlInstance $Instance -Login sa).Count } - } - catch { + } catch { $There = $false $SaExist = [pscustomobject] @{ Exist = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $SaExist = [pscustomobject] @{ Exist = 'We Could not Connect to $Instance' @@ -371,16 +349,14 @@ function Get-AllInstanceInfo { State = $SqlEngineService.State StartType = $SqlEngineService.StartMode } - } - catch { + } catch { $There = $false $EngineService = [pscustomobject] @{ State = 'We Could not Connect to $Instance $ComputerName , $InstanceName from catch' StartType = 'We Could not Connect to $Instance $ComputerName , $InstanceName from catch' } } - } - else { + } else { $There = $false $EngineService = [pscustomobject] @{ State = 'We Could not Connect to $Instance' @@ -407,15 +383,13 @@ function Get-AllInstanceInfo { $PublicRolePermission = [pscustomobject] @{ Count = $results.RowCount } - } - catch { + } catch { $There = $false $PublicRolePermission = [pscustomobject] @{ Count = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $PublicRolePermission = [pscustomobject] @{ Count = 'We Could not Connect to $Instance' @@ -428,15 +402,13 @@ function Get-AllInstanceInfo { $BuiltInAdmin = [pscustomobject] @{ Exist = @(Get-DbaLogin -SqlInstance $Instance -Login "BUILTIN\Administrators").Count } - } - catch { + } catch { $There = $false $BuiltInAdmin = [pscustomobject] @{ Exist = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $BuiltInAdmin = [pscustomobject] @{ Exist = 'We Could not Connect to $Instance' @@ -455,21 +427,18 @@ function Get-AllInstanceInfo { $LocalWindowsGroup = [pscustomobject] @{ Exist = $true } - } - else { + } else { $LocalWindowsGroup = [pscustomobject] @{ Exist = $false } } - } - catch { + } catch { $There = $false $LocalWindowsGroup = [pscustomobject] @{ Exist = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $LocalWindowsGroup = [pscustomobject] @{ Exist = 'We Could not Connect to $Instance' @@ -479,19 +448,17 @@ function Get-AllInstanceInfo { 'LoginAuditFailed' { if ($There) { try { - $results = Get-DbaInstanceProperty -SQLInstance $instance -InstanceProperty AuditLevel + $results = Get-DbaInstanceProperty -SqlInstance $instance -InstanceProperty AuditLevel $LoginAuditFailed = [pscustomobject] @{ AuditLevel = $results.Value } - } - catch { + } catch { $There = $false $LoginAuditFailed = [pscustomobject] @{ AuditLevel = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $LoginAuditFailed = [pscustomobject] @{ AuditLevel = 'We Could not Connect to $Instance' @@ -502,19 +469,17 @@ function Get-AllInstanceInfo { 'LoginAuditSuccessful' { if ($There) { try { - $results = Get-DbaInstanceProperty -SQLInstance $instance -InstanceProperty AuditLevel + $results = Get-DbaInstanceProperty -SqlInstance $instance -InstanceProperty AuditLevel $LoginAuditSuccessful = [pscustomobject] @{ AuditLevel = $results.Value } - } - catch { + } catch { $There = $false $LoginAuditSuccessful = [pscustomobject] @{ AuditLevel = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $LoginAuditSuccessful = [pscustomobject] @{ AuditLevel = 'We Could not Connect to $Instance' @@ -537,8 +502,7 @@ function Get-AllInstanceInfo { $SqlAgentProxiesWithPublicRole += $SqlAgentProxyWithPublicRole } } - } - catch { + } catch { $There = $false $SqlAgentProxiesWithPublicRole = [pscustomobject] @{ Name = 'We Could not Connect to $Instance' @@ -546,8 +510,7 @@ function Get-AllInstanceInfo { CredentialIdentity = $null } } - } - else { + } else { $There = $false $SqlAgentProxiesWithPublicRole = [pscustomobject] @{ Name = 'We Could not Connect to $Instance' @@ -564,15 +527,13 @@ function Get-AllInstanceInfo { $HideInstance = [pscustomobject] @{ HideInstance = $results.HideInstance } - } - catch { + } catch { $There = $false $HideInstance = [pscustomobject] @{ HideInstance = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $HideInstance = [pscustomobject] @{ HideInstance = 'We Could not Connect to $Instance' @@ -586,8 +547,7 @@ function Get-AllInstanceInfo { $EngineServiceAdmin = [pscustomobject] @{ Exist = 'We Cant Check running on Linux' } - } - else { + } else { try { $ComputerName , $InstanceName = $Instance.Name.Split('\') if ($null -eq $InstanceName) { @@ -599,28 +559,24 @@ function Get-AllInstanceInfo { $EngineServiceAdmin = [pscustomobject] @{ Exist = $localAdmins.Name.Contains($SqlEngineService.StartName) } - } - catch [System.Exception] { + } catch [System.Exception] { if ($_.Exception.Message -like '*No services found in relevant namespaces*') { $EngineServiceAdmin = [pscustomobject] @{ Exist = $false } - } - else { + } else { $EngineServiceAdmin = [pscustomobject] @{ Exist = 'Some sort of failure' } } - } - catch { + } catch { $There = $false $EngineServiceAdmin = [pscustomobject] @{ Exist = 'We Could not Connect to $Instance $ComputerName , $InstanceName from catch' } } } - } - else { + } else { $There = $false $EngineServiceAdmin = [pscustomobject] @{ Exist = 'We Could not Connect to $Instance' @@ -634,8 +590,7 @@ function Get-AllInstanceInfo { $AgentServiceAdmin = [pscustomobject] @{ Exist = 'We Cant Check running on Linux' } - } - else { + } else { try { $ComputerName , $InstanceName = $Instance.Name.Split('\') if ($null -eq $InstanceName) { @@ -647,28 +602,24 @@ function Get-AllInstanceInfo { $AgentServiceAdmin = [pscustomobject] @{ Exist = $localAdmins.Name.Contains($SqlAgentService.StartName) } - } - catch [System.Exception] { + } catch [System.Exception] { if ($_.Exception.Message -like '*No services found in relevant namespaces*') { $AgentServiceAdmin = [pscustomobject] @{ Exist = $false } - } - else { + } else { $AgentServiceAdmin = [pscustomobject] @{ Exist = 'Some sort of failure' } } - } - catch { + } catch { $There = $false $AgentServiceAdmin = [pscustomobject] @{ Exist = 'We Could not Connect to $Instance $ComputerName , $InstanceName from catch' } } } - } - else { + } else { $There = $false $AgentServiceAdmin = [pscustomobject] @{ Exist = 'We Could not Connect to $Instance' @@ -682,8 +633,7 @@ function Get-AllInstanceInfo { $FullTextServiceAdmin = [pscustomobject] @{ Exist = 'We Cant Check running on Linux' } - } - else { + } else { try { $ComputerName , $InstanceName = $Instance.Name.Split('\') if ($null -eq $InstanceName) { @@ -694,20 +644,17 @@ function Get-AllInstanceInfo { $FullTextServiceAdmin = [pscustomobject] @{ Exist = $localAdmins.Name.Contains($SqlFullTextService.StartName) } - } - catch [System.Exception] { + } catch [System.Exception] { if ($_.Exception.Message -like '*No services found in relevant namespaces*') { $FullTextServiceAdmin = [pscustomobject] @{ Exist = $false } - } - else { + } else { $FullTextServiceAdmin = [pscustomobject] @{ Exist = 'Some sort of failure' } } - } - catch { + } catch { $There = $false $FullTextServiceAdmin = [pscustomobject] @{ Exist = "We Could not Connect to $Instance $ComputerName , $InstanceName from catch" @@ -715,8 +662,7 @@ function Get-AllInstanceInfo { } } - } - else { + } else { $There = $false $FullTextServiceAdmin = [pscustomobject] @{ Exist = 'We Could not Connect to $Instance' @@ -728,17 +674,15 @@ function Get-AllInstanceInfo { if ($There) { try { $LoginCheckPolicy = [pscustomobject] @{ - Count = @(Get-DbaLogin -SQLInstance $instance -Type SQL | Where-Object { $_.PasswordPolicyEnforced -eq $false -and $_.IsDisabled -eq $false }).Count + Count = @(Get-DbaLogin -SqlInstance $instance -Type SQL | Where-Object { $_.PasswordPolicyEnforced -eq $false -and $_.IsDisabled -eq $false }).Count } - } - catch { + } catch { $There = $false $LoginCheckPolicy = [pscustomobject] @{ Count = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $LoginCheckPolicy = [pscustomobject] @{ Count = 'We Could not Connect to $Instance' @@ -749,20 +693,18 @@ function Get-AllInstanceInfo { 'LoginPasswordExpiration' { if ($There) { try { - $role = Get-DbaServerRole -SQLInstance $instance -ServerRole "sysadmin" + $role = Get-DbaServerRole -SqlInstance $instance -ServerRole "sysadmin" $LoginPasswordExpiration = [pscustomobject] @{ - Count = @(Get-DbaLogin -SQLInstance $instance -Login @($role.Login) -Type SQL | Where-Object { $_.PasswordExpirationEnabled -eq $false -and $_.IsDisabled -eq $false }).Count + Count = @(Get-DbaLogin -SqlInstance $instance -Login @($role.Login) -Type SQL | Where-Object { $_.PasswordExpirationEnabled -eq $false -and $_.IsDisabled -eq $false }).Count } - } - catch { + } catch { $There = $false $LoginPasswordExpiration = [pscustomobject] @{ Count = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $LoginPasswordExpiration = [pscustomobject] @{ Count = 'We Could not Connect to $Instance' @@ -772,20 +714,18 @@ function Get-AllInstanceInfo { 'LoginMustChange' { if ($There) { try { - $role = Get-DbaServerRole -SQLInstance $instance -ServerRole "sysadmin" + $role = Get-DbaServerRole -SqlInstance $instance -ServerRole "sysadmin" $LoginMustChange = [pscustomobject] @{ - Count = @(Get-DbaLogin -SQLInstance $instance -Login @($role.Login) -Type SQL | Where-Object { $_.IsMustChange -eq $false -and $_.IsDisabled -eq $false -and $null -eq $_LastLogin }).Count + Count = @(Get-DbaLogin -SqlInstance $instance -Login @($role.Login) -Type SQL | Where-Object { $_.MustChangePassword -eq $false -and $_.IsDisabled -eq $false -and $null -eq $_LastLogin }).Count } - } - catch { + } catch { $There = $false $LoginMustChange = [pscustomobject] @{ Count = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $LoginMustChange = [pscustomobject] @{ Count = 'We Could not Connect to $Instance' @@ -800,15 +740,13 @@ function Get-AllInstanceInfo { $SQLMailXPsDisabled = [pscustomobject] @{ ConfiguredValue = $SpConfig.ConfiguredValue } - } - catch { + } catch { $There = $false $SQLMailXPsDisabled = [pscustomobject] @{ ConfiguredValue = 'We Could not Connect to $Instance' } } - } - else { + } else { $There = $false $SQLMailXPsDisabled = [pscustomobject] @{ ConfiguredValue = 'We Could not Connect to $Instance' @@ -874,7 +812,7 @@ function Assert-ScanForStartupProcedures { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")] [CmdletBinding()] param ($AllInstanceInfo, $ScanForStartupProcsDisabled) - ($AllInstanceInfo.ScanForStartupProceduresDisabled.ConfiguredValue -eq 0) | Should -Be $ScanForStartupProcsDisabled -Because "We expected the scan for startup procedures to be configured correctly" + ($AllInstanceInfo.ScanForStartupProceduresDisabled.ConfiguredValue -eq 0) | Should -Be $ScanForStartupProcsDisabled -Because "We expected the scan for startup procedures to be configured correctly" } function Assert-MaxDump { Param($AllInstanceInfo, $maxdumps) @@ -896,8 +834,7 @@ function Assert-InstanceMaxDop { if ($UseRecommended) { #if UseRecommended - check that the CurrentInstanceMaxDop property returned from Test-DbaMaxDop matches the the RecommendedMaxDop property $MaxDop.CurrentInstanceMaxDop | Should -Be $MaxDop.RecommendedMaxDop -Because "We expect the MaxDop Setting to be the recommended value $($MaxDop.RecommendedMaxDop)" - } - else { + } else { #if not UseRecommended - check that the CurrentInstanceMaxDop property returned from Test-DbaMaxDop matches the MaxDopValue parameter $MaxDop.CurrentInstanceMaxDop | Should -Be $MaxDopValue -Because "We expect the MaxDop Setting to be $MaxDopValue" } @@ -928,8 +865,7 @@ function Assert-InstanceSupportedBuild { $Build = $results.build $Compliant | Should -Be $true -Because "this build $Build should not be behind the required build" #If no $BuildBehind only check against support dates - } - else { + } else { $Results = Test-DbaBuild -SqlInstance $Instance -SqlCredential $sqlcredential -Latest [DateTime]$SupportedUntil = Get-Date $results.SupportedUntil -Format O $Build = $results.build @@ -960,9 +896,8 @@ function Assert-TraceFlag { ) if ($ExpectedTraceFlag -ne 0) { - $ExpectedTraceFlag | Should -BeIn $ActualTraceflags -Because "We expect that Trace Flag $ExpectedTraceFlag will be set" - } - else { + $ExpectedTraceFlag | Should -BeIn $ActualTraceflags -Because "We expect that Trace Flag $ExpectedTraceFlag will be set" + } else { $ActualTraceflags | Should -BeNullOrEmpty -Because "We expect that there will be no Trace Flags set" } } @@ -977,8 +912,7 @@ function Assert-NotTraceFlag { if ($null -eq $NotExpectedTraceFlag) { (@(Get-DbaTraceFlag -SqlInstance $SQLInstance).Where{ $_.TraceFlag -notin $ExpectedTraceFlag } | Select-Object).TraceFlag | Should -BeNullOrEmpty -Because "We expect that there will be no Trace Flags set on $SQLInstance" - } - else { + } else { @($NotExpectedTraceFlag).ForEach{ (Get-DbaTraceFlag -SqlInstance $SQLInstance).TraceFlag | Should -Not -Contain $PSItem -Because "We expect that Trace Flag $PsItem will not be set on $SQLInstance" } @@ -1070,7 +1004,7 @@ function Assert-LoginAuditSuccessful { function Assert-LoginAuditFailed { Param($AllInstanceInfo) - $AllInstanceInfo.LoginAuditFailed.AuditLevel | Should -BeIn @("Failure", "All") -Because "We expected the audit level to be set to capture failed logins" + $AllInstanceInfo.LoginAuditFailed.AuditLevel | Should -BeIn @("Failure", "All") -Because "We expected the audit level to be set to capture failed logins" } From 21110c1bbaff3c531a0d1da5f6fb92d053c25301 Mon Sep 17 00:00:00 2001 From: Rob Sewell Date: Fri, 12 May 2023 12:40:15 +0000 Subject: [PATCH 5/6] so we can check logins that must change passwords --- developing/Robs-Instance.ps1 | 1 + source/checks/Instancev5.Tests.ps1 | 9 +++++++++ source/internal/assertions/Instance.Assertions.ps1 | 2 +- source/internal/functions/NewGet-AllInstanceInfo.ps1 | 10 ++++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/developing/Robs-Instance.ps1 b/developing/Robs-Instance.ps1 index 39777bf5..68440523 100644 --- a/developing/Robs-Instance.ps1 +++ b/developing/Robs-Instance.ps1 @@ -16,6 +16,7 @@ $Checks = 'LoginAuditSuccessful' $Checks = 'LoginCheckPolicy' $Checks = 'SuspectPageLimit' $Checks = 'SupportedBuild' +$Checks = 'LoginMustChange' $Checks = 'LoginAuditSuccessful', 'LoginAuditFailed' Invoke-PerfAndValidateCheck -Checks $Checks diff --git a/source/checks/Instancev5.Tests.ps1 b/source/checks/Instancev5.Tests.ps1 index fb1a2f82..6b69ea16 100644 --- a/source/checks/Instancev5.Tests.ps1 +++ b/source/checks/Instancev5.Tests.ps1 @@ -227,6 +227,15 @@ Describe "Login Check Policy" -Tag LoginCheckPolicy, Security, CIS, Medium, Inst } } +Describe "Login Must Change" -Tag LoginMustChange, Security, CIS, Medium, Instance -ForEach $InstancesToTest { + $skip = ($__dbcconfig | Where-Object { $_.Name -eq 'skip.security.LoginMustChange' }).Value + Context "Testing if the new SQL logins that have not logged have to change their password when they log in on <_.Name>" { + It "All new sql logins should have the have to change their password when they log in for the first time on <_.Name>" -Skip:$skip { + $PsItem.LoginMustChangeCount | Should -Be 0 -Because "We expected the all the new sql logins to have to change the password on first login" + } + } +} + Describe "Instance MaxDop" -Tag MaxDopInstance, MaxDop, Medium, Instance -ForEach ($InstancesToTest | Where-Object { $psitem.Name -notin $psitem.ConfigValues.ExcludeInstanceMaxDop }) { $skip = ($__dbcconfig | Where-Object { $_.Name -eq 'skip.instance.MaxDopInstance' }).Value Context "Testing Instance MaxDop Value on <_.Name>" { diff --git a/source/internal/assertions/Instance.Assertions.ps1 b/source/internal/assertions/Instance.Assertions.ps1 index 9e63d64a..004d3a7e 100644 --- a/source/internal/assertions/Instance.Assertions.ps1 +++ b/source/internal/assertions/Instance.Assertions.ps1 @@ -717,7 +717,7 @@ function Get-AllInstanceInfo { $role = Get-DbaServerRole -SqlInstance $instance -ServerRole "sysadmin" $LoginMustChange = [pscustomobject] @{ - Count = @(Get-DbaLogin -SqlInstance $instance -Login @($role.Login) -Type SQL | Where-Object { $_.MustChangePassword -eq $false -and $_.IsDisabled -eq $false -and $null -eq $_LastLogin }).Count + Count = @(Get-DbaLogin -SqlInstance $instance -Login @($role.Login) -Type SQL | Where-Object { $_.MustChangePassword -eq $false -and $_.IsDisabled -eq $false -and $null -eq $_.LastLogin }).Count } } catch { $There = $false diff --git a/source/internal/functions/NewGet-AllInstanceInfo.ps1 b/source/internal/functions/NewGet-AllInstanceInfo.ps1 index 0f10de12..3814bd52 100644 --- a/source/internal/functions/NewGet-AllInstanceInfo.ps1 +++ b/source/internal/functions/NewGet-AllInstanceInfo.ps1 @@ -461,6 +461,15 @@ function NewGet-AllInstanceInfo { } } + 'LoginMustChange' { + $loginTimeSql = "SELECT login_name, MAX(login_time) AS login_time FROM sys.dm_exec_sessions GROUP BY login_name" + $loginTimes = $instance.ConnectionContext.ExecuteWithResults($loginTimeSql).Tables[0] + $lastlogin = @{Name = 'LastLogin' ; Expression = { $Name = $_.name; ($loginTimes | Where-Object { $_.login_name -eq $name }).login_time + } + } + $LoginMustChangeCount = ($Instance.Logins | Where-Object { $_.Name -in $Instance.Roles['sysadmin'].EnumMemberNames() } | Select-Object Name, $lastlogin, MustChangePassword, IsDisabled | Where-Object { $_.MustChangePassword -eq $false -and $_.IsDisabled -eq $false -and $null -eq $_.LastLogin }).Count + } + Default { } } @@ -563,6 +572,7 @@ function NewGet-AllInstanceInfo { HideInstance = $HideInstance SuspectPageCountResult = $SuspectPageCountResult SupportedBuild = $SupportedBuild + LoginMustChangeCount = $LoginMustChangeCount # TempDbConfig = [PSCustomObject]@{ # TF118EnabledCurrent = $tempDBTest[0].CurrentSetting # TF118EnabledRecommended = $tempDBTest[0].Recommended From 6d1ebe036291dda75c063feed86a8c2fbdf2f306 Mon Sep 17 00:00:00 2001 From: Rob Sewell Date: Fri, 12 May 2023 13:52:30 +0000 Subject: [PATCH 6/6] so we check password expirations --- source/checks/Instancev5.Tests.ps1 | 9 +++ .../functions/NewGet-AllInstanceInfo.ps1 | 63 ++++++++++--------- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/source/checks/Instancev5.Tests.ps1 b/source/checks/Instancev5.Tests.ps1 index 6b69ea16..f49d210a 100644 --- a/source/checks/Instancev5.Tests.ps1 +++ b/source/checks/Instancev5.Tests.ps1 @@ -236,6 +236,15 @@ Describe "Login Must Change" -Tag LoginMustChange, Security, CIS, Medium, Instan } } +Describe "Login Password Expiration" -Tag LoginPasswordExpiration, Security, CIS, Medium, Instance -ForEach $InstancesToTest { + $skip = ($__dbcconfig | Where-Object { $_.Name -eq 'skip.security.LoginPasswordExpiration' }).Value + Context "Testing if the login password expiration is enabled for sql logins in the sysadmin role on <_.Name>" { + It "All sql logins should have the password expiration option set to ON in the sysadmin role on <_.Name>" -Skip:$skip { + $PsItem.LoginPasswordExpirationCount | Should -Be 0 -Because "We expected the password expiration policy to set on all sql logins in the sysadmin role" + } + } +} + Describe "Instance MaxDop" -Tag MaxDopInstance, MaxDop, Medium, Instance -ForEach ($InstancesToTest | Where-Object { $psitem.Name -notin $psitem.ConfigValues.ExcludeInstanceMaxDop }) { $skip = ($__dbcconfig | Where-Object { $_.Name -eq 'skip.instance.MaxDopInstance' }).Value Context "Testing Instance MaxDop Value on <_.Name>" { diff --git a/source/internal/functions/NewGet-AllInstanceInfo.ps1 b/source/internal/functions/NewGet-AllInstanceInfo.ps1 index 3814bd52..c172abd2 100644 --- a/source/internal/functions/NewGet-AllInstanceInfo.ps1 +++ b/source/internal/functions/NewGet-AllInstanceInfo.ps1 @@ -467,7 +467,11 @@ function NewGet-AllInstanceInfo { $lastlogin = @{Name = 'LastLogin' ; Expression = { $Name = $_.name; ($loginTimes | Where-Object { $_.login_name -eq $name }).login_time } } - $LoginMustChangeCount = ($Instance.Logins | Where-Object { $_.Name -in $Instance.Roles['sysadmin'].EnumMemberNames() } | Select-Object Name, $lastlogin, MustChangePassword, IsDisabled | Where-Object { $_.MustChangePassword -eq $false -and $_.IsDisabled -eq $false -and $null -eq $_.LastLogin }).Count + $LoginMustChangeCount = ($Instance.Logins | Where-Object { $_.LoginType -eq 'SqlLogin' } | Where-Object { $_.Name -in $Instance.Roles['sysadmin'].EnumMemberNames() } | Select-Object Name, $lastlogin, MustChangePassword, IsDisabled | Where-Object { $_.MustChangePassword -eq $false -and $_.IsDisabled -eq $false -and $null -eq $_.LastLogin }).Count + } + + 'LoginPasswordExpiration' { + $LoginPasswordExpirationCount = ($Instance.Logins | Where-Object { $_.Name -in $Instance.Roles['sysadmin'].EnumMemberNames() } | Where-Object { $_.LoginType -eq 'SqlLogin' -and $_.PasswordExpirationEnabled -EQ $false -and $_.IsDisabled -EQ $false }).Count } Default { } @@ -476,20 +480,20 @@ function NewGet-AllInstanceInfo { #build the object $testInstanceObject = [PSCustomObject]@{ - ComputerName = $Instance.ComputerName - InstanceName = $Instance.DbaInstanceName - Name = $Instance.Name - ConfigValues = $ConfigValues - VersionMajor = $Instance.VersionMajor - Configuration = if ($configurations) { $Instance.Configuration } else { $null } - Settings = $Instance.Settings - Logins = $Instance.Logins - Databases = $Instance.Databases - NumberOfLogFiles = $Instance.NumberOfLogFiles - MaxDopSettings = $MaxDopSettings - ExpectedTraceFlags = $ExpectedTraceFlags - NotExpectedTraceFlags = $NotExpectedTraceFlags - XESessions = [pscustomobject]@{ + ComputerName = $Instance.ComputerName + InstanceName = $Instance.DbaInstanceName + Name = $Instance.Name + ConfigValues = $ConfigValues + VersionMajor = $Instance.VersionMajor + Configuration = if ($configurations) { $Instance.Configuration } else { $null } + Settings = $Instance.Settings + Logins = $Instance.Logins + Databases = $Instance.Databases + NumberOfLogFiles = $Instance.NumberOfLogFiles + MaxDopSettings = $MaxDopSettings + ExpectedTraceFlags = $ExpectedTraceFlags + NotExpectedTraceFlags = $NotExpectedTraceFlags + XESessions = [pscustomobject]@{ RequiredStopped = $RequiredStopped.ForEach{ [pscustomobject]@{ Name = $Instance.Name @@ -524,23 +528,23 @@ function NewGet-AllInstanceInfo { Sessions = $Sessions Running = $RunningSessions } - ErrorLogEntries = [pscustomobject]@{ + ErrorLogEntries = [pscustomobject]@{ errorLogCount = $ErrorLogCount logWindow = $logWindow } - InstanceConnection = $InstanceConnection - BackupPathAccess = [pscustomobject]@{ + InstanceConnection = $InstanceConnection + BackupPathAccess = [pscustomobject]@{ Result = $BackupPathAccess BackupPath = $BackupPath } - LatestBuild = [PSCustomObject]@{ + LatestBuild = [PSCustomObject]@{ Compliant = $LatestBuild.Compliant } - NetworkLatency = [PSCustomObject]@{ + NetworkLatency = [PSCustomObject]@{ Latency = $Latency Threshold = $NetworkThreshold } - LinkedServerResults = if ($LinkedServerResults) { + LinkedServerResults = if ($LinkedServerResults) { $LinkedServerResults.ForEach{ [pscustomobject]@{ InstanceName = $Instance.Name @@ -559,20 +563,21 @@ function NewGet-AllInstanceInfo { Result = 'None' } } - MaxMemory = $MaxMemory - OrphanedFile = [pscustomobject]@{ + MaxMemory = $MaxMemory + OrphanedFile = [pscustomobject]@{ FileCount = $FileCount } - ServerNameMatch = [pscustomobject]@{ + ServerNameMatch = [pscustomobject]@{ configuredServerName = $ServerNameMatchconfiguredServerName netName = $ServerNameMatchnetName renamerequired = $ServerNameMatchrenamerequired } - MemoryDump = $Dump - HideInstance = $HideInstance - SuspectPageCountResult = $SuspectPageCountResult - SupportedBuild = $SupportedBuild - LoginMustChangeCount = $LoginMustChangeCount + MemoryDump = $Dump + HideInstance = $HideInstance + SuspectPageCountResult = $SuspectPageCountResult + SupportedBuild = $SupportedBuild + LoginMustChangeCount = $LoginMustChangeCount + LoginPasswordExpirationCount = $LoginPasswordExpirationCount # TempDbConfig = [PSCustomObject]@{ # TF118EnabledCurrent = $tempDBTest[0].CurrentSetting # TF118EnabledRecommended = $tempDBTest[0].Recommended