functions/Remove-SqlOrphanUser.ps1
Function Remove-SqlOrphanUser { <# .SYNOPSIS Drop orphan users with no existing login to map .DESCRIPTION An orphan user is defined by a user that does not have their matching login. (Login property = ""). If exists a login to map the drop will not be performed unless you specify the -Force parameter. .PARAMETER SqlServer The SQL Server instance. .PARAMETER SqlCredential Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted. To use: $scred = Get-Credential, then pass $scred object to the -SqlCredential parameter. Windows Authentication will be used if SqlCredential is not specified. SQL Server does not accept Windows credentials being passed as credentials. To connect as a different Windows user, run PowerShell as that user. .PARAMETER Users List of users to remove .PARAMETER Force If exists a login to map the drop will not be performed unless you specify this parameter. .NOTES Original Author: Cláudio Silva (@ClaudioESSilva) dbatools PowerShell module (https://dbatools.io, clemaire@gmail.com) Copyright (C) 2016 Chrissy LeMaire This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. .LINK https://dbatools.io/Remove-SqlOrphanUser .EXAMPLE Remove-SqlOrphanUser -SqlServer sql2005 Will find and drop all orphan users without matching login of all databases present on server 'sql2005' .EXAMPLE Remove-SqlOrphanUser -SqlServer sqlserver2014a -SqlCredential $cred Will find and drop all orphan users without matching login of all databases present on server 'sqlserver2014a'. Will be verified using SQL credentials. .EXAMPLE Remove-SqlOrphanUser -SqlServer sqlserver2014a -Databases db1, db2 -Force Will find all and drop orphan users even if exists their matching login on both db1 and db2 databases .EXAMPLE Remove-SqlOrphanUser -SqlServer sqlserver2014a -Users OrphanUser Will remove from all databases the user OrphanUser only if not have their matching login .EXAMPLE Remove-SqlOrphanUser -SqlServer sqlserver2014a -Users OrphanUser -Force Will remove from all databases the user OrphanUser EVEN if exists their matching login. #> [CmdletBinding(SupportsShouldProcess = $true)] Param ( [parameter(Mandatory = $true, ValueFromPipeline = $true)] [Alias("ServerInstance", "SqlInstance")] [object[]]$SqlServer, [object]$SqlCredential, [parameter(Mandatory = $false, ValueFromPipeline = $true)] [object[]]$Users, [switch]$Force ) DynamicParam { if ($SqlServer) { return Get-ParamSqlDatabases -SqlServer $SqlServer -SqlCredential $SqlCredential } } BEGIN { Write-Output "Attempting to connect to Sql Server.." $server = Connect-SqlServer -SqlServer $SqlServer -SqlCredential $SqlCredential } PROCESS { # Convert from RuntimeDefinedParameter object to regular array $databases = $psboundparameters.Databases if ($databases.Count -eq 0) { $databases = $server.Databases | Where-Object {$_.IsSystemObject -eq $false -and $_.IsAccessible -eq $true} } else { if ($pipedatabase.Length -gt 0) { $Source = $pipedatabase[0].parent.name $databases = $pipedatabase.name } else { $databases = $server.Databases | Where-Object {$_.IsSystemObject -eq $false -and $_.IsAccessible -eq $true -and ($databases -contains $_.Name)} } } $CallStack = Get-PSCallStack | Select-Object -Property * if ($CallStack.Count -eq 1) { $StackSource = $CallStack[0].Command } else { #-2 because index base is 0 and we want the one before the last (the last is the actual command) $StackSource = $CallStack[($CallStack.Count – 2)].Command } if ($databases.Count -gt 0) { $start = [System.Diagnostics.Stopwatch]::StartNew() foreach ($db in $databases) { try { #if SQL 2012 or higher only validate databases with ContainmentType = NONE if ($server.versionMajor -gt 10) { if ($db.ContainmentType -ne [Microsoft.SqlServer.Management.Smo.ContainmentType]::None) { Write-Warning "Database '$db' is a contained database. Contained databases can't have orphaned users. Skipping validation." Continue } } if ($StackSource -eq "Repair-SqlOrphanUser") { Write-Verbose "Call origin: Repair-SqlOrphanUser" #Will use collection from parameter ($Users) } else { Write-Output "Validating users on database '$db'" if ($Users.Count -eq 0) { $Users = $db.Users | Where {$_.Login -eq "" -and ("dbo","guest","sys","INFORMATION_SCHEMA" -notcontains $_.Name)} } else { if ($pipedatabase.Length -gt 0) { $Source = $pipedatabase[0].parent.name $Users = $pipedatabase.name } else { $Users = $db.Users | Where {$_.Login -eq "" -and ($Users -contains $_.Name)} } } } if ($Users.Count -gt 0) { Write-Output "Orphan users found" foreach ($User in $Users) { if ($server.versionMajor -gt 8) { $query = "DROP USER " + $User } else { $query = "EXEC master.dbo.sp_droplogin @loginame = N'$User'" } $ExistLogin = $null if ($StackSource -ne "Repair-SqlOrphanUser") { #do not need to validate Existing Login because the call come from Repair-SqlOrphanUser $ExistLogin = $server.logins | Where-Object {$_.Isdisabled -eq $False -and $_.IsSystemObject -eq $False -and $_.IsLocked -eq $False -and $_.Name -eq $User.Name } } if ($ExistLogin) { if ($Force) { if ($Pscmdlet.ShouldProcess($db.Name, "Dropping user '$($User.Name)' using -Force")) { $server.Databases[$db.Name].ExecuteNonQuery($query) | Out-Null Write-Output "User '$($User.Name)' was dropped. -Force parameter was used!" } } else { Write-Warning "Orphan user $($User.Name) have a matching login. The user will not be dropped. If you want to drop anyway, use -Force parameter." Continue } } else { if ($Pscmdlet.ShouldProcess($db.Name, "Dropping user '$($User.Name)'")) { $server.Databases[$db.Name].ExecuteNonQuery($query) | Out-Null Write-Output "User '$($User.Name)' was dropped." } } } } else { Write-Output "No orphan users found on database '$db'" } #reset collection $Users = $null } catch { throw $_ } } } else { Write-Output "There are no databases to analyse." } } END { $server.ConnectionContext.Disconnect() $totaltime = ($start.Elapsed) #If the call don't come from Repair-SqlOrphanUser function, show elapsed time if ($StackSource -ne "Repair-SqlOrphanUser") { Write-Output "Total Elapsed time: $totaltime" } } } |