Skip to content

Commit

Permalink
New-DbaDbUser - Refactoring (dataplat#8372)
Browse files Browse the repository at this point in the history
  • Loading branch information
andreasjordan authored May 26, 2022
1 parent 18cb00d commit 22d137e
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 147 deletions.
186 changes: 67 additions & 119 deletions functions/New-DbaDbUser.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ function New-DbaDbUser {
For MFA support, please use Connect-DbaInstance.
.PARAMETER Database
Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all databases will be processed.
Specifies the database(s) to process. Options for this list are auto-populated from the server. If unspecified, all user databases will be processed.
.PARAMETER ExcludeDatabase
Specifies the database(s) to exclude from processing. Options for this list are auto-populated from the server. By default, system databases are excluded.
.PARAMETER IncludeSystem
If this switch is enabled, the user will be added to system databases.
If this switch is enabled, the user will be added to system databases. This switch will be ignored if -Database is used.
.PARAMETER Login
When specified, the user will be associated to this SQL login and have the same name as the Login.
Expand All @@ -50,7 +50,7 @@ function New-DbaDbUser {
.NOTES
Tags: Database, User
Author: Frank Henninger (@osiris687)
Author: Frank Henninger (@osiris687) | Andreas Jordan (@JordanOrdix), ordix.de
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
Expand Down Expand Up @@ -80,19 +80,16 @@ function New-DbaDbUser {
Creates a new sql user named user1 mapped to Login1 in the specified database and specifies the default schema to be schema1.
#>
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "NoLogin", ConfirmImpact = "Medium")]
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Medium")]
param(
[parameter(Mandatory, Position = 1)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[object[]]$Database,
[object[]]$ExcludeDatabase,
[string[]]$Database,
[string[]]$ExcludeDatabase,
[switch]$IncludeSystem,
[parameter(ParameterSetName = "Login")]
[string]$Login,
[parameter(ParameterSetName = "NoLogin")]
[parameter(ParameterSetName = "Login")]
[string[]]$Username,
[string]$Username = $Login,
[string]$DefaultSchema = 'dbo',
[switch]$Force,
[switch]$EnableException
Expand All @@ -101,154 +98,105 @@ function New-DbaDbUser {
begin {
if ($Force) { $ConfirmPreference = 'none' }

function Test-SqlLoginInDatabase {
param(
[Microsoft.SqlServer.Management.Smo.Login]$Login,
[Microsoft.SqlServer.Management.Smo.Database]$Database
function Remove-User {
param (
[Microsoft.SqlServer.Management.Smo.User]$User
)

# Does user exist with same login?
if ( $existingUser = ( $Database.Users | Where-Object Login -eq $smoLogin ) ) {
if ($Force) {
if ($Pscmdlet.ShouldProcess($existingUser, "Dropping existing user $($existingUser.Name) because -Force was used")) {
try {
$existingUser.Drop()
} catch {
Stop-Function -Message "Could not remove existing user $($existingUser.Name), skipping." -Target $existingUser -ErrorRecord $_ -Exception $_.Exception.InnerException.InnerException.InnerException -Continue
}
}
} else {
Stop-Function -Message "User $($existingUser.Name) already exists and -Force was not specified" -Target $existingUser -Continue
}
}
}

function Test-SqlUserInDatabase {
param(
[string[]]$Username,
[Microsoft.SqlServer.Management.Smo.Database]$Database
)

# Does user exist with same login?
if ( $existingUser = ( $Database.Users | Where-Object Name -eq $Username ) ) {
if ($Force) {
if ($Pscmdlet.ShouldProcess($existingUser, "Dropping existing user $($existingUser.Name) because -Force was used")) {
try {
$existingUser.Drop()
} catch {
Stop-Function -Message "Could not remove existing user $($existingUser.Name), skipping." -Target $existingUser -ErrorRecord $_ -Exception $_.Exception.InnerException.InnerException.InnerException -Continue
}
if ($Force) {
if ($Pscmdlet.ShouldProcess($User, "Dropping existing user $($User.Name) in the database $($User.Parent.Name) on $instance because -Force was used")) {
try {
$User.Drop()
} catch {
Stop-Function -Message "Could not remove existing user $($User.Name) in the database $($User.Parent.Name) on $instance, skipping." -Target $User -ErrorRecord $_ -Continue
}
} else {
Stop-Function -Message "User $($existingUser.Name) already exists and -Force was not specified" -Target $existingUser -Continue
}
} else {
Stop-Function -Message "User $($User.Name) already exists in the database $($User.Parent.Name) on $instance and -Force was not specified, skipping." -Target $User -Continue
}
}
}

process {
if (-not $Login -and -not $Username) {
Stop-Function -Message "One of -Login or -Username is needed."
return
}

foreach ($instance in $SqlInstance) {
try {
$server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}

# Does the given login exist?
if ($Login) {
$existingLogin = $server.Logins | Where-Object Name -eq $Login
if (-not $existingLogin) {
Stop-Function -Message "Invalid Login: $Login is not found on $instance, skipping." -Target $instance -Continue
}
}

$databases = $server.Databases | Where-Object IsAccessible -eq $true

if ($Database) {
$databases = $databases | Where-Object Name -In $Database
} else {
if (-not $IncludeSystem) {
$databases = $databases | Where-Object IsSystemObject -ne $true
}
}
if ($ExcludeDatabase) {
$databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
}
if (Test-Bound 'IncludeSystem' -Not) {
$databases = $databases | Where-Object IsSystemObject -NE $true
}

if ($null -eq $databases -or $databases.Count -eq 0) {
Stop-Function -Message "Error occurred while establishing a connection to $Database" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}

foreach ($db in $databases) {
Write-Message -Level Verbose -Message "Add users to Database $db on target $server"

switch -Wildcard ($PSCmdlet.ParameterSetName) {
"Login*" {
# Creates a user with Login
Write-Message -Level VeryVerbose -Message "Using UserType: SqlLogin"

if ($PSBoundParameters.Keys -notcontains 'Login') {
Stop-Function -Message "Parameter -Login is required " -Target $instance
}
if ($Login.GetType().Name -eq 'Login') {
$smoLogin = $Login
} else {
#get the login associated with the given name.
$smoLogin = $server.Logins | Where-Object Name -eq $Login
if ($null -eq $smoLogin) {
Stop-Function -Message "Invalid Login: $Login is not found on $Server" -Target $instance;
return
}
}

Test-SqlLoginInDatabase -Database $db -Login $smoLogin

if ( $PSCmdlet.ParameterSetName -eq "LoginWithNewUsername" ) {
$Name = $Username
Write-Message -Level Verbose -Message "Using UserName: $Username"
} else {
$Name = $smoLogin.Name
Write-Message -Level Verbose -Message "Using LoginName: $Name"
}

$UserType = [Microsoft.SqlServer.Management.Smo.UserType]::SqlLogin
}
Write-Message -Level Verbose -Message "Add user $Username to database $db on $instance"

"NoLogin" {
# Creates a user without login
Write-Message -Level Verbose -Message "Using UserType: NoLogin"
$UserType = [Microsoft.SqlServer.Management.Smo.UserType]::NoLogin
$Name = $Username
}
} #switch
# Does a schema exist with the given name?
$existingSchema = $db.Schemas | Where-Object Name -eq $DefaultSchema
if (-not $existingSchema) {
Stop-Function -Message "Invalid DefaultSchema: $DefaultSchema is not found in the database $db on $instance, skipping." -Continue
}

# Does user exist with same name?
Test-SqlUserInDatabase -Database $db -Username $Name
# Does a user exist with the given name?
$existingUser = $db.Users | Where-Object Name -eq $Username
if ($existingUser) {
Remove-User -User $existingUser
}

# Check if DefaultSchema exists.
# Using a SQL query here because there is some unknown error occurring if the $db.Schemas.Name is accessed.
if (-not $db.Query("SELECT 1 FROM sys.schemas WHERE name = '$DefaultSchema'")) {
Stop-Function -Message "DefaultSchema $DefaultSchema does not exist in the database $db on $instance" -Continue
if ($Login) {
# Does a user exist with same login?
$existingUser = $db.Users | Where-Object Login -eq $Login
if ($existingUser) {
Remove-User -User $existingUser
}

Write-Message -Level Verbose -Message "Using UserType: SqlLogin"
$userType = [Microsoft.SqlServer.Management.Smo.UserType]::SqlLogin
} else {
Write-Message -Level Verbose -Message "Using UserType: NoLogin"
$userType = [Microsoft.SqlServer.Management.Smo.UserType]::NoLogin
}

if ($Pscmdlet.ShouldProcess($db, "Creating user $Name")) {
if ($Pscmdlet.ShouldProcess($db, "Creating user $Username")) {
try {
$smoUser = New-Object Microsoft.SqlServer.Management.Smo.User
$smoUser.Parent = $db
$smoUser.Name = $Name

if ( $PSBoundParameters.Keys -contains 'Login' -and $Login.GetType().Name -eq 'Login' ) {
$smoUser.Login = $Login
}
$smoUser.UserType = $UserType
$smoUser.Name = $Username
$smoUser.Login = $Login
$smoUser.UserType = $userType
$smoUser.DefaultSchema = $DefaultSchema

$smoUser.Create()
} catch {
Stop-Function -Message "Failed to add user $Name in $db to $instance" -Category InvalidOperation -ErrorRecord $_ -Target $instance -Continue
}
$smoUser.Refresh()
Write-Message -Level Verbose -Message "Successfully added $Username in $db to $instance."

if ( $PSBoundParameters.Keys -contains 'Username' -and $smoUser.Name -ne $Username ) {
$smoUser.Rename($Username)
# Display Results
Get-DbaDbUser -SqlInstance $server -Database $db.Name -User $Username
} catch {
Stop-Function -Message "Failed to add user $Username in $db to $instance" -Category InvalidOperation -ErrorRecord $_ -Target $instance -Continue
}

Write-Message -Level Verbose -Message "Successfully added $smoUser in $db to $instance."
}

#Display Results
Get-DbaDbUser -SqlInstance $instance -SqlCredential $SqlCredential -Database $db.Name | Where-Object name -eq $smoUser.Name
}
}
}
Expand Down
42 changes: 15 additions & 27 deletions tests/New-DbaDbUser.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,6 @@ Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
(@(Compare-Object -ReferenceObject ($knownParameters | Where-Object { $_ }) -DifferenceObject $params).Count ) | Should Be 0
}
}
Context "Should error out if the database does not exist" {
Mock Connect-DbaInstance -MockWith {
$obj = [PSCustomObject]@{
Databases = [String]::Empty
IsAccessible = $false
}
return $obj
} -ModuleName dbatools
It "Errors out when the databases does not exist and -EnableException is specified" {
{ New-DbaDbUser -SqlInstance localhost -Database 'NotAtAllReal' -Username $userName -EnableException } | Should -Throw
}
}
}

Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
Expand All @@ -33,32 +21,32 @@ Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {

$password = 'MyV3ry$ecur3P@ssw0rd'
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$null = New-DbaLogin -SqlInstance $script:instance3 -Login $userName -Password $securePassword -Force
$null = New-DbaDatabase -SqlInstance $script:instance3 -Name $dbname
$null = New-DbaLogin -SqlInstance $script:instance2 -Login $userName -Password $securePassword -Force
$null = New-DbaDatabase -SqlInstance $script:instance2 -Name $dbname
}
AfterAll {
$null = Remove-DbaDatabase -SqlInstance $script:instance3 -Database $dbname -Confirm:$false
$null = Remove-DbaLogin -SqlInstance $script:instance3 -Login $userName -Confirm:$false
$null = Remove-DbaDatabase -SqlInstance $script:instance2 -Database $dbname -Confirm:$false
$null = Remove-DbaLogin -SqlInstance $script:instance2 -Login $userName -Confirm:$false
}
Context "Test error handling" {
It "Tries to create the user with an invalid default schema" {
$results = New-DbaDbUser -SqlInstance $script:instance3 -Database $dbname -Login $userName -DefaultSchema invalidSchemaName -WarningVariable warningMessage
$results = New-DbaDbUser -SqlInstance $script:instance2 -Database $dbname -Login $userName -DefaultSchema invalidSchemaName -WarningVariable warningMessage
$results | Should -BeNullOrEmpty
$warningMessage | Should -BeLike "*DefaultSchema invalidSchemaName does not exist in the database*"
$warningMessage | Should -BeLike "*Invalid DefaultSchema*"
}
}
Context "Should create the user with login" {
It "Creates the user and get it" {
New-DbaDbUser -SqlInstance $script:instance3 -Database $dbname -Login $userName -DefaultSchema guest
$newDbUser = Get-DbaDbUser -SqlInstance $script:instance3 -Database $dbname | Where-Object Name -eq $userName
New-DbaDbUser -SqlInstance $script:instance2 -Database $dbname -Login $userName -DefaultSchema guest
$newDbUser = Get-DbaDbUser -SqlInstance $script:instance2 -Database $dbname | Where-Object Name -eq $userName
$newDbUser.Name | Should Be $userName
$newDbUser.DefaultSchema | Should -Be 'guest'
}
}
Context "Should create the user without login" {
It "Creates the user and get it. Login property is empty" {
New-DbaDbUser -SqlInstance $script:instance3 -Database $dbname -User $userNameWithoutLogin -DefaultSchema guest
$results = Get-DbaDbUser -SqlInstance $script:instance3 -Database $dbname | Where-Object Name -eq $userNameWithoutLogin
New-DbaDbUser -SqlInstance $script:instance2 -Database $dbname -User $userNameWithoutLogin -DefaultSchema guest
$results = Get-DbaDbUser -SqlInstance $script:instance2 -Database $dbname | Where-Object Name -eq $userNameWithoutLogin
$results.Name | Should Be $userNameWithoutLogin
$results.DefaultSchema | Should -Be 'guest'
$results.Login | Should -BeNullOrEmpty
Expand All @@ -71,15 +59,15 @@ Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {

$password = 'MyV3ry$ecur3P@ssw0rd'
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$null = New-DbaLogin -SqlInstance $script:instance3 -Login $loginName -Password $securePassword -Force
$null = New-DbaDatabase -SqlInstance $script:instance3 -Name $dbs
$null = New-DbaLogin -SqlInstance $script:instance2 -Login $loginName -Password $securePassword -Force
$null = New-DbaDatabase -SqlInstance $script:instance2 -Name $dbs
}
AfterAll {
$null = Remove-DbaDatabase -SqlInstance $script:instance3 -Database $dbs -Confirm:$false
$null = Remove-DbaLogin -SqlInstance $script:instance3 -Login $loginName -Confirm:$false
$null = Remove-DbaDatabase -SqlInstance $script:instance2 -Database $dbs -Confirm:$false
$null = Remove-DbaLogin -SqlInstance $script:instance2 -Login $loginName -Confirm:$false
}
It "Should add login to all databases provided" {
$results = New-DbaDbUser -SqlInstance $script:instance3 -Login $loginName -Database $dbs -Force -EnableException
$results = New-DbaDbUser -SqlInstance $script:instance2 -Login $loginName -Database $dbs -Force -EnableException
$results.Count | Should -Be 3
$results.Name | Should -Be $loginName, $loginName, $loginName
$results.DefaultSchema | Should -Be dbo, dbo, dbo
Expand Down
2 changes: 1 addition & 1 deletion tests/pester.groups.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ $TestsRunGroups = @{
'Read-DbaXEFile',
'Stop-DbaXESession',
'Test-DbaTempDbConfig',
'New-DbaDbUser',
#'New-DbaDbUser',
'Stop-DbaXESession',
'New-DbaLogin',
'Watch-DbaDbLogin',
Expand Down

0 comments on commit 22d137e

Please sign in to comment.