diff --git a/bin/dbatools-buildref-index.json b/bin/dbatools-buildref-index.json index 9b171cddcb..bad31c29ee 100644 --- a/bin/dbatools-buildref-index.json +++ b/bin/dbatools-buildref-index.json @@ -1,5 +1,5 @@ { - "LastUpdated": "2023-06-07T00:00:00", + "LastUpdated": "2023-06-15T00:00:00", "Data": [ { "Version": "8.0.47", @@ -4505,6 +4505,11 @@ "Version": "15.0.4312", "KBList": "5024276" }, + { + "CU": "CU21", + "Version": "15.0.4316", + "KBList": "5025808" + }, { "Version": "16.0.100", "Name": "2022" @@ -4545,6 +4550,11 @@ "CU": "CU4", "Version": "16.0.4035", "KBList": "5026717" + }, + { + "CU": "CU5", + "Version": "16.0.4045", + "KBList": "5026806" } ] } diff --git a/private/configurations/configuration.ps1 b/private/configurations/configuration.ps1 index 226c28f94b..e9f5451074 100644 --- a/private/configurations/configuration.ps1 +++ b/private/configurations/configuration.ps1 @@ -135,7 +135,7 @@ $script:path_typedata = [IO.Path]::Combine($script:AppData, $psVersionName, "dba #endregion Paths # Determine Registry Availability -$script:NoRegistry = $true +$script:NoRegistry = $false if (($PSVersionTable.PSVersion.Major -ge 6) -and ($PSVersionTable.OS -notlike "*Windows*")) { $script:NoRegistry = $true } diff --git a/public/Backup-DbaDatabase.ps1 b/public/Backup-DbaDatabase.ps1 index cca3c3f50b..3e79c61766 100644 --- a/public/Backup-DbaDatabase.ps1 +++ b/public/Backup-DbaDatabase.ps1 @@ -815,7 +815,7 @@ function Backup-DbaDatabase { Write-Message -Message "Exception thrown by db going into restoring mode due to recovery" -Leve Verbose } else { Write-Progress -Id $ProgressId -Activity "Backup" -Status "Failed" -Completed - Stop-Function -message "Backup Failed" -ErrorRecord $_ -Continue + Stop-Function -message "Backup of [$dbName] failed" -ErrorRecord $_ -Target $dbName -Continue $BackupComplete = $false } } diff --git a/public/Connect-DbaInstance.ps1 b/public/Connect-DbaInstance.ps1 index 34dd921ed8..b343494038 100644 --- a/public/Connect-DbaInstance.ps1 +++ b/public/Connect-DbaInstance.ps1 @@ -645,15 +645,39 @@ function Connect-DbaInstance { } elseif ($inputObjectType -eq 'SqlConnection') { $server = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server -ArgumentList $inputObject } elseif ($inputObjectType -in 'RegisteredServer', 'ConnectionString') { - $server = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server -ArgumentList $serverName - # Parameter TrustServerCertificate changes the connection string be allow connections to instances with the default self-signed certificate + # Create the server SMO in the same way as when passing a string (see #8962 for details). + # Best way to get connection pooling to work is to use SqlConnectionInfo -> ServerConnection -> Server + $sqlConnectionInfo = New-Object -TypeName Microsoft.SqlServer.Management.Common.SqlConnectionInfo + + # Set properties of SqlConnectionInfo based on the used properties of the connection string. + $csb = New-Object -TypeName Microsoft.Data.SqlClient.SqlConnectionStringBuilder -ArgumentList $connectionString + if ($csb.ShouldSerialize('Data Source')) { + Write-Message -Level Debug -Message "ServerName will be set to '$($csb.DataSource)'" + $sqlConnectionInfo.ServerName = $csb.DataSource + $null = $csb.Remove('Data Source') + } + if ($csb.ShouldSerialize('User ID')) { + Write-Message -Level Debug -Message "UserName will be set to '$($csb.UserID)'" + $sqlConnectionInfo.UserName = $csb.UserID + $null = $csb.Remove('User ID') + } + if ($csb.ShouldSerialize('Password')) { + Write-Message -Level Debug -Message "Password will be set" + $sqlConnectionInfo.Password = $csb.Password + $null = $csb.Remove('Password') + } + + # Add all remaining parts of the connection string as additional parameters. + $sqlConnectionInfo.AdditionalParameters = $csb.ConnectionString + + # Set properties based on used parameters. if ($TrustServerCertificate) { - Write-Message -Level Verbose -Message "TrustServerCertificate will be set to 'True'" - $csb = New-Object -TypeName Microsoft.Data.SqlClient.SqlConnectionStringBuilder -ArgumentList $connectionString - $csb.TrustServerCertificate = $true - $connectionString = $csb.ConnectionString + Write-Message -Level Debug -Message "TrustServerCertificate will be set to '$TrustServerCertificate'" + $sqlConnectionInfo.TrustServerCertificate = $TrustServerCertificate } - $server.ConnectionContext.ConnectionString = $connectionString + + $serverConnection = New-Object -TypeName Microsoft.SqlServer.Management.Common.ServerConnection -ArgumentList $sqlConnectionInfo + $server = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server -ArgumentList $serverConnection } elseif ($inputObjectType -eq 'String') { # Identify authentication method if (Test-Azure -SqlInstance $instance) { diff --git a/public/Export-DbaUser.ps1 b/public/Export-DbaUser.ps1 index 2732387381..da6d450cd3 100644 --- a/public/Export-DbaUser.ps1 +++ b/public/Export-DbaUser.ps1 @@ -153,7 +153,7 @@ function Export-DbaUser { [string[]]$Database, [string[]]$ExcludeDatabase, [string[]]$User, - [ValidateSet('SQLServer2000', 'SQLServer2005', 'SQLServer2008/2008R2', 'SQLServer2012', 'SQLServer2014', 'SQLServer2016', 'SQLServer2017', 'SQLServer2019')] + [ValidateSet('SQLServer2000', 'SQLServer2005', 'SQLServer2008/2008R2', 'SQLServer2012', 'SQLServer2014', 'SQLServer2016', 'SQLServer2017', 'SQLServer2019', 'SQLServer2022')] [string]$DestinationVersion, [string]$Path = (Get-DbatoolsConfigValue -FullName 'Path.DbatoolsExport'), [Alias("OutFile", "FileName")] @@ -185,6 +185,7 @@ function Export-DbaUser { 'SQLServer2016' = 'Version130' 'SQLServer2017' = 'Version140' 'SQLServer2019' = 'Version150' + 'SQLServer2022' = 'Version160' } $versionName = @{ @@ -196,6 +197,7 @@ function Export-DbaUser { 'Version130' = 'SQLServer2016' 'Version140' = 'SQLServer2017' 'Version150' = 'SQLServer2019' + 'Version160' = 'SQLServer2022' } $eol = [System.Environment]::NewLine diff --git a/public/Get-DbaAvailableCollation.ps1 b/public/Get-DbaAvailableCollation.ps1 index 995031e760..6c7b598a89 100644 --- a/public/Get-DbaAvailableCollation.ps1 +++ b/public/Get-DbaAvailableCollation.ps1 @@ -8,7 +8,7 @@ function Get-DbaAvailableCollation { Only the connect permission is required to get this information. .PARAMETER SqlInstance - TThe target SQL Server instance or instances. Only connect permission is required. + The target SQL Server instance or instances. Only connect permission is required. .PARAMETER SqlCredential Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). diff --git a/public/Get-DbaDbMail.ps1 b/public/Get-DbaDbMail.ps1 index daae1e60b9..b43d70f987 100644 --- a/public/Get-DbaDbMail.ps1 +++ b/public/Get-DbaDbMail.ps1 @@ -7,7 +7,7 @@ function Get-DbaDbMail { Gets the database mail from SQL Server .PARAMETER SqlInstance - TThe target SQL Server instance or instances. + The target SQL Server instance or instances. .PARAMETER SqlCredential Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). diff --git a/public/Get-DbaDbMailConfig.ps1 b/public/Get-DbaDbMailConfig.ps1 index fcf64c730b..f728b6edc7 100644 --- a/public/Get-DbaDbMailConfig.ps1 +++ b/public/Get-DbaDbMailConfig.ps1 @@ -7,7 +7,7 @@ function Get-DbaDbMailConfig { Gets database mail configs from SQL Server .PARAMETER SqlInstance - TThe target SQL Server instance or instances. + The target SQL Server instance or instances. .PARAMETER SqlCredential Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). diff --git a/public/Get-DbaDbMailHistory.ps1 b/public/Get-DbaDbMailHistory.ps1 index 99f267f870..1be9118c59 100644 --- a/public/Get-DbaDbMailHistory.ps1 +++ b/public/Get-DbaDbMailHistory.ps1 @@ -7,7 +7,7 @@ function Get-DbaDbMailHistory { Gets the history of mail sent from a SQL instance .PARAMETER SqlInstance - TThe target SQL Server instance or instances. + The target SQL Server instance or instances. .PARAMETER SqlCredential Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). diff --git a/public/Get-DbaDbMailLog.ps1 b/public/Get-DbaDbMailLog.ps1 index e18b95f5aa..c64596be6a 100644 --- a/public/Get-DbaDbMailLog.ps1 +++ b/public/Get-DbaDbMailLog.ps1 @@ -7,7 +7,7 @@ function Get-DbaDbMailLog { Gets the DBMail log from a SQL instance .PARAMETER SqlInstance - TThe target SQL Server instance or instances. + The target SQL Server instance or instances. .PARAMETER SqlCredential Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). diff --git a/public/Get-DbaDbMailServer.ps1 b/public/Get-DbaDbMailServer.ps1 index 4c722147cc..cec0636db7 100644 --- a/public/Get-DbaDbMailServer.ps1 +++ b/public/Get-DbaDbMailServer.ps1 @@ -7,7 +7,7 @@ function Get-DbaDbMailServer { Gets database mail servers from SQL Server .PARAMETER SqlInstance - TThe target SQL Server instance or instances. + The target SQL Server instance or instances. .PARAMETER SqlCredential Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). diff --git a/public/Get-DbaDbRole.ps1 b/public/Get-DbaDbRole.ps1 index 56711f6309..525c19f429 100644 --- a/public/Get-DbaDbRole.ps1 +++ b/public/Get-DbaDbRole.ps1 @@ -84,7 +84,7 @@ function Get-DbaDbRole { #> [CmdletBinding()] param ( - [parameter(ValueFromPipeline)] + [Parameter(ValueFromPipeline)] [DbaInstance[]]$SqlInstance, [PSCredential]$SqlCredential, [string[]]$Database, @@ -92,11 +92,10 @@ function Get-DbaDbRole { [string[]]$Role, [string[]]$ExcludeRole, [switch]$ExcludeFixedRole, - [parameter(ValueFromPipeline)] + [Parameter(ValueFromPipeline)] [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject, [switch]$EnableException ) - process { if (-not $InputObject -and -not $SqlInstance) { Stop-Function -Message "You must pipe in a database or specify a SqlInstance" @@ -104,7 +103,7 @@ function Get-DbaDbRole { } if ($SqlInstance) { - $InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database -ExcludeDatabase $ExcludeDatabase + $InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database -ExcludeDatabase $ExcludeDatabase -EnableException:$EnableException } foreach ($db in $InputObject) { @@ -115,15 +114,12 @@ function Get-DbaDbRole { Write-Message -Level 'Verbose' -Message "Getting Database Roles for $db on $server" $dbRoles = $db.Roles - if ($Role) { $dbRoles = $dbRoles | Where-Object { $_.Name -in $Role } } - if ($ExcludeRole) { $dbRoles = $dbRoles | Where-Object { $_.Name -notin $ExcludeRole } } - if ($ExcludeFixedRole) { $dbRoles = $dbRoles | Where-Object { $_.IsFixedRole -eq $false -and $_.Name -ne 'public' } } @@ -133,7 +129,6 @@ function Get-DbaDbRole { Add-Member -Force -InputObject $dbRole -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName Add-Member -Force -InputObject $dbRole -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName Add-Member -Force -InputObject $dbRole -MemberType NoteProperty -Name Database -Value $db.Name - Select-DefaultView -InputObject $dbRole -Property "ComputerName", "InstanceName", "Database", "Name", "IsFixedRole" } } diff --git a/public/Get-DbaDbRoleMember.ps1 b/public/Get-DbaDbRoleMember.ps1 index 055a952fca..4413a358da 100644 --- a/public/Get-DbaDbRoleMember.ps1 +++ b/public/Get-DbaDbRoleMember.ps1 @@ -92,7 +92,7 @@ function Get-DbaDbRoleMember { #> [CmdletBinding()] param ( - [parameter(ValueFromPipeline)] + [Parameter(ValueFromPipeline)] [DbaInstance[]]$SqlInstance, [PSCredential]$SqlCredential, [string[]]$Database, @@ -101,7 +101,7 @@ function Get-DbaDbRoleMember { [string[]]$ExcludeRole, [switch]$ExcludeFixedRole, [switch]$IncludeSystemUser, - [parameter(ValueFromPipeline)] + [Parameter(ValueFromPipeline)] [object[]]$InputObject, [switch]$EnableException ) @@ -118,18 +118,31 @@ function Get-DbaDbRoleMember { foreach ($input in $InputObject) { $inputType = $input.GetType().FullName + $dbRoleParams = @{ + SqlInstance = $input + SqlCredential = $SqlCredential + Database = $Database + ExcludeDatabase = $ExcludeDatabase + Role = $Role + ExcludeRole = $ExcludeRole + ExcludeFixedRole = $ExcludeFixedRole + EnableException = $EnableException + } switch ($inputType) { 'Dataplat.Dbatools.Parameter.DbaInstanceParameter' { Write-Message -Level Verbose -Message "Processing DbaInstanceParameter through InputObject" - $dbRoles = Get-DbaDBRole -SqlInstance $input -SqlCredential $SqlCredential -Database $Database -ExcludeDatabase $ExcludeDatabase -Role $Role -ExcludeRole $ExcludeRole -ExcludeFixedRole:$ExcludeFixedRole + $dbRoles = Get-DbaDbRole @dbRoleParams } 'Microsoft.SqlServer.Management.Smo.Server' { Write-Message -Level Verbose -Message "Processing Server through InputObject" - $dbRoles = Get-DbaDBRole -SqlInstance $input -SqlCredential $SqlCredential -Database $Database -ExcludeDatabase $ExcludeDatabase -Role $Role -ExcludeRole $ExcludeRole -ExcludeFixedRole:$ExcludeFixedRole + $dbRoles = Get-DbaDbRole @dbRoleParams } 'Microsoft.SqlServer.Management.Smo.Database' { + $dbRoleParams.Remove('SqlInstance') + $dbRoleParams.Remove('SqlCredential') + $dbRoleParams.Remove('Database') Write-Message -Level Verbose -Message "Processing Database through InputObject" - $dbRoles = $input | Get-DbaDBRole -Role $Role -ExcludeRole $ExcludeRole -ExcludeFixedRole:$ExcludeFixedRole + $dbRoles = $input | Get-DbaDbRole @dbRoleParams } 'Microsoft.SqlServer.Management.Smo.DatabaseRole' { Write-Message -Level Verbose -Message "Processing DatabaseRole through InputObject" diff --git a/public/Get-DbaDefaultPath.ps1 b/public/Get-DbaDefaultPath.ps1 index 54176b3807..91624ff473 100644 --- a/public/Get-DbaDefaultPath.ps1 +++ b/public/Get-DbaDefaultPath.ps1 @@ -7,7 +7,7 @@ function Get-DbaDefaultPath { Gets the default SQL Server paths for data, logs and backups .PARAMETER SqlInstance - TThe target SQL Server instance or instances. + The target SQL Server instance or instances. .PARAMETER SqlCredential Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). diff --git a/public/Get-DbaErrorLog.ps1 b/public/Get-DbaErrorLog.ps1 index 16a7022e2d..e13c4f9bf0 100644 --- a/public/Get-DbaErrorLog.ps1 +++ b/public/Get-DbaErrorLog.ps1 @@ -7,7 +7,7 @@ function Get-DbaErrorLog { Gets the "SQL Error Log" of an instance. Returns all 10 error logs by default. .PARAMETER SqlInstance - TThe target SQL Server instance or instances. + The target SQL Server instance or instances. .PARAMETER SqlCredential Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). diff --git a/public/Get-DbaMaintenanceSolutionLog.ps1 b/public/Get-DbaMaintenanceSolutionLog.ps1 index 29938782af..f33e842b3c 100644 --- a/public/Get-DbaMaintenanceSolutionLog.ps1 +++ b/public/Get-DbaMaintenanceSolutionLog.ps1 @@ -6,6 +6,10 @@ function Get-DbaMaintenanceSolutionLog { .DESCRIPTION Ola wrote a .sql script to get the content from the commandLog table. However, if LogToTable='N', there will be no logging in that table. This function reads the text files that are written in the SQL Instance's Log directory. + Be aware that this command only works if sqlcmd is used to execute the procedures, which is a legacy way to start them and not used by new installations. + + We would instead recommend installing using Install-DbaMaintenanceSolution and calling the procedures with the LogToTable='Y' parameter. + .PARAMETER SqlInstance The target SQL Server instance or instances. diff --git a/public/Get-DbaStartupParameter.ps1 b/public/Get-DbaStartupParameter.ps1 index 2cca86704d..7e4dbc6104 100644 --- a/public/Get-DbaStartupParameter.ps1 +++ b/public/Get-DbaStartupParameter.ps1 @@ -83,13 +83,7 @@ function Get-DbaStartupParameter { $masterLog = $params | Where-Object { $_.StartsWith('-l') } $errorLog = $params | Where-Object { $_.StartsWith('-e') } $traceFlags = $params | Where-Object { $_.StartsWith('-T') } - - $debugflag = $params | Where-Object { $_.StartsWith('-t') } - - if ($debugflag.length -ne 0) { - Write-Message -Level Warning "$instance is using the lowercase -t trace flag. This is for internal debugging only. Please ensure this was intentional." - } - #> + $debugFlags = $params | Where-Object { $_.StartsWith('-t') } if ($traceFlags.length -eq 0) { $traceFlags = "None" @@ -97,6 +91,12 @@ function Get-DbaStartupParameter { [int[]]$traceFlags = $traceFlags.substring(2) } + if ($debugFlags.length -eq 0) { + $debugFlags = "None" + } else { + [int[]]$debugFlags = $debugFlags.substring(2) + } + if ($Simple -eq $true) { [PSCustomObject]@{ ComputerName = $computerName @@ -106,6 +106,7 @@ function Get-DbaStartupParameter { MasterLog = $masterLog.TrimStart('-l') ErrorLog = $errorLog.TrimStart('-e') TraceFlags = $traceFlags + DebugFlags = $debugFlags ParameterString = $wmisvc.StartupParameters } } else { @@ -159,6 +160,7 @@ function Get-DbaStartupParameter { MasterLog = $masterLog -replace '^-[lL]', '' ErrorLog = $errorLog -replace '^-[eE]', '' TraceFlags = $traceFlags + DebugFlags = $debugFlags CommandPromptStart = $commandPrompt MinimalStart = $minimalStart MemoryToReserve = $memoryToReserve diff --git a/public/Install-DbaMaintenanceSolution.ps1 b/public/Install-DbaMaintenanceSolution.ps1 index 753855f042..bff240616b 100644 --- a/public/Install-DbaMaintenanceSolution.ps1 +++ b/public/Install-DbaMaintenanceSolution.ps1 @@ -93,14 +93,14 @@ function Install-DbaMaintenanceSolution { https://dbatools.io/Install-DbaMaintenanceSolution .EXAMPLE - PS C:\> Install-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -CleanupTime 72 + PS C:\> Install-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -InstallJobs -CleanupTime 72 Installs Ola Hallengren's Solution objects on RES14224 in the DBA database. Backups will default to the default Backup Directory. If the Maintenance Solution already exists, the script will be halted. .EXAMPLE - PS C:\> Install-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -BackupLocation "Z:\SQLBackup" -CleanupTime 72 + PS C:\> Install-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -InstallJobs -BackupLocation "Z:\SQLBackup" -CleanupTime 72 This will create the Ola Hallengren's Solution objects. Existing objects are not affected in any way. @@ -120,7 +120,7 @@ function Install-DbaMaintenanceSolution { Installs Maintenance Solution to myserver in database. Adds Agent Jobs, and if any currently exist, they'll be replaced. .EXAMPLE - PS C:\> Install-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -BackupLocation "Z:\SQLBackup" -CleanupTime 72 -ReplaceExisting + PS C:\> Install-DbaMaintenanceSolution -SqlInstance RES14224 -Database DBA -InstallJobs -BackupLocation "Z:\SQLBackup" -CleanupTime 72 -ReplaceExisting This will drop and then recreate the Ola Hallengren's Solution objects The cleanup script will drop and recreate: diff --git a/public/Test-DbaWindowsLogin.ps1 b/public/Test-DbaWindowsLogin.ps1 index 005d8b6980..61a8a2bfcc 100644 --- a/public/Test-DbaWindowsLogin.ps1 +++ b/public/Test-DbaWindowsLogin.ps1 @@ -28,6 +28,9 @@ function Test-DbaWindowsLogin { .PARAMETER IgnoreDomains Specifies a list of Active Directory domains to ignore. By default, all domains in the forest as well as all trusted domains are traversed. + .PARAMETER InputObject + Allows piping from Get-DbaLogin. + .PARAMETER EnableException By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting. @@ -59,17 +62,24 @@ function Test-DbaWindowsLogin { Tests all Domain logins excluding any that are from the testdomain + .EXAMPLE + PS C:\> Get-DbaLogin -SqlInstance Dev01 -Login DOMAIN\User | Test-DbaWindowsLogin + + Tests only the login returned by Get-DbaLogin + #> [CmdletBinding()] param ( - [parameter(Mandatory, ValueFromPipeline)] + [parameter(ValueFromPipeline)] [DbaInstanceParameter[]]$SqlInstance, [PSCredential]$SqlCredential, - [object[]]$Login, - [object[]]$ExcludeLogin, + [string[]]$Login, + [string[]]$ExcludeLogin, [ValidateSet("LoginsOnly", "GroupsOnly", "None")] [string]$FilterBy = "None", [string[]]$IgnoreDomains, + [Parameter(ValueFromPipeline)] + [Microsoft.SqlServer.Management.Smo.Login[]]$InputObject, [switch]$EnableException ) @@ -104,192 +114,199 @@ function Test-DbaWindowsLogin { 'NO_AUTH_DATA_REQUIRED' = 33554432 'PARTIAL_SECRETS_ACCOUNT' = 67108864 } + + $allWindowsLoginsGroups = @( ) } process { + if (-not (Test-Bound SqlInstance, InputObject -Max 1)) { + Stop-Function -Message "You must supply either -SqlInstance or an Input Object" + return + } + foreach ($instance in $SqlInstance) { try { $server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential } catch { Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue } + $allWindowsLoginsGroups += $server.Logins | Where-Object { $_.LoginType -in ('WindowsUser', 'WindowsGroup') } + } - - # we can only validate AD logins - $allWindowsLoginsGroups = $server.Logins | Where-Object { $_.LoginType -in ('WindowsUser', 'WindowsGroup') } - - # we cannot validate local users - $allWindowsLoginsGroups = $allWindowsLoginsGroups | Where-Object { $_.Name.StartsWith("NT ") -eq $false -and $_.Name.StartsWith($server.ComputerName) -eq $false -and $_.Name.StartsWith("BUILTIN") -eq $false } - if ($Login) { - $allWindowsLoginsGroups = $allWindowsLoginsGroups | Where-Object Name -In $Login + if ($InputObject) { + $allWindowsLoginsGroups += $InputObject + } + } + end { + # we cannot validate local users + $allWindowsLoginsGroups = $allWindowsLoginsGroups | Where-Object { $_.Name.StartsWith("NT ") -eq $false -and $_.Name.StartsWith($_.Parent.ComputerName) -eq $false -and $_.Name.StartsWith("BUILTIN") -eq $false } + if ($Login) { + $allWindowsLoginsGroups = $allWindowsLoginsGroups | Where-Object Name -In $Login + } + if ($ExcludeLogin) { + $allWindowsLoginsGroups = $allWindowsLoginsGroups | Where-Object Name -NotIn $ExcludeLogin + } + switch ($FilterBy) { + "LoginsOnly" { + Write-Message -Message "Search restricted to logins." -Level Verbose + $windowsLogins = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsUser' } - if ($ExcludeLogin) { - $allWindowsLoginsGroups = $allWindowsLoginsGroups | Where-Object Name -NotIn $ExcludeLogin + "GroupsOnly" { + Write-Message -Message "Search restricted to groups." -Level Verbose + $windowsGroups = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsGroup' } - switch ($FilterBy) { - "LoginsOnly" { - Write-Message -Message "Search restricted to logins." -Level Verbose - $windowsLogins = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsUser' - } - "GroupsOnly" { - Write-Message -Message "Search restricted to groups." -Level Verbose - $windowsGroups = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsGroup' + "None" { + Write-Message -Message "Search both logins and groups." -Level Verbose + $windowsLogins = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsUser' + $windowsGroups = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsGroup' + } + } + foreach ($winLogin in $windowsLogins) { + $adLogin = $winLogin.Name + $loginSid = $winLogin.Sid -join '' + $domain, $username = $adLogin.Split("\") + if ($domain.ToUpper() -in $IgnoreDomainsNormalized) { + Write-Message -Message "Skipping Login $adLogin." -Level Verbose + continue + } + Write-Message -Message "Parsing Login $adLogin." -Level Verbose + $exists = $false + try { + $loginBinary = [byte[]]$winLogin.Sid + $SID = New-Object Security.Principal.SecurityIdentifier($loginBinary, 0) + $SIDForAD = $SID.Value + Write-Message -Message "SID for AD is $SIDForAD" -Level Debug + $u = Get-DbaADObject -ADObject "$domain\$SIDForAD" -Type User -IdentityType Sid -EnableException + if ($null -eq $u -and $adLogin -like '*$') { + Write-Message -Message "Parsing Login as computer" -Level Verbose + $u = Get-DbaADObject -ADObject $adLogin -Type Computer -EnableException + $adType = 'Computer' + } else { + $adType = 'User' } - "None" { - Write-Message -Message "Search both logins and groups." -Level Verbose - $windowsLogins = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsUser' - $windowsGroups = $allWindowsLoginsGroups | Where-Object LoginType -eq 'WindowsGroup' + $foundUser = $u.GetUnderlyingObject() + $foundSid = $foundUser.ObjectSid.Value -join '' + if ($foundUser) { + $exists = $true } - } - foreach ($login in $windowsLogins) { - $adLogin = $login.Name - $loginSid = $login.Sid -join '' - $domain, $username = $adLogin.Split("\") - if ($domain.ToUpper() -in $IgnoreDomainsNormalized) { - Write-Message -Message "Skipping Login $adLogin." -Level Verbose - continue + if ($foundSid -ne $loginSid) { + Write-Message -Message "SID mismatch detected for $adLogin." -Level Warning + Write-Message -Message "SID mismatch detected for $adLogin (MSSQL: $loginSid, AD: $foundSid)." -Level Debug + $exists = $false } - Write-Message -Message "Parsing Login $adLogin." -Level Verbose - $exists = $false - try { - $loginBinary = [byte[]]$login.Sid - $SID = New-Object Security.Principal.SecurityIdentifier($loginBinary, 0) - $SIDForAD = $SID.Value - Write-Message -Message "SID for AD is $SIDForAD" -Level Debug - $u = Get-DbaADObject -ADObject "$domain\$SIDForAD" -Type User -IdentityType Sid -EnableException - if ($null -eq $u -and $adLogin -like '*$') { - Write-Message -Message "Parsing Login as computer" -Level Verbose - $u = Get-DbaADObject -ADObject $adLogin -Type Computer -EnableException - $adType = 'Computer' - } else { - $adType = 'User' - } - $foundUser = $u.GetUnderlyingObject() - $foundSid = $foundUser.ObjectSid.Value -join '' - if ($foundUser) { - $exists = $true - } - if ($foundSid -ne $loginSid) { - Write-Message -Message "SID mismatch detected for $adLogin." -Level Warning - Write-Message -Message "SID mismatch detected for $adLogin (MSSQL: $loginSid, AD: $foundSid)." -Level Debug - $exists = $false - } - if ($u.SamAccountName -ne $username) { - Write-Message -Message "SamAccountName mismatch detected for $adLogin." -Level Warning - Write-Message -Message "SamAccountName mismatch detected for $adLogin (MSSQL: $username, AD: $($u.SamAccountName))." -Level Debug - } - } catch { - Write-Message -Message "AD Searcher Error for $username." -Level Warning + if ($u.SamAccountName -ne $username) { + Write-Message -Message "SamAccountName mismatch detected for $adLogin." -Level Warning + Write-Message -Message "SamAccountName mismatch detected for $adLogin (MSSQL: $username, AD: $($u.SamAccountName))." -Level Debug } + } catch { + Write-Message -Message "AD Searcher Error for $username." -Level Warning + } - $uac = $foundUser.Properties.UserAccountControl - + $uac = $foundUser.Properties.UserAccountControl + + $additionalProps = @{ + AccountNotDelegated = $null + AllowReversiblePasswordEncryption = $null + CannotChangePassword = $null + PasswordExpired = $null + LockedOut = $null + Enabled = $null + PasswordNeverExpires = $null + PasswordNotRequired = $null + SmartcardLogonRequired = $null + TrustedForDelegation = $null + } + if ($uac) { $additionalProps = @{ - AccountNotDelegated = $null - AllowReversiblePasswordEncryption = $null - CannotChangePassword = $null - PasswordExpired = $null - LockedOut = $null - Enabled = $null - PasswordNeverExpires = $null - PasswordNotRequired = $null - SmartcardLogonRequired = $null - TrustedForDelegation = $null - } - if ($uac) { - $additionalProps = @{ - AccountNotDelegated = [bool]($uac.Value -band $mappingRaw['NOT_DELEGATED']) - AllowReversiblePasswordEncryption = [bool]($uac.Value -band $mappingRaw['ENCRYPTED_TEXT_PASSWORD_ALLOWED']) - CannotChangePassword = [bool]($uac.Value -band $mappingRaw['PASSWD_CANT_CHANGE']) - PasswordExpired = [bool]($uac.Value -band $mappingRaw['PASSWORD_EXPIRED']) - LockedOut = [bool]($uac.Value -band $mappingRaw['LOCKOUT']) - Enabled = !($uac.Value -band $mappingRaw['ACCOUNTDISABLE']) - PasswordNeverExpires = [bool]($uac.Value -band $mappingRaw['DONT_EXPIRE_PASSWD']) - PasswordNotRequired = [bool]($uac.Value -band $mappingRaw['PASSWD_NOTREQD']) - SmartcardLogonRequired = [bool]($uac.Value -band $mappingRaw['SMARTCARD_REQUIRED']) - TrustedForDelegation = [bool]($uac.Value -band $mappingRaw['TRUSTED_FOR_DELEGATION']) - UserAccountControl = $uac.Value - } - } - $rtn = [PSCustomObject]@{ - Server = $server.DomainInstanceName - Domain = $domain - Login = $username - Type = $adType - Found = $exists - DisabledInSQLServer = $login.IsDisabled - AccountNotDelegated = $additionalProps.AccountNotDelegated - AllowReversiblePasswordEncryption = $additionalProps.AllowReversiblePasswordEncryption - CannotChangePassword = $additionalProps.CannotChangePassword - PasswordExpired = $additionalProps.PasswordExpired - LockedOut = $additionalProps.LockedOut - Enabled = $additionalProps.Enabled - PasswordNeverExpires = $additionalProps.PasswordNeverExpires - PasswordNotRequired = $additionalProps.PasswordNotRequired - SmartcardLogonRequired = $additionalProps.SmartcardLogonRequired - TrustedForDelegation = $additionalProps.TrustedForDelegation - UserAccountControl = $additionalProps.UserAccountControl + AccountNotDelegated = [bool]($uac.Value -band $mappingRaw['NOT_DELEGATED']) + AllowReversiblePasswordEncryption = [bool]($uac.Value -band $mappingRaw['ENCRYPTED_TEXT_PASSWORD_ALLOWED']) + CannotChangePassword = [bool]($uac.Value -band $mappingRaw['PASSWD_CANT_CHANGE']) + PasswordExpired = [bool]($uac.Value -band $mappingRaw['PASSWORD_EXPIRED']) + LockedOut = [bool]($uac.Value -band $mappingRaw['LOCKOUT']) + Enabled = !($uac.Value -band $mappingRaw['ACCOUNTDISABLE']) + PasswordNeverExpires = [bool]($uac.Value -band $mappingRaw['DONT_EXPIRE_PASSWD']) + PasswordNotRequired = [bool]($uac.Value -band $mappingRaw['PASSWD_NOTREQD']) + SmartcardLogonRequired = [bool]($uac.Value -band $mappingRaw['SMARTCARD_REQUIRED']) + TrustedForDelegation = [bool]($uac.Value -band $mappingRaw['TRUSTED_FOR_DELEGATION']) + UserAccountControl = $uac.Value } + } + $rtn = [PSCustomObject]@{ + Server = $winLogin.Parent.DomainInstanceName + Domain = $domain + Login = $username + Type = $adType + Found = $exists + DisabledInSQLServer = $winLogin.IsDisabled + AccountNotDelegated = $additionalProps.AccountNotDelegated + AllowReversiblePasswordEncryption = $additionalProps.AllowReversiblePasswordEncryption + CannotChangePassword = $additionalProps.CannotChangePassword + PasswordExpired = $additionalProps.PasswordExpired + LockedOut = $additionalProps.LockedOut + Enabled = $additionalProps.Enabled + PasswordNeverExpires = $additionalProps.PasswordNeverExpires + PasswordNotRequired = $additionalProps.PasswordNotRequired + SmartcardLogonRequired = $additionalProps.SmartcardLogonRequired + TrustedForDelegation = $additionalProps.TrustedForDelegation + UserAccountControl = $additionalProps.UserAccountControl + } - Select-DefaultView -InputObject $rtn -ExcludeProperty AccountNotDelegated, AllowReversiblePasswordEncryption, CannotChangePassword, PasswordNeverExpires, SmartcardLogonRequired, TrustedForDelegation, UserAccountControl + Select-DefaultView -InputObject $rtn -ExcludeProperty AccountNotDelegated, AllowReversiblePasswordEncryption, CannotChangePassword, PasswordNeverExpires, SmartcardLogonRequired, TrustedForDelegation, UserAccountControl + } + foreach ($winLogin in $windowsGroups) { + $adLogin = $winLogin.Name + $loginSid = $winLogin.Sid -join '' + $domain, $groupName = $adLogin.Split("\") + if ($domain.ToUpper() -in $IgnoreDomainsNormalized) { + Write-Message -Message "Skipping Login $adLogin." -Level Verbose + continue } - - foreach ($login in $windowsGroups) { - $adLogin = $login.Name - $loginSid = $login.Sid -join '' - $domain, $groupName = $adLogin.Split("\") - if ($domain.ToUpper() -in $IgnoreDomainsNormalized) { - Write-Message -Message "Skipping Login $adLogin." -Level Verbose - continue + Write-Message -Message "Parsing Login $adLogin on $($_.Parent)." -Level Verbose + $exists = $false + try { + $loginBinary = [byte[]]$winLogin.Sid + $SID = New-Object Security.Principal.SecurityIdentifier($loginBinary, 0) + $SIDForAD = $SID.Value + Write-Message -Message "SID for AD is $SIDForAD" -Level Debug + $u = Get-DbaADObject -ADObject "$domain\$SIDForAD" -Type Group -IdentityType Sid -EnableException + $foundUser = $u.GetUnderlyingObject() + $foundSid = $foundUser.objectSid.Value -join '' + if ($foundUser) { + $exists = $true } - Write-Message -Message "Parsing Login $adLogin on $server." -Level Verbose - $exists = $false - try { - $loginBinary = [byte[]]$login.Sid - $SID = New-Object Security.Principal.SecurityIdentifier($loginBinary, 0) - $SIDForAD = $SID.Value - Write-Message -Message "SID for AD is $SIDForAD" -Level Debug - $u = Get-DbaADObject -ADObject "$domain\$SIDForAD" -Type Group -IdentityType Sid -EnableException - $foundUser = $u.GetUnderlyingObject() - $foundSid = $foundUser.objectSid.Value -join '' - if ($foundUser) { - $exists = $true - } - if ($foundSid -ne $loginSid) { - Write-Message -Message "SID mismatch detected for $adLogin." -Level Warning - Write-Message -Message "SID mismatch detected for $adLogin (MSSQL: $loginSid, AD: $foundSid)." -Level Debug - $exists = $false - } - if ($u.SamAccountName -ne $groupName) { - Write-Message -Message "SamAccountName mismatch detected for $adLogin." -Level Warning - Write-Message -Message "SamAccountName mismatch detected for $adLogin (MSSQL: $groupName, AD: $($u.SamAccountName))." -Level Debug - } - } catch { - Write-Message -Message "AD Searcher Error for $groupName on $server" -Level Warning + if ($foundSid -ne $loginSid) { + Write-Message -Message "SID mismatch detected for $adLogin." -Level Warning + Write-Message -Message "SID mismatch detected for $adLogin (MSSQL: $loginSid, AD: $foundSid)." -Level Debug + $exists = $false } - $rtn = [PSCustomObject]@{ - Server = $server.DomainInstanceName - Domain = $domain - Login = $groupName - Type = "Group" - Found = $exists - DisabledInSQLServer = $login.IsDisabled - AccountNotDelegated = $null - AllowReversiblePasswordEncryption = $null - CannotChangePassword = $null - PasswordExpired = $null - LockedOut = $null - Enabled = $null - PasswordNeverExpires = $null - PasswordNotRequired = $null - SmartcardLogonRequired = $null - TrustedForDelegation = $null - UserAccountControl = $null + if ($u.SamAccountName -ne $groupName) { + Write-Message -Message "SamAccountName mismatch detected for $adLogin." -Level Warning + Write-Message -Message "SamAccountName mismatch detected for $adLogin (MSSQL: $groupName, AD: $($u.SamAccountName))." -Level Debug } - - Select-DefaultView -InputObject $rtn -ExcludeProperty AccountNotDelegated, AllowReversiblePasswordEncryption, CannotChangePassword, PasswordNeverExpires, SmartcardLogonRequired, TrustedForDelegation, UserAccountControl - + } catch { + Write-Message -Message "AD Searcher Error for $groupName on $($_.Parent)" -Level Warning } + $rtn = [PSCustomObject]@{ + Server = $winLogin.Parent.DomainInstanceName + Domain = $domain + Login = $groupName + Type = "Group" + Found = $exists + DisabledInSQLServer = $winLogin.IsDisabled + AccountNotDelegated = $null + AllowReversiblePasswordEncryption = $null + CannotChangePassword = $null + PasswordExpired = $null + LockedOut = $null + Enabled = $null + PasswordNeverExpires = $null + PasswordNotRequired = $null + SmartcardLogonRequired = $null + TrustedForDelegation = $null + UserAccountControl = $null + } + + Select-DefaultView -InputObject $rtn -ExcludeProperty AccountNotDelegated, AllowReversiblePasswordEncryption, CannotChangePassword, PasswordNeverExpires, SmartcardLogonRequired, TrustedForDelegation, UserAccountControl } } } \ No newline at end of file diff --git a/readme.md b/readme.md index 78e35ed86f..5025e97a0c 100644 --- a/readme.md +++ b/readme.md @@ -224,7 +224,7 @@ To store credentials to disk, please read more at [Jaap Brasser's blog](https:// #### Servers with custom ports -If you use non-default ports and SQL Browser is disabled, you can access servers using a semicolon (functionality we've added) or a comma (the way Microsoft does it). +If you use non-default ports and SQL Browser is disabled, you can access servers using a colon (functionality we've added) or a comma (the way Microsoft does it). ```powershell -SqlInstance sql2017:55559 diff --git a/tests/Connect-DbaInstance.Tests.ps1 b/tests/Connect-DbaInstance.Tests.ps1 index 4c40e34df7..6688db6077 100644 --- a/tests/Connect-DbaInstance.Tests.ps1 +++ b/tests/Connect-DbaInstance.Tests.ps1 @@ -67,6 +67,11 @@ Describe "$commandname Integration Tests" -Tags "IntegrationTests" { $server.ConnectionContext.ConnectionString -match "Intent=ReadOnly" | Should Be $true } + It "keeps the same database context" { + $null = $server.Databases['msdb'].Tables.Count + $server.ConnectionContext.ExecuteScalar("select db_name()") | Should -Be 'master' + } + It "sets StatementTimeout to 0" { $server = Connect-DbaInstance -SqlInstance $script:instance1 -StatementTimeout 0 @@ -78,6 +83,13 @@ Describe "$commandname Integration Tests" -Tags "IntegrationTests" { $server.Databases.Name.Count -gt 0 | Should Be $true } + It "keeps the same database context when connected using a connection string" { + $server = Connect-DbaInstance -SqlInstance "Data Source=$script:instance1;Initial Catalog=tempdb;Integrated Security=True" + # Before #8962 this changed the context to msdb + $null = $server.Databases['msdb'].Tables.Count + $server.ConnectionContext.ExecuteScalar("select db_name()") | Should -Be 'tempdb' + } + It "connects using a dot" { $newinstance = $script:instance1.Replace("localhost", ".") Write-Warning "Connecting to $newinstance" diff --git a/tests/Test-DbaWindowsLogin.Tests.ps1 b/tests/Test-DbaWindowsLogin.Tests.ps1 index c0ffafef05..312629e0b0 100644 --- a/tests/Test-DbaWindowsLogin.Tests.ps1 +++ b/tests/Test-DbaWindowsLogin.Tests.ps1 @@ -11,7 +11,7 @@ Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan Describe "$CommandName Unit Tests" -Tag 'UnitTests' { Context "Validate parameters" { [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} - [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Login', 'ExcludeLogin', 'FilterBy', 'IgnoreDomains', 'EnableException' + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Login', 'ExcludeLogin', 'FilterBy', 'IgnoreDomains', 'InputObject', 'EnableException' $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters It "Should only contain our specific parameters" { (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 diff --git a/xml/dbatools.Format.ps1xml b/xml/dbatools.Format.ps1xml index 9f36df7e9f..fdebc09493 100644 --- a/xml/dbatools.Format.ps1xml +++ b/xml/dbatools.Format.ps1xml @@ -49,6 +49,8 @@ + + @@ -74,6 +76,9 @@ IsClustered + + IsHadrEnabled + ConnectedAs