From 7baafc51f0c70009fadbf8f5d480a2c54c09eafd Mon Sep 17 00:00:00 2001 From: Paul Shamus Date: Tue, 28 May 2019 09:57:53 -0700 Subject: [PATCH 01/10] Refactored SqlDatabaseRole to allow managing the role as well as its members --- .../MSFT_SqlDatabaseRole.psm1 | 554 ++++++++++++------ .../MSFT_SqlDatabaseRole.schema.mof | 10 +- .../en-US/MSFT_SqlDatabaseRole.strings.psd1 | 35 +- 3 files changed, 414 insertions(+), 185 deletions(-) diff --git a/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.psm1 b/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.psm1 index 00133ba40..5b3e998d3 100644 --- a/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.psm1 +++ b/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.psm1 @@ -8,25 +8,32 @@ $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_SqlDatabaseRole' <# .SYNOPSIS - Returns the current state of the user memberships in the role(s). - - .PARAMETER Ensure - Specifies the desired state of the membership of the role(s). - - .PARAMETER Name - Specifies the name of the login that evaluated if it is member of the role(s). + Returns the current state of the database role along with its membership. .PARAMETER ServerName - Specifies the SQL server on which the instance exist. + Specifies the host name of the SQL Server to be configured. .PARAMETER InstanceName - Specifies the SQL instance in which the database exist. + Specifies the name of the SQL instance to be configured. .PARAMETER Database - Specifies the database in which the login (user) and role(s) exist. + Specifies name of the database in which the role should be configured. + + .PARAMETER Name + Specifies the name of the database role to be added or removed. + + .PARAMETER Members + Specifies the members the database role should have. Existing members not included in this parameter will be + removed. + + .PARAMETER MembersToInclude + Specifies members the database role should include. Existing members will be left alone. - .PARAMETER Role - Specifies one or more roles to which the login (user) will be evaluated if it should be added or removed. + .PARAMETER MembersToExclude + Specifies members the database role should exclude. + + .PARAMETER Ensure + Specifies the desired state of the role. #> function Get-TargetResource { @@ -34,11 +41,6 @@ function Get-TargetResource [OutputType([System.Collections.Hashtable])] param ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $Name, - [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] @@ -56,84 +58,120 @@ function Get-TargetResource [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] + [System.String] + $Name, + + [Parameter()] [System.String[]] - $Role + $Members, + + [Parameter()] + [System.String[]] + $MembersToInclude, + + [Parameter()] + [System.String[]] + $MembersToExclude, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present' ) Write-Verbose -Message ( - $script:localizedData.GetDatabaseRole -f $Name, $Database, $InstanceName + $script:localizedData.GetDatabaseRoleProperties -f $Name ) $sqlServerObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName if ($sqlServerObject) { - # Check database exists - if ( -not ($sqlDatabaseObject = $sqlServerObject.Databases[$Database]) ) + $currentEnsure = 'Present' + + # Check if database exists. + if (-not ($sqlDatabaseObject = $sqlServerObject.Databases[$Database])) { + $currentEnsure = 'Absent' $errorMessage = $script:localizedData.DatabaseNotFound -f $Database New-ObjectNotFoundException -Message $errorMessage } - # Check role exists - foreach ($currentRole in $Role) + if ($sqlDatabaseRoleObject = $sqlDatabaseObject.Roles[$Name]) { - if ( -not ($sqlDatabaseObject.Roles[$currentRole]) ) + try { - $errorMessage = $script:localizedData.RoleNotFound -f $currentRole, $Database - New-ObjectNotFoundException -Message $errorMessage + [System.String[]] $roleMembers = $sqlDatabaseRoleObject.EnumMembers() + } + catch + { + $currentEnsure = 'Absent' + $errorMessage = $script:localizedData.EnumDatabaseRoleMemberNamesError -f $Name, $Database + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ } - } - - # Check login exists - if ( -not ($sqlServerObject.Logins[$Name]) ) - { - $errorMessage = $script:localizedData.LoginNotFound -f $Name - New-ObjectNotFoundException -Message $errorMessage - } - - $ensure = 'Absent' - $grantedRole = @() - if ($sqlDatabaseUser = $sqlDatabaseObject.Users[$Name] ) - { - foreach ($currentRole in $Role) + if ($Members) { - if ($sqlDatabaseUser.IsMember($currentRole)) + if ($MembersToInclude -or $MembersToExclude) { - Write-Verbose -Message ( - $script:localizedData.IsMember -f $Name, $currentRole, $Database - ) - - $grantedRole += $currentRole + $currentEnsure = 'Absent' + $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull + New-InvalidOperationException -Message $errorMessage } - else + + if ($null -ne (Compare-Object -ReferenceObject $roleMembers -DifferenceObject $Members)) { Write-Verbose -Message ( - $script:localizedData.IsNotMember -f $Name, $currentRole, $Database + $script:localizedData.DesiredMembersNotPresent -f $Name, $Database ) + $currentEnsure = 'Absent' } } - - if ( -not (Compare-Object -ReferenceObject $Role -DifferenceObject $grantedRole) ) + else { - $ensure = 'Present' + if ($MembersToInclude) + { + foreach ($memberName in $MembersToInclude) + { + if (-not ($memberName -in $roleMembers)) + { + Write-Verbose -Message ( + $script:localizedData.MemberNotPresent -f $memberName, $Name, $Database + ) + $currentEnsure = 'Absent' + } + } + } + + if ($MembersToExclude) + { + foreach ($memberName in $MembersToExclude) + { + if ($memberName -in $roleMembers) + { + Write-Verbose -Message ( + $script:localizedData.MemberPresent -f $memberName, $Name, $Database + ) + $currentEnsure = 'Absent' + } + } + } } } else { - Write-Verbose -Message ( - $script:localizedData.LoginIsNotUser -f $Name, $Database - ) + $currentEnsure = 'Absent' } } $returnValue = @{ - Ensure = $ensure - Name = $Name - ServerName = $ServerName - InstanceName = $InstanceName - Database = $Database - Role = $grantedRole + ServerName = $ServerName + InstanceName = $InstanceName + Database = $Database + Name = $Name + Members = $roleMembers + MembersToInclude = $MembersToInclude + MembersToExclude = $MembersToExclude + Ensure = $currentEnsure } $returnValue @@ -141,45 +179,40 @@ function Get-TargetResource <# .SYNOPSIS - Adds the login (user) to each of the provided roles when Ensure is set to 'Present'. - When Ensure is set to 'Absent' the login (user) will be removed from each of the provided roles. - If the login does not exist as a user in the database, then the user will be created in the database using the login. - - .PARAMETER Ensure - Specifies the desired state of the membership of the role(s). - - .PARAMETER Name - Specifies the name of the login that evaluated if it is member of the role(s), if it is not it will be added. - If the login does not exist as a user, a user will be created using the login. + Adds the role to the database and sets role membership when Ensure is set to 'Present'. When Ensure is set to + 'Absent' the role is removed from the database. .PARAMETER ServerName - Specifies the SQL server on which the instance exist. + Specifies the host name of the SQL Server to be configured. .PARAMETER InstanceName - Specifies the SQL instance in which the database exist. + Specifies the name of the SQL instance to be configured. .PARAMETER Database - Specifies the database in which the login (user) and role(s) exist. + Specifies name of the database in which the role should be configured. + + .PARAMETER Name + Specifies the name of the database role to be added or removed. + + .PARAMETER Members + Specifies the members the database role should have. Existing members not included in this parameter will be + removed. + + .PARAMETER MembersToInclude + Specifies members the database role should include. Existing members will be left alone. + + .PARAMETER MembersToExclude + Specifies members the database role should exclude. - .PARAMETER Role - Specifies one or more roles to which the login (user) will be added or removed. + .PARAMETER Ensure + Specifies the desired state of the role. #> + function Set-TargetResource { [CmdletBinding()] param ( - [Parameter()] - [ValidateSet('Present', 'Absent')] - [ValidateNotNullOrEmpty()] - [System.String] - $Ensure = 'Present', - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $Name, - [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] @@ -197,8 +230,29 @@ function Set-TargetResource [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] + [System.String] + $Name, + + [Parameter()] + [System.String[]] + $Members, + + [Parameter()] [System.String[]] - $Role + $MembersToInclude, + + [Parameter()] + [System.String[]] + $MembersToExclude, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present' + ) + + Write-Verbose -Message ( + $script:localizedData.SetDatabaseRoleProperties -f $Name ) $sqlServerObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName @@ -208,69 +262,123 @@ function Set-TargetResource switch ($Ensure) { - 'Present' + 'Absent' { - # Adding database user if it does not exist. - if ( -not ($sqlDatabaseObject.Users[$Name]) ) + try { - try + $sqlDatabaseRoleObjectToDrop = $sqlDatabaseObject.Roles[$Name] + if ($sqlDatabaseRoleObjectToDrop) { Write-Verbose -Message ( - '{0} {1}' -f - ($script:localizedData.LoginIsNotUser -f $Name, $Database), - $script:localizedData.AddingLoginAsUser + $script:localizedData.DropDatabaseRole -f $Name, $Database ) - - $sqlDatabaseUser = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.User' ` - -ArgumentList $sqlDatabaseObject, $Name - $sqlDatabaseUser.Login = $Name - $sqlDatabaseUser.Create() - } - catch - { - $errorMessage = $script:localizedData.FailedToAddUser -f $Name, $Database - New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + $sqlDatabaseRoleObjectToDrop.Drop() } } + catch + { + $errorMessage = $script:localizedData.DropDatabaseRoleError -f $Name, $Database + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + } + } - # Adding database user to the role. - foreach ($currentRole in $Role) + 'Present' + { + if ($null -eq $sqlDatabaseObject.Roles[$Name]) { try { - Write-Verbose -Message ( - $script:localizedData.AddUserToRole -f $Name, $currentRole, $Database - ) - - $sqlDatabaseRole = $sqlDatabaseObject.Roles[$currentRole] - $sqlDatabaseRole.AddMember($Name) + $newRoleObjectParams = @{ + TypeName = 'Microsoft.SqlServer.Management.Smo.DatabaseRole' + ArgumentList = @($sqlDatabaseObject, $Name) + } + $sqlDatabaseRoleObject = New-Object @newRoleObjectParams + if ($sqlDatabaseRoleObject) + { + Write-Verbose -Message ( + $script:localizedData.CreateDatabaseRole -f $Name, $Database + ) + $sqlDatabaseRoleObject.Create() + } } catch { - $errorMessage = $script:localizedData.FailedToAddUserToRole -f $Name, $currentRole, $Database + $errorMessage = $script:localizedData.CreateDatabaseRoleError -f $Name, $Database New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ } } - } - 'Absent' - { - try + if ($Members) { - foreach ($currentRole in $Role) + if ($MembersToInclude -or $MembersToExclude) { - Write-Verbose -Message ( - $script:localizedData.DropUserFromRole -f $Name, $currentRole, $Database - ) + $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull + New-InvalidOperationException -Message $errorMessage + } - $sqlDatabaseRole = $sqlDatabaseObject.Roles[$currentRole] - $sqlDatabaseRole.DropMember($Name) + $roleMembers = $sqlDatabaseObject.Roles[$Name].EnumMembers() + foreach ($memberName in $roleMembers) + { + if (-not ($memberName -in $Members)) + { + $removeMemberParams = @{ + SqlDatabaseObject = $sqlDatabaseObject + Name = $Name + Member = $memberName + } + Remove-SqlDscDatabaseRoleMember @removeMemberParams + } + } + + $roleMembers = $sqlDatabaseObject.Roles[$Name].EnumMembers() + foreach ($memberName in $Members) + { + if (-not ($memberName -in $roleMembers)) + { + $addMemberParams = @{ + SqlDatabaseObject = $sqlDatabaseObject + Name = $Name + Member = $memberName + } + Add-SqlDscDatabaseRoleMember @addMemberParams + } } } - catch + else { - $errorMessage = $script:localizedData.FailedToDropUserFromRole -f $Name, $currentRole, $Database - New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + if ($MembersToInclude) + { + $roleMembers = $sqlDatabaseObject.Roles[$Name].EnumMembers() + foreach ($memberName in $MembersToInclude) + { + if (-not ($memberName -in $roleMembers)) + { + $addMemberParams = @{ + SqlDatabaseObject = $sqlDatabaseObject + Name = $Name + Member = $memberName + } + Add-SqlDscDatabaseRoleMember @addMemberParams + } + } + } + + if ($MembersToExclude) + { + $roleMembers = $sqlDatabaseObject.Roles[$Name].EnumMembers() + foreach ($memberName in $MembersToExclude) + { + if ($memberName -in $roleMembers) + { + $removeMemberParams = @{ + SqlDatabaseObject = $sqlDatabaseObject + Name = $Name + Member = $memberName + } + Remove-SqlDscDatabaseRoleMember @removeMemberParams + } + } + } } } } @@ -279,25 +387,32 @@ function Set-TargetResource <# .SYNOPSIS - Tests if the login (user) has the desired state in each of the provided roles. - - .PARAMETER Ensure - Specifies the desired state of the membership of the role(s). - - .PARAMETER Name - Specifies the name of the login that evaluated if it is member of the role(s). + Tests the current state of the database role along with its membership. .PARAMETER ServerName - Specifies the SQL server on which the instance exist. + Specifies the host name of the SQL Server to be configured. .PARAMETER InstanceName - Specifies the SQL instance in which the database exist. + Specifies the name of the SQL instance to be configured. .PARAMETER Database - Specifies the database in which the login (user) and role(s) exist. + Specifies name of the database in which the role should be configured. + + .PARAMETER Name + Specifies the name of the database role to be added or removed. + + .PARAMETER Members + Specifies the members the database role should have. Existing members not included in this parameter will be + removed. - .PARAMETER Role - Specifies one or more roles to which the login (user) will be tested if it should added or removed. + .PARAMETER MembersToInclude + Specifies members the database role should include. Existing members will be left alone. + + .PARAMETER MembersToExclude + Specifies members the database role should exclude. + + .PARAMETER Ensure + Specifies the desired state of the role. #> function Test-TargetResource { @@ -305,17 +420,6 @@ function Test-TargetResource [OutputType([System.Boolean])] param ( - [Parameter()] - [ValidateSet('Present', 'Absent')] - [ValidateNotNullOrEmpty()] - [System.String] - $Ensure = 'Present', - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $Name, - [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] @@ -333,24 +437,41 @@ function Test-TargetResource [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] + [System.String] + $Name, + + [Parameter()] [System.String[]] - $Role + $Members, + + [Parameter()] + [System.String[]] + $MembersToInclude, + + [Parameter()] + [System.String[]] + $MembersToExclude, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present' ) Write-Verbose -Message ( - $script:localizedData.TestingConfiguration -f $Name, $Database, $InstanceName + $script:localizedData.TestDatabaseRoleProperties -f $Name ) $getTargetResourceParameters = @{ - InstanceName = $PSBoundParameters.InstanceName - ServerName = $PSBoundParameters.ServerName - Role = $PSBoundParameters.Role - Database = $PSBoundParameters.Database - Name = $PSBoundParameters.Name + ServerName = $PSBoundParameters.ServerName + InstanceName = $PSBoundParameters.InstanceName + Database = $PSBoundParameters.Database + Name = $PSBoundParameters.Name + Members = $PSBoundParameters.Members + MembersToInclude = $PSBoundParameters.MembersToInclude + MembersToExclude = $PSBoundParameters.MembersToExclude } - $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters - $isDatabaseRoleInDesiredState = $true switch ($Ensure) @@ -360,9 +481,8 @@ function Test-TargetResource if ($getTargetResourceResult.Ensure -ne 'Absent') { Write-Verbose -Message ( - $script:localizedData.NotInDesiredStateAbsent -f $Name, $Database + $script:localizedData.EnsureIsAbsent -f $Name ) - $isDatabaseRoleInDesiredState = $false } } @@ -372,22 +492,126 @@ function Test-TargetResource if ($getTargetResourceResult.Ensure -ne 'Present') { Write-Verbose -Message ( - $script:localizedData.NotInDesiredStatePresent -f $Name, $Database + $script:localizedData.EnsureIsPresent -f $Name ) - $isDatabaseRoleInDesiredState = $false } } } - if ($isDatabaseRoleInDesiredState) + $isDatabaseRoleInDesiredState +} + +<# + .SYNOPSIS + Adds a member to a database role in the SQL Server instance provided. + + .PARAMETER SqlDatabaseObject + A database object. + + .PARAMETER Name + String containing the name of the database role to add the member to. + + .PARAMETER Member + String containing the name of the member which should be added to the database role. +#> +function Add-SqlDscDatabaseRoleMember +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.Object] + $SqlDatabaseObject, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Name, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Member + ) + + $databaseName = $SqlDatabaseObject.Name + + if (-not ($SqlDatabaseObject.Roles[$Member] -or $SqlDatabaseObject.Users[$Member])) + { + $errorMessage = $script:localizedData.DatabaseRoleOrUserNotFound -f $Member, $databaseName + New-ObjectNotFoundException -Message $errorMessage + } + + try { Write-Verbose -Message ( - $script:localizedData.InDesiredState -f $Name, $Database + $script:localizedData.AddDatabaseRoleMember -f $Member, $Name, $databaseName ) + $SqlDatabaseObject.Roles[$Name].AddMember($Member) + } + catch + { + $errorMessage = $script:localizedData.AddDatabaseRoleMemberError -f $Member, $Name, $databaseName + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + } +} + +<# + .SYNOPSIS + Removes a member from a database role in the SQL Server instance provided. + + .PARAMETER SqlDatabaseObject + A database object. + + .PARAMETER Name + String containing the name of the database role to remove the member from. + + .PARAMETER Member + String containing the name of the member which should be removed from the database role. +#> +function Remove-SqlDscDatabaseRoleMember +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.Object] + $SqlDatabaseObject, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Name, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Member + ) + + $databaseName = $SqlDatabaseObject.Name + + if (-not ($SqlDatabaseObject.Roles[$Member] -or $SqlDatabaseObject.Users[$Member])) + { + $errorMessage = $script:localizedData.DatabaseRoleOrUserNotFound -f $Member, $databaseName + New-ObjectNotFoundException -Message $errorMessage } - return $isDatabaseRoleInDesiredState + try + { + Write-Verbose -Message ( + $script:localizedData.DropDatabaseRoleMember -f $Member, $Name, $databaseName + ) + $SqlDatabaseObject.Roles[$Name].DropMember($Member) + } + catch + { + $errorMessage = $script:localizedData.DropDatabaseRoleMemberError -f $Member, $Name, $databaseName + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + } } Export-ModuleMember -Function *-TargetResource diff --git a/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.schema.mof b/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.schema.mof index 85d405360..4a2c8a01e 100644 --- a/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.schema.mof +++ b/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.schema.mof @@ -1,10 +1,12 @@ [ClassVersion("1.0.0.0"), FriendlyName("SqlDatabaseRole")] class MSFT_SqlDatabaseRole : OMI_BaseResource { - [Write, Description("If 'Present' (the default value) then the login (user) will be added to the role(s). If 'Absent' then the login (user) will be removed from the role(s)."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; - [Key, Description("The name of the login that will become a member, or removed as a member, of the role(s).")] String Name; [Key, Description("The host name of the SQL Server to be configured.")] String ServerName; [Key, Description("The name of the SQL instance to be configured.")] String InstanceName; - [Key, Description("The database in which the login (user) and role(s) exist.")] String Database; - [Required, Description("One or more roles to which the login (user) will be added or removed.")] String Role[]; + [Key, Description("The name of the database in which the role should be configured.")] String Database; + [Key, Description("The name of the database role to be added or removed.")] String Name; + [Write, Description("The members the database role should have. This parameter will replace all the current database role members with the specified members. Can only be used when parameter Ensure is set to 'Present'.")] String Members[]; + [Write, Description("The members the database role should include. This parameter will only add members to a database role. Can only be used when parameter Ensure is set to 'Present'. Can not be used at the same time as parameter Members.")] String MembersToInclude[]; + [Write, Description("The members the database role should exclude. This parameter will only remove members from a database role. Can only be used when parameter Ensure is set to 'Present'. Can not be used at the same time as parameter Members.")] String MembersToExclude[]; + [Write, Description("If 'Present' (the default value) then the role will be added to the database and the role membership will be set. If 'Absent' then the role will be removed from the database."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; }; diff --git a/DSCResources/MSFT_SqlDatabaseRole/en-US/MSFT_SqlDatabaseRole.strings.psd1 b/DSCResources/MSFT_SqlDatabaseRole/en-US/MSFT_SqlDatabaseRole.strings.psd1 index b377825a8..daaa5df70 100644 --- a/DSCResources/MSFT_SqlDatabaseRole/en-US/MSFT_SqlDatabaseRole.strings.psd1 +++ b/DSCResources/MSFT_SqlDatabaseRole/en-US/MSFT_SqlDatabaseRole.strings.psd1 @@ -1,19 +1,22 @@ ConvertFrom-StringData @' - GetDatabaseRole = Getting current role(s) for the user '{0}' of the database '{1}' on the instance '{2}'. + AddDatabaseRoleMember = Adding member '{0}' to role '{1}' in database '{2}'. + AddDatabaseRoleMemberError = Failed to add member '{0}' to role '{1}' in database '{2}'. + CreateDatabaseRole = Creating role '{0}' in database '{1}'. + CreateDatabaseRoleError = Failed to create role '{0}' in database '{1}'. DatabaseNotFound = The database '{0}' does not exist. - RoleNotFound = The role '{0}' does not exist in the database '{1}'. - LoginNotFound = The login '{0}' does not exist on the instance. - IsMember = The login '{0}' is a member of the role '{1}' in the database '{2}'. - IsNotMember = The login '{0}' is not a member of the role '{1}' in the database '{2}'. - LoginIsNotUser = The login '{0}' is not a user in the database '{1}'. - AddingLoginAsUser = Adding the login as a user of the database. - FailedToAddUser = Failed to add the login '{0}' as a user of the database '{1}'. - AddUserToRole = Adding the user (login) '{0}' to the role '{1}' in the database '{2}'. - FailedToAddUserToRole = Failed to add the user {0} to the role {1} in the database {2}. - DropUserFromRole = Removing the user (login) '{0}' from the role '{1}' in the database '{2}'. - FailedToDropUserFromRole = Failed to remove the login {0} from the role {1} in the database {2}. - TestingConfiguration = Determines if the the user '{0}' of the database '{1}' on the instance '{2}' is a member of the desired role(s). - InDesiredState = The user '{0}' of the database '{1}' is member of the specified role(s). - NotInDesiredStateAbsent = Expected the user '{0}' to not be a member of the specified role(s) in the database '{1}', but the user was member of at least one of the roles. - NotInDesiredStatePresent = Expected the user '{0}' to be a member of the specified role(s) in the database '{1}', but the user was not a member of at least one of the roles. + DatabaseRoleOrUserNotFound = The role or user '{0}' does not exist in database '{1}'. + DesiredMembersNotPresent = One or more of the desired members are not present in the role '{0}' in database '{1}'. + DropDatabaseRole = Removing role '{0}' from database '{1}'. + DropDatabaseRoleError = Failed to drop the role '{0}' in database '{1}'. + DropDatabaseRoleMember = Removing member '{0}' from role '{1}' in database '{2}'. + DropDatabaseRoleMemberError = Failed to drop member '{0}' from role '{1}' in database '{2}'. + EnsureIsAbsent = Ensure is set to Absent. The existing role '{0}' should be removed. + EnsureIsPresent = Ensure is set to Present. Either the role '{0}' is missing and should be created, or members in the role are not in the desired state. + EnumDatabaseRoleMemberNamesError = Failed to enumerate members of the role '{0}' in database '{1}'. + GetDatabaseRoleProperties = Getting properties of the SQL database role '{0}'. + MemberNotPresent = The user '{0}' is not a member of the role '{1}' in database '{2}'. + MemberPresent = The user '{0}' should not be a member of the role '{1}' in database '{2}'. + MembersToIncludeAndExcludeParamMustBeNull = The parameter MembersToInclude and/or MembersToExclude must not be set, or be set to $null, when parameter Members are used. + SetDatabaseRoleProperties = Setting properties of the SQL database role '{0}'. + TestDatabaseRoleProperties = Testing the desired state of the SQL database role '{0}'. '@ From 1175557e0f1da50e592fef2d6cb96266da34ae1c Mon Sep 17 00:00:00 2001 From: Paul Shamus Date: Tue, 28 May 2019 09:59:18 -0700 Subject: [PATCH 02/10] Updated SqlDatabaseRole examples --- .../SqlDatabaseRole/1-AddDatabaseRole.ps1 | 20 +++++++++--- .../SqlDatabaseRole/2-RemoveDatabaseRole.ps1 | 12 +++---- .../3-EnforceDatabaseRoleMembers.ps1 | 31 +++++++++++++++++++ .../4-MembersToIncludeInDatabaseRole.ps1 | 31 +++++++++++++++++++ .../5-MembersToExcludeFromDatabaseRole.ps1 | 31 +++++++++++++++++++ 5 files changed, 114 insertions(+), 11 deletions(-) create mode 100644 Examples/Resources/SqlDatabaseRole/3-EnforceDatabaseRoleMembers.ps1 create mode 100644 Examples/Resources/SqlDatabaseRole/4-MembersToIncludeInDatabaseRole.ps1 create mode 100644 Examples/Resources/SqlDatabaseRole/5-MembersToExcludeFromDatabaseRole.ps1 diff --git a/Examples/Resources/SqlDatabaseRole/1-AddDatabaseRole.ps1 b/Examples/Resources/SqlDatabaseRole/1-AddDatabaseRole.ps1 index 3e8d23d75..34c47b727 100644 --- a/Examples/Resources/SqlDatabaseRole/1-AddDatabaseRole.ps1 +++ b/Examples/Resources/SqlDatabaseRole/1-AddDatabaseRole.ps1 @@ -1,8 +1,9 @@ <# .EXAMPLE - This example shows how to ensure that the user account CONTOSO\SQLAdmin - has "MyRole" and "MySecondRole" SQL database roles. + This example shows how to ensure that the database roles named ReportEditor and ReportViewer is present in the + AdventureWorks database on instance sqltest.company.local\DSC. #> + Configuration Example { param @@ -16,14 +17,23 @@ Configuration Example node localhost { - SqlDatabaseRole Add_Database_Role + SqlDatabaseRole ReportEditor_AddRole { + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + Database = 'AdventureWorks' + Name = 'ReportEditor' Ensure = 'Present' + PsDscRunAsCredential = $SqlAdministratorCredential + } + + SqlDatabaseRole ReportViewer_AddRole + { ServerName = 'sqltest.company.local' InstanceName = 'DSC' - Name = 'CONTOSO\SQLAdmin' - Role = 'MyRole', 'MySecondRole' Database = 'AdventureWorks' + Name = 'ReportViewer' + Ensure = 'Present' PsDscRunAsCredential = $SqlAdministratorCredential } } diff --git a/Examples/Resources/SqlDatabaseRole/2-RemoveDatabaseRole.ps1 b/Examples/Resources/SqlDatabaseRole/2-RemoveDatabaseRole.ps1 index cf088c6c3..6a13bfdeb 100644 --- a/Examples/Resources/SqlDatabaseRole/2-RemoveDatabaseRole.ps1 +++ b/Examples/Resources/SqlDatabaseRole/2-RemoveDatabaseRole.ps1 @@ -1,8 +1,9 @@ <# .EXAMPLE - This example shows how to ensure that the user account CONTOSO\SQLAdmin - is not member of the "DeleteRole" SQL database role. + This example shows how to ensure that the database role named ReportViewer is not present in the AdventureWorks + database on instance sqltest.company.local\DSC. #> + Configuration Example { param @@ -16,14 +17,13 @@ Configuration Example node localhost { - SqlDatabaseRole Remove_Database_Role + SqlDatabaseRole ReportViewer_DropRole { - Ensure = 'Absent' ServerName = 'sqltest.company.local' InstanceName = 'DSC' - Name = 'CONTOSO\SQLAdmin' - Role = 'DeleteRole' Database = 'AdventureWorks' + Name = 'ReportViewer' + Ensure = 'Absent' PsDscRunAsCredential = $SqlAdministratorCredential } } diff --git a/Examples/Resources/SqlDatabaseRole/3-EnforceDatabaseRoleMembers.ps1 b/Examples/Resources/SqlDatabaseRole/3-EnforceDatabaseRoleMembers.ps1 new file mode 100644 index 000000000..ff3e95258 --- /dev/null +++ b/Examples/Resources/SqlDatabaseRole/3-EnforceDatabaseRoleMembers.ps1 @@ -0,0 +1,31 @@ +<# +.EXAMPLE + This example shows how to ensure that the database role named ReportViewer is present in the AdventureWorks + database on instance sqltest.company.local\DSC and that only users CONTOSO\Barbara and CONTOSO\Fred are members of + this role. +#> + +Configuration Example +{ + param( + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $SqlAdministratorCredential + ) + + Import-DscResource -ModuleName SqlServerDsc + + node localhost + { + SqlDatabaseRole ReportViewer_EnforceRoleMembers + { + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + Database = 'AdventureWorks' + Name = 'ReportViewer' + Members = @('CONTOSO\Barbara', 'CONTOSO\Fred') + Ensure = 'Present' + PsDscRunAsCredential = $SqlAdministratorCredential + } + } +} diff --git a/Examples/Resources/SqlDatabaseRole/4-MembersToIncludeInDatabaseRole.ps1 b/Examples/Resources/SqlDatabaseRole/4-MembersToIncludeInDatabaseRole.ps1 new file mode 100644 index 000000000..72259d6e0 --- /dev/null +++ b/Examples/Resources/SqlDatabaseRole/4-MembersToIncludeInDatabaseRole.ps1 @@ -0,0 +1,31 @@ +<# +.EXAMPLE + This example shows how to ensure that the database role named ReportViewer is present in the AdventureWorks + database on instance sqltest.company.local\DSC and that users CONTOSO\Barbara and CONTOSO\Fred are added as members + of this role. +#> + +Configuration Example +{ + param( + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $SqlAdministratorCredential + ) + + Import-DscResource -ModuleName SqlServerDsc + + node localhost + { + SqlDatabaseRole ReportViewer_IncludeRoleMembers + { + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + Database = 'AdventureWorks' + Name = 'ReportViewer' + MembersToInclude = @('CONTOSO\Barbara', 'CONTOSO\Fred') + Ensure = 'Present' + PsDscRunAsCredential = $SqlAdministratorCredential + } + } +} diff --git a/Examples/Resources/SqlDatabaseRole/5-MembersToExcludeFromDatabaseRole.ps1 b/Examples/Resources/SqlDatabaseRole/5-MembersToExcludeFromDatabaseRole.ps1 new file mode 100644 index 000000000..3e888e245 --- /dev/null +++ b/Examples/Resources/SqlDatabaseRole/5-MembersToExcludeFromDatabaseRole.ps1 @@ -0,0 +1,31 @@ +<# +.EXAMPLE + This example shows how to ensure that the database role named ReportViewer is present in the AdventureWorks + database on instance sqltest.company.local\DSC and that users CONTOSO\Barbara and CONTOSO\Fred are not members of + this role. +#> + +Configuration Example +{ + param( + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $SqlAdministratorCredential + ) + + Import-DscResource -ModuleName SqlServerDsc + + node localhost + { + SqlDatabaseRole ReportViewer_ExcludeRoleMembers + { + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + Database = 'AdventureWorks' + Name = 'ReportViewer' + MembersToExclude = @('CONTOSO\Barbara', 'CONTOSO\Fred') + Ensure = 'Present' + PsDscRunAsCredential = $SqlAdministratorCredential + } + } +} From 1c2f7493a510db01539c56b89841e597a88c284a Mon Sep 17 00:00:00 2001 From: Paul Shamus Date: Tue, 28 May 2019 10:03:08 -0700 Subject: [PATCH 03/10] Updated README and CHANGELOG --- CHANGELOG.md | 8 ++++++++ README.md | 46 +++++++++++++++++++++++++++++++--------------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b9abe1d9..0099c5570 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,14 @@ of showing a message saying that connect failed another unrelated error message could have been shown, because of an error in the code. - Fix typo in test it block. +- Changes to SqlDatabaseRole + - BREAKING CHANGE: Refactored to enable creation/deletion of the database role + itself as well as management of the role members. *Note that the resource no + longer adds database users.* ([issue #845](https://github.com/PowerShell/SqlServerDsc/issues/845), + [issue #847](https://github.com/PowerShell/SqlServerDsc/issues/847), + [issue #1252](https://github.com/PowerShell/SqlServerDsc/issues/1252), + [issue #1339](https://github.com/PowerShell/SqlServerDsc/issues/1339)). + [Paul Shamus @pshamus](https://github.com/pshamus) ## 12.5.0.0 diff --git a/README.md b/README.md index f285c7164..bb075512c 100644 --- a/README.md +++ b/README.md @@ -126,8 +126,7 @@ A full list of changes in each version can be found in the [change log](CHANGELO manage SQL database permissions. * [**SqlDatabaseRecoveryModel**](#sqldatabaserecoverymodel) resource to manage database recovery model. -* [**SqlDatabaseRole**](#sqldatabaserole) resource to manage SQL - database roles. +* [**SqlDatabaseRole**](#sqldatabaserole) resource to manage SQL database roles. * [**SqlRS**](#sqlrs) configures SQL Server Reporting. Services to use a database engine in another instance. * [**SqlRSSetup**](#sqlrssetup) Installs the standalone @@ -709,8 +708,9 @@ All issues are not listed here, see [here for all open issues](https://github.co ### SqlDatabaseRole -This resource is used to add or remove role for a login in a database. -Read more about database role in this article [CREATE ROLE (Transact-SQL)](https://msdn.microsoft.com/en-us/library/ms187936.aspx) +This resource is used to create a database role when Ensure is set to 'Present' +or remove a database role when Ensure is set to 'Absent'. The resource also +manages members in both built-in and user created database roles. #### Requirements @@ -719,22 +719,38 @@ Read more about database role in this article [CREATE ROLE (Transact-SQL)](https #### Parameters -* **`[String]` Name** _(Key)_: The name of the login that will become a member, or - removed as a member, of the role(s). * **`[String]` ServerName** _(Key)_: The host name of the SQL Server to be configured. * **`[String]` InstanceName** _(Key)_: The name of the SQL instance to be configured. -* **`[String]` Database** _(Key)_: The database in which the login (user) and role(s) - exist. -* **`[String]` Ensure** _(Write)_: If 'Present' (the default value) then the login - (user) will be added to the role(s). If 'Absent' then the login (user) will be - removed from the role(s). { *Present* | Absent }. -* **`[String[]]` Role**_(Required): One or more roles to which the login (user) will - be added or removed. +* **`[String]` Database** _(Key)_: The name of the database in which the role should + be configured. +* **`[String]` Name** _(Key)_: The name of the database role to be added or removed. +* **`[String[]]` Members** _(Write)_: The members the database role should have. + This parameter will replace all the current database role members with the + specified members. Can only be used when parameter Ensure is set to 'Present'. +* **`[String[]]` MembersToInclude** _(Write)_: The members the database role should + include. This parameter will only add members to a database role. Can only + be used when parameter Ensure is set to 'Present'. Can not be used at the same + time as parameter Members. +* **`[String[]]` MembersToExclude** _(Write)_: The members the database role should + exclude. This parameter will only remove members from a database role. Can only + be used when parameter Ensure is set to 'Present'. Can not be used at the same + time as parameter Members. +* **`[String]` Ensure** _(Write)_: If 'Present' (the default value) then the role + will be added to the database and the role membership will be set. If 'Absent' + then the role will be removed from the database. { *Present* | Absent }. + +#### Read-Only Properties from Get-TargetResource + +* **`[String]` MembersInDesiredState** _(Read)_: Indicates whether the database + role members are in the desired state. #### Examples -* [Add Role of a database](/Examples/Resources/SqlDatabaseRole/1-AddDatabaseRole.ps1) -* [Remove Role of a database](/Examples/Resources/SqlDatabaseRole/2-RemoveDatabaseRole.ps1) +* [Add Role to a database](/Examples/Resources/SqlDatabaseRole/1-AddDatabaseRole.ps1) +* [Remove Role from a database](/Examples/Resources/SqlDatabaseRole/2-RemoveDatabaseRole.ps1) +* [Enforce Role membership](/Examples/Resources/SqlDatabaseRole/3-EnforceDatabaseRoleMembers.ps1) +* [Members to include in database role](/Examples/Resources/SqlDatabaseRole/4-MembersToIncludeInDatabaseRole.ps1) +* [Members to exclude from database role](/Examples/Resources/SqlDatabaseRole/5-MembersToExcludeFromDatabaseRole.ps1) #### Known issues From 24fa68496fa45f752e6e10a5ae7831b2d458eb9f Mon Sep 17 00:00:00 2001 From: Paul Shamus Date: Tue, 28 May 2019 11:03:01 -0700 Subject: [PATCH 04/10] Updated SqlDatabaseRole unit tests --- Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 | 957 ++++++++++++++++------ 1 file changed, 701 insertions(+), 256 deletions(-) diff --git a/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 b/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 index 4ff6e1c94..8ded92af5 100644 --- a/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 @@ -22,10 +22,10 @@ $script:dscResourceName = 'MSFT_SqlDatabaseRole' # Unit Test Template Version: 1.2.0 $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) -if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` - (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +if ((-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1')))) { - & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) + & git.exe @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) } Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force @@ -57,24 +57,35 @@ try $mockServerName = 'localhost' $mockInstanceName = 'MSSQLSERVER' $mockSqlDatabaseName = 'AdventureWorks' - $mockSqlServerLogin = 'John' - $mockSqlServerLoginOne = 'CONTOSO\KingJulian' - $mockSqlServerLoginTwo = 'CONTOSO\SQLAdmin' - $mockSqlServerLoginType = 'WindowsUser' - $mockSqlDatabaseRole = 'MyRole' - $mockSqlDatabaseRoleSecond = 'MySecondRole' + + $mockSqlServerLogin1 = 'John' + $mockSqlServerLogin1Type = 'WindowsUser' + $mockSqlServerLogin2 = 'CONTOSO\KingJulian' + $mockSqlServerLogin2Type = 'WindowsGroup' + $mockSqlServerLogin3 = 'CONTOSO\SQLAdmin' + $mockSqlServerLogin3Type = 'WindowsGroup' + $mockSqlServerInvalidLogin = 'KingJulian' + + $mockSqlDatabaseRole1 = 'MyRole' + $mockSqlDatabaseRole2 = 'MySecondRole' + $mockSqlDatabaseRole3 = 'NewRole' + + $mockEnumMembers = @($mockSqlServerLogin1, $mockSqlServerLogin2) + $mockExpectedSqlDatabaseRole = 'MyRole' + $mockInvalidOperationForAddMemberMethod = $false - $mockInvalidOperationForDropMemberMethod = $false $mockInvalidOperationForCreateMethod = $false - $mockExpectedForAddMemberMethod = 'MySecondRole' - $mockExpectedForDropMemberMethod = 'MyRole' - $mockExpectedForCreateMethod = 'John' + $mockInvalidOperationForDropMethod = $false + $mockInvalidOperationForDropMemberMethod = $false + + $mockExpectedMemberToAdd = 'MySecondRole' + $mockExpectedMemberToDrop = 'MyRole' # Default parameters that are used for the It-blocks $mockDefaultParameters = @{ - InstanceName = $mockInstanceName ServerName = $mockServerName + InstanceName = $mockInstanceName } #region Function mocks @@ -82,25 +93,25 @@ try return @( ( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name InstanceName -Value $mockInstanceName -PassThru | - Add-Member -MemberType NoteProperty -Name ComputerNamePhysicalNetBIOS -Value $mockServerName -PassThru | - Add-Member -MemberType ScriptProperty -Name Databases -Value { + Add-Member -MemberType NoteProperty -Name InstanceName -Value $mockInstanceName -PassThru | + Add-Member -MemberType NoteProperty -Name ComputerNamePhysicalNetBIOS -Value $mockServerName -PassThru | + Add-Member -MemberType ScriptProperty -Name Databases -Value { return @{ $mockSqlDatabaseName = @(( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name Name -Value $mockSqlDatabaseName -PassThru | - Add-Member -MemberType ScriptProperty -Name Users -Value { + Add-Member -MemberType NoteProperty -Name Name -Value $mockSqlDatabaseName -PassThru | + Add-Member -MemberType ScriptProperty -Name Users -Value { return @{ - $mockSqlServerLoginOne = @(( + $mockSqlServerLogin1 = @(( New-Object -TypeName Object | - Add-Member -MemberType ScriptMethod -Name IsMember -Value { + Add-Member -MemberType ScriptMethod -Name IsMember -Value { param ( [Parameter()] [System.String] - $mockSqlDatabaseRole + $Name ) - if ( $mockSqlDatabaseRole -eq $mockExpectedSqlDatabaseRole ) + if ($Name -eq $mockExpectedSqlDatabaseRole) { return $true } @@ -110,89 +121,118 @@ try } } -PassThru )) - $mockSqlServerLoginTwo = @(( + $mockSqlServerLogin2 = @(( + New-Object -TypeName Object | + Add-Member -MemberType ScriptMethod -Name IsMember -Value { + return $true + } -PassThru + )) + $mockSqlServerLogin3 = @(( New-Object -TypeName Object | - Add-Member -MemberType ScriptMethod -Name IsMember -Value { + Add-Member -MemberType ScriptMethod -Name IsMember -Value { return $true } -PassThru )) + } } -PassThru | - Add-Member -MemberType ScriptProperty -Name Roles -Value { + Add-Member -MemberType ScriptProperty -Name Roles -Value { return @{ - $mockSqlDatabaseRole = @(( + $mockSqlDatabaseRole1 = @(( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name Name -Value $mockSqlDatabaseRole -PassThru | - Add-Member -MemberType ScriptMethod -Name AddMember -Value { + Add-Member -MemberType NoteProperty -Name Name -Value $mockSqlDatabaseRole1 -PassThru | + Add-Member -MemberType ScriptMethod -Name AddMember -Value { param ( [Parameter()] [System.String] - $mockSqlServerLogin + $Name ) if ($mockInvalidOperationForAddMemberMethod) { throw 'Mock AddMember Method was called with invalid operation.' } - if ( $this.Name -ne $mockExpectedForAddMemberMethod ) + if ($Name -ne $mockExpectedMemberToAdd) { throw "Called mocked AddMember() method without adding the right user. Expected '{0}'. But was '{1}'." ` - -f $mockExpectedForAddMemberMethod, $this.Name + -f $mockExpectedMemberToAdd, $Name } } -PassThru | - Add-Member -MemberType ScriptMethod -Name DropMember -Value { + Add-Member -MemberType ScriptMethod -Name Drop -Value { + if ($mockInvalidOperationForDropMethod) + { + throw 'Mock Drop Method was called with invalid operation.' + } + + if ($Name -ne $mockExpectedSqlDatabaseRole) + { + throw "Called mocked Drop() method without dropping the right database role. Expected '{0}'. But was '{1}'." ` + -f $mockExpectedSqlDatabaseRole, $Name + } + } -PassThru | + Add-Member -MemberType ScriptMethod -Name DropMember -Value { param ( [Parameter()] [System.String] - $mockSqlServerLogin + $Name ) if ($mockInvalidOperationForDropMemberMethod) { throw 'Mock DropMember Method was called with invalid operation.' } - if ( $this.Name -ne $mockExpectedForDropMemberMethod ) + if ($Name -ne $mockExpectedMemberToDrop) { throw "Called mocked Drop() method without adding the right user. Expected '{0}'. But was '{1}'." ` - -f $mockExpectedForDropMemberMethod, $this.Name + -f $mockExpectedMemberToDrop, $Name + } + } -PassThru | + Add-Member -MemberType ScriptMethod -Name EnumMembers -Value { + if ($mockInvalidOperationForEnumMethod) + { + throw 'Mock EnumMembers Method was called with invalid operation.' + } + else + { + $mockEnumMembers } } -PassThru )) - $mockSqlDatabaseRoleSecond = @(( + $mockSqlDatabaseRole2 = @(( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name Name -Value $mockSqlDatabaseRoleSecond -PassThru | - Add-Member -MemberType ScriptMethod -Name AddMember -Value { + Add-Member -MemberType NoteProperty -Name Name -Value $mockSqlDatabaseRole2 -PassThru | + Add-Member -MemberType ScriptMethod -Name AddMember -Value { param ( [Parameter()] [System.String] - $mockSqlServerLogin + $Name ) if ($mockInvalidOperationForAddMemberMethod) { throw 'Mock AddMember Method was called with invalid operation.' } - if ( $this.Name -ne $mockExpectedForAddMemberMethod ) + if ($Name -ne $mockExpectedMemberToAdd) { throw "Called mocked AddMember() method without adding the right user. Expected '{0}'. But was '{1}'." ` - -f $mockExpectedForAddMemberMethod, $this.Name + -f $mockExpectedMemberToAdd, $Name } } -PassThru | - Add-Member -MemberType ScriptMethod -Name DropMember -Value { + Add-Member -MemberType ScriptMethod -Name DropMember -Value { param ( [Parameter()] [System.String] - $mockSqlServerLogin + $Name ) if ($mockInvalidOperationForDropMemberMethod) { throw 'Mock DropMember Method was called with invalid operation.' } - if ( $this.Name -ne $mockExpectedForDropMemberMethod ) + if ($Name -ne $mockExpectedMemberToDrop) { throw "Called mocked Drop() method without adding the right user. Expected '{0}'. But was '{1}'." ` - -f $mockExpectedForDropMemberMethod, $this.Name + -f $mockExpectedMemberToDrop, $Name } } -PassThru )) @@ -201,19 +241,19 @@ try )) } } -PassThru -Force | - Add-Member -MemberType ScriptProperty -Name Logins -Value { + Add-Member -MemberType ScriptProperty -Name Logins -Value { return @{ - $mockSqlServerLoginOne = @(( + $mockSqlServerLogin1 = @(( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name LoginType -Value $mockSqlServerLoginType -PassThru + Add-Member -MemberType NoteProperty -Name LoginType -Value $mockSqlServerLogin1Type -PassThru )) - $mockSqlServerLoginTwo = @(( + $mockSqlServerLogin2 = @(( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name LoginType -Value $mockSqlServerLoginType -PassThru + Add-Member -MemberType NoteProperty -Name LoginType -Value $mockSqlServerLogin2Type -PassThru )) - $mockSqlServerLogin = @(( + $mockSqlServerLogin3 = @(( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name LoginType -Value $mockSqlServerLoginType -PassThru + Add-Member -MemberType NoteProperty -Name LoginType -Value $mockSqlServerLogin3Type -PassThru )) } } -PassThru -Force @@ -222,24 +262,21 @@ try ) } - $mockNewObjectUser = { + $mockNewObjectDatabaseRole = { return @( - ( - New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name Name -Value $mockSqlServerLogin -PassThru | - Add-Member -MemberType NoteProperty -Name Login -Value $mockSqlServerLogin -PassThru | - Add-Member -MemberType ScriptMethod -Name Create -Value { - if ($mockInvalidOperationForCreateMethod) - { - throw 'Mock Create Method was called with invalid operation.' - } - if ( $this.Name -ne $mockExpectedForCreateMethod ) - { - throw "Called mocked Create() method without adding the right user. Expected '{0}'. But was '{1}'." ` - -f $mockExpectedForCreateMethod, $this.Name - } - } -PassThru -Force - ) + New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name Name -Value $mockExpectedSqlDatabaseRole -PassThru | + Add-Member -MemberType ScriptMethod -Name Create -Value { + if ($mockInvalidOperationForCreateMethod) + { + throw 'Mock Create Method was called with invalid operation.' + } + if ($this.Name -ne $mockExpectedSqlDatabaseRole) + { + throw "Called mocked Create() method without adding the right user. Expected '{0}'. But was '{1}'." ` + -f $mockExpectedSqlDatabaseRole, $this.Name + } + } -PassThru -Force ) } #endregion @@ -253,9 +290,8 @@ try It 'Should throw the correct error' { $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginOne Database = 'unknownDatabaseName' - Role = $mockSqlDatabaseRole + Name = $mockSqlDatabaseRole1 } $errorMessage = $script:localizedData.DatabaseNotFound -f $testParameters.Database @@ -268,18 +304,89 @@ try } } + Context 'When the system is in the desired state and Ensure is set to Absent' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = 'UnknownRoleName' + } + + It 'Should return the state as Absent when the role does not exist' { + $result = Get-TargetResource @testParameters + $result.Ensure | Should -Be 'Absent' + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the members as null' { + $result = Get-TargetResource @testParameters + $result.Members | Should -Be $null + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the same values as passed as parameters' { + $result = Get-TargetResource @testParameters + $result.ServerName | Should -Be $testParameters.ServerName + $result.InstanceName | Should -Be $testParameters.InstanceName + $result.Database | Should -Be $testParameters.Database + $result.Name | Should -Be $testParameters.Name + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + } + + Context 'When the system is not in the desired state and Ensure is set to Absent' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + } + + It 'Should not return the state as Absent when the role exist' { + $result = Get-TargetResource @testParameters + $result.Ensure | Should -Not -Be 'Absent' + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the members as not null' { + $result = Get-TargetResource @testParameters + $result.Members | Should -Not -Be $null + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the members as string array' { + $result = Get-TargetResource @testParameters + ($result.Members -is [System.String[]]) | Should -Be $true + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the same values as passed as parameters' { + $result = Get-TargetResource @testParameters + $result.ServerName | Should -Be $testParameters.ServerName + $result.InstanceName | Should -Be $testParameters.InstanceName + $result.Database | Should -Be $testParameters.Database + $result.Name | Should -Be $testParameters.Name + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + } + Context 'When passing values to parameters and role does not exist' { - It 'Should throw the correct error' { + It 'Should return the state as Absent' { $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginOne Database = $mockSqlDatabaseName - Role = 'unknownRoleName' + Name = 'unknownRoleName' } - $errorMessage = $script:localizedData.RoleNotFound -f $testParameters.Role, $testParameters.Database + $result = Get-TargetResource @testParameters + $result.Ensure | Should -Be 'Absent' - { Get-TargetResource @testParameters } | Should -Throw $errorMessage + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } It 'Should call the mock function Connect-SQL' { @@ -287,16 +394,18 @@ try } } - Context 'When passing values to parameters and multiple values to Role parameter' { - It 'Should not throw' { + Context 'When passing values to parameters and throwing with EnumMembers method' { + It 'Should throw the correct error' { + $mockInvalidOperationForEnumMethod = $true $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginOne Database = $mockSqlDatabaseName - Role = @($mockSqlDatabaseRole, $mockSqlDatabaseRoleSecond) + Name = $mockSqlDatabaseRole1 } - { Get-TargetResource @testParameters } | Should -Not -Throw + $errorMessage = $script:localizedData.EnumDatabaseRoleMemberNamesError -f $mockSqlDatabaseRole1, $mockSqlDatabaseName + + { Get-TargetResource @testParameters } | Should -Throw $errorMessage } It 'Should call the mock function Connect-SQL' { @@ -304,17 +413,145 @@ try } } - Context 'When passing values to parameters and login does not exist' { + Context 'When the system is in the desired state, parameter Members is assigned a value and Ensure is set to Present' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + Members = $mockEnumMembers + } + + It 'Should return the state as present when the members are correct' { + $result = Get-TargetResource @testParameters + $result.Ensure | Should -Be 'Present' + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the members as not null' { + $result = Get-TargetResource @testParameters + $result.Members | Should -Be $testParameters.Members + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the members as string array' { + $result = Get-TargetResource @testParameters + ($result.Members -is [System.String[]]) | Should -Be $true + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the same values as passed as parameters' { + $result = Get-TargetResource @testParameters + $result.ServerName | Should -Be $testParameters.ServerName + $result.InstanceName | Should -Be $testParameters.InstanceName + $result.Database | Should -Be $testParameters.Database + $result.Name | Should -Be $testParameters.Name + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + } + + Context 'When the system is in the desired state, parameter MembersToInclude is assigned a value and Ensure is set to Present' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + MembersToInclude = $mockSqlServerLogin1 + } + + It 'Should return the state as present when the correct members exist' { + $result = Get-TargetResource @testParameters + $result.Ensure | Should -Be 'Present' + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the members as not null' { + $result = Get-TargetResource @testParameters + $result.Members | Should -Not -Be $null + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the members as string array' { + $result = Get-TargetResource @testParameters + ($result.Members -is [System.String[]]) | Should -Be $true + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the same values as passed as parameters' { + $result = Get-TargetResource @testParameters + $result.ServerName | Should -Be $testParameters.ServerName + $result.InstanceName | Should -Be $testParameters.InstanceName + $result.Database | Should -Be $testParameters.Database + $result.Name | Should -Be $testParameters.Name + $result.MembersToInclude | Should -Be $testParameters.MembersToInclude + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + } + + Context 'When the system is in the desired state, parameter MembersToExclude is assigned a value and Ensure is set to Present' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + MembersToExclude = $mockSqlServerLoginTwo + } + + It 'Should return the state as Present when the member does not exist' { + $result = Get-TargetResource @testParameters + $result.Ensure | Should -Be 'Present' + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the same values as passed as parameters' { + $result = Get-TargetResource @testParameters + $result.ServerName | Should -Be $testParameters.ServerName + $result.InstanceName | Should -Be $testParameters.InstanceName + $result.Database | Should -Be $testParameters.Database + $result.Name | Should -Be $testParameters.Name + $result.MembersToExclude | Should -Be $testParameters.MembersToExclude + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + } + + Context 'When the system is not in the desired state, parameter MembersToInclude is assigned a value, parameter Members is assigned a value, and Ensure is set to Present' { It 'Should throw the correct error' { $testParameters = $mockDefaultParameters $testParameters += @{ - Name = 'unknownLoginName' - Database = $mockSqlDatabaseName - Role = $mockSqlDatabaseRole + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + Members = $mockEnumMembers + MembersToInclude = $mockSqlServerLogin1 } + $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull + + { Get-TargetResource @testParameters } | Should -Throw $errorMessage + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + + Context 'When the system is not in the desired state, parameter MembersToExclude is assigned a value, parameter Members is assigned a value, and Ensure is set to Present' { + It 'Should throw the correct error' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + Members = $mockEnumMembers + MembersToExclude = $mockSqlServerLogin1 + } - $errorMessage = $script:localizedData.LoginNotFound -f $testParameters.Name + $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull { Get-TargetResource @testParameters } | Should -Throw $errorMessage } @@ -322,26 +559,26 @@ try It 'Should call the mock function Connect-SQL' { Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context } + } - Context 'When the system is not in the desired state, with one role' { + Context 'When the system is not in the desired state and Ensure is set to Present' { $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginOne Database = $mockSqlDatabaseName - Role = $mockSqlDatabaseRoleSecond + Name = 'UnknownRoleName' } - It 'Should return the state as absent' { + It 'Should return the state as Absent when the role does not exist' { $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Absent' Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - It 'Should not return any granted roles' { + It 'Should return the members as null' { $result = Get-TargetResource @testParameters - $result.Role | Should -Be $null + $result.Members | Should -Be $null Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } @@ -357,24 +594,31 @@ try } } - Context 'When the system is not in the desired state, with two roles' { + Context 'When the system is not in the desired state, parameter Members is assigned a value and Ensure is set to Present' { $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginOne Database = $mockSqlDatabaseName - Role = @($mockSqlDatabaseRole, $mockSqlDatabaseRoleSecond) + Name = $mockSqlDatabaseRole1 + Members = @($mockSqlServerLogin1, $mockSqlServerLogin3) } - It 'Should return the state as absent' { + It 'Should return the state as Absent when the members are correct' { $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Absent' Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - It 'Should only return the one granted role' { + It 'Should return the members as not null' { $result = Get-TargetResource @testParameters - $result.Role | Should -Be $mockSqlDatabaseRole + $result.Members | Should -Not -Be $null + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the members as string array' { + $result = Get-TargetResource @testParameters + ($result.Members -is [System.String[]]) | Should -Be $true Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } @@ -390,43 +634,58 @@ try } } - Context 'When the system is not in the desired state, and login is not a member of the database' { + Context 'When the system is not in the desired state, parameter MembersToInclude is assigned a value and Ensure is set to Present' { $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLogin - Database = $mockSqlDatabaseName - Role = $mockSqlDatabaseRole + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + MembersToInclude = $mockSqlServerLogin3 } - It 'Should return the state as absent' { + It 'Should return the state as absent when the members in the role are missing' { $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Absent' Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } + It 'Should return the members as not null' { + $result = Get-TargetResource @testParameters + $result.Members | Should -Not -Be $null + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the members as string array' { + $result = Get-TargetResource @testParameters + ($result.Members -is [System.String[]]) | Should -Be $true + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + It 'Should return the same values as passed as parameters' { $result = Get-TargetResource @testParameters $result.ServerName | Should -Be $testParameters.ServerName $result.InstanceName | Should -Be $testParameters.InstanceName $result.Database | Should -Be $testParameters.Database $result.Name | Should -Be $testParameters.Name + $result.MembersToInclude | Should -Be $testParameters.MembersToInclude Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } - Context 'When the system is in the desired state for a Windows user' { + Context 'When the system is not in the desired state, parameter MembersToExclude is assigned a value and Ensure is set to Present' { $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginOne - Database = $mockSqlDatabaseName - Role = $mockSqlDatabaseRole + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + MembersToExclude = $mockSqlServerLogin1 } - It 'Should return the state as absent' { + It 'Should return the state as absent when the members in the role are present' { $result = Get-TargetResource @testParameters - $result.Ensure | Should -Be 'Present' + $result.Ensure | Should -Be 'Absent' Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } @@ -437,7 +696,7 @@ try $result.InstanceName | Should -Be $testParameters.InstanceName $result.Database | Should -Be $testParameters.Database $result.Name | Should -Be $testParameters.Name - $result.Role | Should -Be $testParameters.Role + $result.MembersToExclude | Should -Be $testParameters.MembersToExclude Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } @@ -446,290 +705,476 @@ try Assert-VerifiableMock } - Describe "MSFT_SqlDatabaseRole\Test-TargetResource" -Tag 'Test' { + Describe "MSFT_SqlServerRole\Set-TargetResource" -Tag 'Set' { BeforeEach { Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable + Mock -CommandName New-Object -MockWith $mockNewObjectDatabaseRole -ParameterFilter { + $TypeName -eq 'Microsoft.SqlServer.Management.Smo.DatabaseRole' + } } - Context 'When the system is not in the desired state and Ensure is set to Present' { - It 'Should return the state as false when one desired role is not configured' { + Context 'When the system is not in the desired state and Ensure is set to Absent' { + It 'Should not throw when calling the Drop method' { + $mockSqlDatabaseRoleToDrop = 'DatabaseRoleToDrop' + $mockExpectedSqlDatabaseRole = 'DatabaseRoleToDrop' $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginOne Database = $mockSqlDatabaseName - Role = $mockSqlDatabaseRoleSecond - Ensure = 'Present' + Name = $mockSqlDatabaseRoleToDrop + Ensure = 'Absent' } - $result = Test-TargetResource @testParameters - $result | Should -Be $false - } + { Set-TargetResource @testParameters } | Should -Not -Throw - It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } - Context 'When the system is not in the desired state and Ensure is set to Present' { - It 'Should return the state as false when two desired roles are not configured' { + Context 'When the system is not in the desired state and Ensure is set to Absent' { + It 'Should throw the correct error when calling the Drop method' { + $mockInvalidOperationForDropMethod = $true $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginOne Database = $mockSqlDatabaseName - Role = @($mockSqlDatabaseRole, $mockSqlDatabaseRoleSecond) - Ensure = 'Present' + Name = $mockSqlDatabaseRole1 + Ensure = 'Absent' } - $result = Test-TargetResource @testParameters - $result | Should -Be $false - } + $errorMessage = $script:localizedData.DropDatabaseRoleError -f $mockSqlDatabaseRole1, $mockSqlDatabaseName - It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + { Set-TargetResource @testParameters } | Should -Throw $errorMessage + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } - Context 'When the system is not in the desired state and Ensure is set to Absent' { - It 'Should return the state as false when undesired roles are not configured' { + Context 'When the system is not in the desired state and Ensure is set to Present' { + It 'Should not throw when calling the Create method' { + $mockSqlDatabaseRoleAdd = 'DatabaseRoleToAdd' + $mockExpectedSqlDatabaseRole = 'DatabaseRoleToAdd' $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginTwo Database = $mockSqlDatabaseName - Role = @($mockSqlDatabaseRole, $mockSqlDatabaseRoleSecond) - Ensure = 'Absent' + Name = $mockSqlDatabaseRoleAdd + Ensure = 'Present' } - $result = Test-TargetResource @testParameters - $result | Should -Be $false + { Set-TargetResource @testParameters } | Should -Not -Throw + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + It 'Should call the mock function New-Object with TypeName equal to Microsoft.SqlServer.Management.Smo.DatabaseRole' { + Assert-MockCalled New-Object -Exactly -Times 1 -ParameterFilter { + $TypeName -eq 'Microsoft.SqlServer.Management.Smo.DatabaseRole' + } -Scope Context } } - Context 'When the system is in the desired state and Ensure is set to Present' { - It 'Should return the state as true when one desired role is correctly configured' { + Context 'When the system is not in the desired state and Ensure is set to Present' { + It 'Should throw the correct error when calling the Create method' { + $mockSqlDatabaseRoleAdd = 'DatabaseRoleToAdd' + $mockExpectedSqlDatabaseRole = 'DatabaseRoleToAdd' + $mockInvalidOperationForCreateMethod = $true $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginOne Database = $mockSqlDatabaseName - Role = $mockSqlDatabaseRole + Name = $mockSqlDatabaseRoleAdd Ensure = 'Present' } - $result = Test-TargetResource @testParameters - $result | Should -Be $true + $errorMessage = $script:localizedData.CreateDatabaseRoleError -f $mockSqlDatabaseRoleAdd, $mockSqlDatabaseName + + { Set-TargetResource @testParameters } | Should -Throw $errorMessage + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + It 'Should call the mock function New-Object with TypeName equal to Microsoft.SqlServer.Management.Smo.DatabaseRole' { + Assert-MockCalled New-Object -Exactly -Times 1 -ParameterFilter { + $TypeName -eq 'Microsoft.SqlServer.Management.Smo.DatabaseRole' + } -Scope Context } } - Context 'When the system is in the desired state and Ensure is set to Present' { - It 'Should return the state as true when two desired role are correctly configured' { + Context 'When both the parameters Members and MembersToInclude are assigned a value and Ensure is set to Present' { + It 'Should throw the correct error' { $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginTwo - Database = $mockSqlDatabaseName - Role = @($mockSqlDatabaseRole, $mockSqlDatabaseRoleSecond) - Ensure = 'Present' + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + Members = $mockEnumMembers + MembersToInclude = $mockSqlServerLogin3 + Ensure = 'Present' } - $result = Test-TargetResource @testParameters - $result | Should -Be $true + $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull + + { Set-TargetResource @testParameters } | Should -Throw $errorMessage + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } + } - It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Context 'When both the parameters Members and MembersToExclude are assigned a value and Ensure is set to Present' { + It 'Should throw the correct error' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + Members = $mockEnumMembers + MembersToExclude = $mockSqlServerLogin2 + Ensure = 'Present' + } + + $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull + + { Set-TargetResource @testParameters } | Should -Throw $errorMessage + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } - Context 'When the system is in the desired state and Ensure is set to Absent' { - It 'Should return the state as true when two desired role are correctly configured' { + Context 'When parameter MembersToInclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { + It 'Should not throw when calling the AddMember method' { + $mockExpectedMemberToAdd = $mockSqlServerLogin3 $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginOne - Database = $mockSqlDatabaseName - Role = $mockSqlDatabaseRoleSecond - Ensure = 'Absent' + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + MembersToInclude = $mockSqlServerLogin3 + Ensure = 'Present' } - $result = Test-TargetResource @testParameters - $result | Should -Be $true + { Set-TargetResource @testParameters } | Should -Not -Throw + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } + } - It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Context 'When parameter MembersToInclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { + It 'Should not throw when calling the AddMember method' { + $mockInvalidOperationForAddMemberMethod = $true + $mockExpectedMemberToAdd = $mockSqlServerLogin3 + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + MembersToInclude = $mockSqlServerLogin3 + Ensure = 'Present' + } + + $errorMessage = $script:localizedData.AddDatabaseRoleMemberError -f $mockSqlServerLogin3, $mockSqlDatabaseRole1, $mockSqlDatabaseName + + { Set-TargetResource @testParameters } | Should -Throw $errorMessage + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } - Assert-VerifiableMock - } + Context 'When parameter MembersToInclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { + It 'Should throw the correct error when the database user does not exist' { + $mockExpectedMemberToAdd = $mockSqlServerLogin3 + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + MembersToInclude = $mockSqlServerInvalidLogin + Ensure = 'Present' + } - Describe "MSFT_SqlDatabaseRole\Set-TargetResource" -Tag 'Set' { - BeforeEach { - Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable - Mock -CommandName New-Object -MockWith $mockNewObjectUser -ParameterFilter { - $TypeName -eq 'Microsoft.SqlServer.Management.Smo.User' - } -Verifiable + $errorMessage = $script:localizedData.DatabaseUserNotFound -f $mockSqlServerInvalidLogin, $mockSqlDatabaseName + + { Set-TargetResource @testParameters } | Should -Throw $errorMessage + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } } - Context 'When the system is not in the desired state, Ensure is set to Present and Login does not exist' { - It 'Should Not Throw when Ensure parameter is set to Present' { - $mockExpectedForAddMemberMethod = 'MyRole' + Context 'When parameter MembersToExclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { + It 'Should not throw when calling the DropMember method' { + $mockExpectedMemberToAdd = $mockSqlServerLogin3 $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLogin - Database = $mockSqlDatabaseName - Role = $mockSqlDatabaseRole - Ensure = 'Present' + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + MembersToExclude = $mockSqlServerLogin3 + Ensure = 'Present' } { Set-TargetResource @testParameters } | Should -Not -Throw - } - It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } + } - It 'Should call the mock function New-Object with TypeName equal to Microsoft.SqlServer.Management.Smo.User' { - Assert-MockCalled New-Object -Exactly -Times 1 -ParameterFilter { - $TypeName -eq 'Microsoft.SqlServer.Management.Smo.User' - } -Scope Context + Context 'When parameter MembersToExclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { + It 'Should throw the correct error when calling the DropMember method' { + $mockInvalidOperationForDropMemberMethod = $true + $mockExpectedMemberToDrop = $mockSqlServerLogin2 + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + MembersToExclude = $mockSqlServerLogin2 + Ensure = 'Present' + } + + $errorMessage = $script:localizedData.DropDatabaseRoleMemberError -f $mockSqlServerLogin2, $mockSqlDatabaseRole1, $mockSqlDatabaseName + + { Set-TargetResource @testParameters } | Should -Throw $errorMessage + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } - Context 'When the system is not in the desired state, Ensure is set to Present and Login does not exist' { - It 'Should Throw the correct error when Ensure parameter is set to Present' { - $mockInvalidOperationForCreateMethod = $true + Context 'When parameter Members is assigned a value and Ensure is set to Present' { + It 'Should throw the correct error when database user does not exist' { + $mockExpectedMemberToAdd = $mockSqlServerInvalidLogin $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLogin Database = $mockSqlDatabaseName - Role = $mockSqlDatabaseRoleSecond + Name = $mockSqlDatabaseRole1 + Members = @($mockSqlServerInvalidLogin, $mockSqlServerLogin1, $mockSqlServerLogin2) Ensure = 'Present' } - $errorMessage = $script:localizedData.FailedToAddUser -f $testParameters.Name, $testParameters.Database + $errorMessage = $script:localizedData.DatabaseUserNotFound -f $mockSqlServerInvalidLogin, $mockSqlDatabaseName { Set-TargetResource @testParameters } | Should -Throw $errorMessage - } - - It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context - } - It 'Should call the mock function New-Object with TypeName equal to Microsoft.SqlServer.Management.Smo.User' { - Assert-MockCalled New-Object -Exactly -Times 1 -ParameterFilter { - $TypeName -eq 'Microsoft.SqlServer.Management.Smo.User' - } -Scope Context + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } - Context 'When the system is not in the desired state, Ensure is set to Present and Login already exist' { - It 'Should Not Throw when Ensure parameter is set to Present' { - $mockExpectedForAddMemberMethod = 'MySecondRole' - $mockSqlServerLogin = $mockSqlServerLoginOne + Context 'When Members parameter is set and Ensure parameter is set to Present' { + It 'Should not throw when calling both the AddMember and DropMember methods' { + $mockExpectedMemberToAdd = $mockSqlServerLogin3 + $mockExpectedMemberToDrop = $mockSqlServerLogin2 $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLogin Database = $mockSqlDatabaseName - Role = $mockSqlDatabaseRoleSecond + Name = $mockSqlDatabaseRole1 + Members = @($mockSqlServerLogin1, $mockSqlServerLogin3) Ensure = 'Present' } { Set-TargetResource @testParameters } | Should -Not -Throw + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } + } - It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-VerifiableMock + } + + Describe "MSFT_SqlDatabaseRole\Test-TargetResource" -Tag 'Test' { + BeforeEach { + Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable + } + + Context 'When the system is not in the desired state and Ensure is set to Absent' { + It 'Should return false when the desired database role exists' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + Ensure = 'Absent' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $false + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } + } - It 'Should not call the mock function New-Object with TypeName equal to Microsoft.SqlServer.Management.Smo.User' { - Assert-MockCalled New-Object -Exactly -Times 0 -ParameterFilter { - $TypeName -eq 'Microsoft.SqlServer.Management.Smo.User' - } -Scope Context + Context 'When the system is in the desired state and Ensure is set to Absent' { + It 'Should return true when the desired database role does not exist' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole3 + Ensure = 'Absent' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $true + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } - Context 'When the system is not in the desired state, Ensure is set to Present and Login already exist' { - It 'Should Throw the correct error when Ensure parameter is set to Present' { - $mockInvalidOperationForAddMemberMethod = $true + Context 'When the system is in the desired state and Ensure is set to Present' { + It 'Should return true when the desired database role exists' { $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginOne Database = $mockSqlDatabaseName - Role = $mockSqlDatabaseRoleSecond + Name = $mockSqlDatabaseRole1 Ensure = 'Present' } - $errorMessage = $script:localizedData.FailedToAddUserToRole -f $testParameters.Name, $testParameters.Role, $testParameters.Database + $result = Test-TargetResource @testParameters + $result | Should -Be $true - { Set-TargetResource @testParameters } | Should -Throw $errorMessage + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } + } - It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context - } + Context 'When the system is not in the desired state and Ensure parameter is set to Present' { + It 'Should return false when the desired members are not in the desired database role' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + Members = @($mockSqlServerLogin3) + Ensure = 'Present' + } - It 'Should not call the mock function New-Object with TypeName equal to Microsoft.SqlServer.Management.Smo.User' { - Assert-MockCalled New-Object -Exactly -Times 0 -ParameterFilter { - $TypeName -eq 'Microsoft.SqlServer.Management.Smo.User' - } -Scope Context + $result = Test-TargetResource @testParameters + $result | Should -Be $false + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } - Context 'When the system is not in the desired state, Ensure is set to Absent' { - It 'Should not throw the correct error when Ensure parameter is set to Absent' { + Context 'When both the parameters Members and MembersToInclude are assigned a value and Ensure is set to Present' { + It 'Should throw the correct error' { $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginTwo - Database = $mockSqlDatabaseName - Role = $mockSqlDatabaseRole - Ensure = 'Absent' + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + Members = $mockEnumMembers + MembersToInclude = $mockSqlServerLogin3 + Ensure = 'Present' } - { Set-TargetResource @testParameters } | Should -Not -Throw + $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull + + { Get-TargetResource @testParameters } | Should -Throw $errorMessage } It 'Should call the mock function Connect-SQL' { Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context } + } - It 'Should not call the mock function New-Object with TypeName equal to Microsoft.SqlServer.Management.Smo.User' { - Assert-MockCalled New-Object -Exactly -Times 0 -ParameterFilter { - $TypeName -eq 'Microsoft.SqlServer.Management.Smo.User' - } -Scope Context + Context 'When parameter MembersToInclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { + It 'Should return true when the desired database role exists and the MembersToInclude contains members that already exist' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + MembersToInclude = $mockSqlServerLogin2 + Ensure = 'Present' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $true + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return false when the desired database role exists and the MembersToInclude contains members that are missing' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + MembersToInclude = $mockSqlServerLogin3 + Ensure = 'Present' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $false + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } - $mockInvalidOperationForDropMemberMethod = $true + Context 'When parameter MembersToInclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { + It 'Should return false when desired database role does not exist' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole3 + MembersToInclude = $mockSqlServerLogin1 + Ensure = 'Present' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $false - Context 'When the system is not in the desired state, Ensure is set to Absent' { - It 'Should not throw the correct error when Ensure parameter is set to Absent' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + } + + Context 'When both the parameters Members and MembersToExclude are assigned a value and Ensure is set to Present' { + It 'Should throw the correct error' { $testParameters = $mockDefaultParameters $testParameters += @{ - Name = $mockSqlServerLoginTwo - Database = $mockSqlDatabaseName - Role = $mockSqlDatabaseRole - Ensure = 'Absent' + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + Members = $mockEnumMembers + MembersToExclude = $mockSqlServerLogin3 + Ensure = 'Present' } - $errorMessage = $script:localizedData.FailedToDropUserFromRole -f $testParameters.Name, $testParameters.Role, $testParameters.Database + $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull - { Set-TargetResource @testParameters } | Should -Throw $errorMessage + { Get-TargetResource @testParameters } | Should -Throw $errorMessage } It 'Should call the mock function Connect-SQL' { Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context } + } + + Context 'When parameter MembersToExclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { + It 'Should return true when the desired database role exists and the MembersToExclude contains members that do not yet exist' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + MembersToExclude = $mockSqlServerLogin3 + Ensure = 'Present' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $true + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } - It 'Should not call the mock function New-Object with TypeName equal to Microsoft.SqlServer.Management.Smo.User' { - Assert-MockCalled New-Object -Exactly -Times 0 -ParameterFilter { - $TypeName -eq 'Microsoft.SqlServer.Management.Smo.User' - } -Scope Context + It 'Should return false when the desired database role exists and the MembersToExclude contains members that already exist' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + MembersToExclude = $mockSqlServerLogin1 + Ensure = 'Present' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $false + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + } + + Context 'When parameter MembersToExclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { + It 'Should return false when desired database role does not exist' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole3 + MembersToExclude = $mockSqlServerLogin1 + Ensure = 'Present' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $false + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } From f7a9bfb8dfe1f5864f75bda725593d4fbb9c84b8 Mon Sep 17 00:00:00 2001 From: Paul Shamus Date: Fri, 31 May 2019 10:20:16 -0700 Subject: [PATCH 05/10] Updated SqlDatabaseRole examples --- .../SqlDatabaseRole/1-AddDatabaseRole.ps1 | 2 +- .../3-EnforceDatabaseRoleMembers.ps1 | 8 +++-- .../4-MembersToIncludeInDatabaseRole.ps1 | 8 +++-- .../5-MembersToExcludeFromDatabaseRole.ps1 | 8 +++-- ...mbersToIncludeAndExcludeInDatabaseRole.ps1 | 35 +++++++++++++++++++ README.md | 1 + 6 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 Examples/Resources/SqlDatabaseRole/6-MembersToIncludeAndExcludeInDatabaseRole.ps1 diff --git a/Examples/Resources/SqlDatabaseRole/1-AddDatabaseRole.ps1 b/Examples/Resources/SqlDatabaseRole/1-AddDatabaseRole.ps1 index 34c47b727..d5c43ad5a 100644 --- a/Examples/Resources/SqlDatabaseRole/1-AddDatabaseRole.ps1 +++ b/Examples/Resources/SqlDatabaseRole/1-AddDatabaseRole.ps1 @@ -1,6 +1,6 @@ <# .EXAMPLE - This example shows how to ensure that the database roles named ReportEditor and ReportViewer is present in the + This example shows how to ensure that the database roles named ReportEditor and ReportViewer are present in the AdventureWorks database on instance sqltest.company.local\DSC. #> diff --git a/Examples/Resources/SqlDatabaseRole/3-EnforceDatabaseRoleMembers.ps1 b/Examples/Resources/SqlDatabaseRole/3-EnforceDatabaseRoleMembers.ps1 index ff3e95258..eb7a7bc24 100644 --- a/Examples/Resources/SqlDatabaseRole/3-EnforceDatabaseRoleMembers.ps1 +++ b/Examples/Resources/SqlDatabaseRole/3-EnforceDatabaseRoleMembers.ps1 @@ -1,8 +1,10 @@ <# .EXAMPLE - This example shows how to ensure that the database role named ReportViewer is present in the AdventureWorks - database on instance sqltest.company.local\DSC and that only users CONTOSO\Barbara and CONTOSO\Fred are members of - this role. + This example shows how to do the following: + + 1. Ensure that the database role named ReportViewer is present in the AdventureWorks database on instance + sqltest.company.local\DSC + 2. Ensure that users CONTOSO\Barbara and CONTOSO\Fred will always be the only members of the role #> Configuration Example diff --git a/Examples/Resources/SqlDatabaseRole/4-MembersToIncludeInDatabaseRole.ps1 b/Examples/Resources/SqlDatabaseRole/4-MembersToIncludeInDatabaseRole.ps1 index 72259d6e0..a7b35d465 100644 --- a/Examples/Resources/SqlDatabaseRole/4-MembersToIncludeInDatabaseRole.ps1 +++ b/Examples/Resources/SqlDatabaseRole/4-MembersToIncludeInDatabaseRole.ps1 @@ -1,8 +1,10 @@ <# .EXAMPLE - This example shows how to ensure that the database role named ReportViewer is present in the AdventureWorks - database on instance sqltest.company.local\DSC and that users CONTOSO\Barbara and CONTOSO\Fred are added as members - of this role. + This example shows how to do the following: + + 1. Ensure that the database role named ReportViewer is present in the AdventureWorks database on instance + sqltest.company.local\DSC + 2. Ensure that users CONTOSO\Barbara and CONTOSO\Fred will always be members of the role #> Configuration Example diff --git a/Examples/Resources/SqlDatabaseRole/5-MembersToExcludeFromDatabaseRole.ps1 b/Examples/Resources/SqlDatabaseRole/5-MembersToExcludeFromDatabaseRole.ps1 index 3e888e245..99415f548 100644 --- a/Examples/Resources/SqlDatabaseRole/5-MembersToExcludeFromDatabaseRole.ps1 +++ b/Examples/Resources/SqlDatabaseRole/5-MembersToExcludeFromDatabaseRole.ps1 @@ -1,8 +1,10 @@ <# .EXAMPLE - This example shows how to ensure that the database role named ReportViewer is present in the AdventureWorks - database on instance sqltest.company.local\DSC and that users CONTOSO\Barbara and CONTOSO\Fred are not members of - this role. + This example shows how to do the following: + + 1. Ensure that the database role named ReportViewer is present in the AdventureWorks database on instance + sqltest.company.local\DSC + 2. Ensure that users CONTOSO\Barbara and CONTOSO\Fred will never be members of the role #> Configuration Example diff --git a/Examples/Resources/SqlDatabaseRole/6-MembersToIncludeAndExcludeInDatabaseRole.ps1 b/Examples/Resources/SqlDatabaseRole/6-MembersToIncludeAndExcludeInDatabaseRole.ps1 new file mode 100644 index 000000000..e4a07c18c --- /dev/null +++ b/Examples/Resources/SqlDatabaseRole/6-MembersToIncludeAndExcludeInDatabaseRole.ps1 @@ -0,0 +1,35 @@ +<# +.EXAMPLE + This example shows how to do the following: + + 1. Ensure that the database role named ReportViewer is present in the AdventureWorks database on instance + sqltest.company.local\DSC + 2. Ensure that users CONTOSO\Barbara and CONTOSO\Fred will always be members of the role + 3. Ensure that the user CONSOSO\Intern1 will never be a member of the role +#> + +Configuration Example +{ + param( + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $SqlAdministratorCredential + ) + + Import-DscResource -ModuleName SqlServerDsc + + node localhost + { + SqlDatabaseRole ReportViewer_IncludeAndExcludeRoleMembers + { + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + Database = 'AdventureWorks' + Name = 'ReportViewer' + MembersToInclude = @('CONTOSO\Barbara', 'CONTOSO\Fred') + MembersToExclude = @('CONTOSO\Intern1') + Ensure = 'Present' + PsDscRunAsCredential = $SqlAdministratorCredential + } + } +} diff --git a/README.md b/README.md index bb075512c..8ac5b5f3f 100644 --- a/README.md +++ b/README.md @@ -751,6 +751,7 @@ manages members in both built-in and user created database roles. * [Enforce Role membership](/Examples/Resources/SqlDatabaseRole/3-EnforceDatabaseRoleMembers.ps1) * [Members to include in database role](/Examples/Resources/SqlDatabaseRole/4-MembersToIncludeInDatabaseRole.ps1) * [Members to exclude from database role](/Examples/Resources/SqlDatabaseRole/5-MembersToExcludeFromDatabaseRole.ps1) +* [Members to include and exclude in a database role](/Examples/Resources/SqlDatabaseRole/6-MembersToIncludeAndExcludeInDatabaseRole.ps1) #### Known issues From d5d1da23a4c5fecf503efb4aea5b7593b2f9cf07 Mon Sep 17 00:00:00 2001 From: Paul Shamus Date: Fri, 31 May 2019 11:53:31 -0700 Subject: [PATCH 06/10] Updated Get/Test-TargetResource functionality --- .../MSFT_SqlDatabaseRole.psm1 | 40 +-- Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 | 303 +++++------------- 2 files changed, 90 insertions(+), 253 deletions(-) diff --git a/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.psm1 b/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.psm1 index 5b3e998d3..936e3de28 100644 --- a/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.psm1 +++ b/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.psm1 @@ -71,12 +71,7 @@ function Get-TargetResource [Parameter()] [System.String[]] - $MembersToExclude, - - [Parameter()] - [ValidateSet('Present', 'Absent')] - [System.String] - $Ensure = 'Present' + $MembersToExclude ) Write-Verbose -Message ( @@ -86,12 +81,12 @@ function Get-TargetResource $sqlServerObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName if ($sqlServerObject) { - $currentEnsure = 'Present' + $membersInDesiredState = $true + $roleStatus = 'Present' # Check if database exists. if (-not ($sqlDatabaseObject = $sqlServerObject.Databases[$Database])) { - $currentEnsure = 'Absent' $errorMessage = $script:localizedData.DatabaseNotFound -f $Database New-ObjectNotFoundException -Message $errorMessage } @@ -104,7 +99,6 @@ function Get-TargetResource } catch { - $currentEnsure = 'Absent' $errorMessage = $script:localizedData.EnumDatabaseRoleMemberNamesError -f $Name, $Database New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ } @@ -113,7 +107,6 @@ function Get-TargetResource { if ($MembersToInclude -or $MembersToExclude) { - $currentEnsure = 'Absent' $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull New-InvalidOperationException -Message $errorMessage } @@ -123,7 +116,7 @@ function Get-TargetResource Write-Verbose -Message ( $script:localizedData.DesiredMembersNotPresent -f $Name, $Database ) - $currentEnsure = 'Absent' + $membersInDesiredState = $false } } else @@ -137,7 +130,7 @@ function Get-TargetResource Write-Verbose -Message ( $script:localizedData.MemberNotPresent -f $memberName, $Name, $Database ) - $currentEnsure = 'Absent' + $membersInDesiredState = $false } } } @@ -151,7 +144,7 @@ function Get-TargetResource Write-Verbose -Message ( $script:localizedData.MemberPresent -f $memberName, $Name, $Database ) - $currentEnsure = 'Absent' + $membersInDesiredState = $false } } } @@ -159,19 +152,20 @@ function Get-TargetResource } else { - $currentEnsure = 'Absent' + $roleStatus = 'Absent' } } $returnValue = @{ - ServerName = $ServerName - InstanceName = $InstanceName - Database = $Database - Name = $Name - Members = $roleMembers - MembersToInclude = $MembersToInclude - MembersToExclude = $MembersToExclude - Ensure = $currentEnsure + ServerName = $ServerName + InstanceName = $InstanceName + Database = $Database + Name = $Name + Members = $roleMembers + MembersToInclude = $MembersToInclude + MembersToExclude = $MembersToExclude + MembersInDesiredState = $membersInDesiredState + Ensure = $roleStatus } $returnValue @@ -489,7 +483,7 @@ function Test-TargetResource 'Present' { - if ($getTargetResourceResult.Ensure -ne 'Present') + if ($getTargetResourceResult.Ensure -ne 'Present' -or $getTargetResourceResult.MembersInDesiredState -eq $false) { Write-Verbose -Message ( $script:localizedData.EnsureIsPresent -f $Name diff --git a/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 b/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 index 8ded92af5..afb1e4e7b 100644 --- a/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 @@ -287,13 +287,13 @@ try } Context 'When passing values to parameters and database name does not exist' { - It 'Should throw the correct error' { - $testParameters = $mockDefaultParameters - $testParameters += @{ - Database = 'unknownDatabaseName' - Name = $mockSqlDatabaseRole1 - } + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = 'unknownDatabaseName' + Name = $mockSqlDatabaseRole1 + } + It 'Should throw the correct error' { $errorMessage = $script:localizedData.DatabaseNotFound -f $testParameters.Database { Get-TargetResource @testParameters } | Should -Throw $errorMessage @@ -304,14 +304,14 @@ try } } - Context 'When the system is in the desired state and Ensure is set to Absent' { + Context 'When passing values to key parameters only and the role does not exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName Name = 'UnknownRoleName' } - It 'Should return the state as Absent when the role does not exist' { + It 'Should return the state as Absent' { $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Absent' @@ -336,55 +336,16 @@ try } } - Context 'When the system is not in the desired state and Ensure is set to Absent' { + Context 'When passing values to key parameters only and the role exists' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName Name = $mockSqlDatabaseRole1 } - It 'Should not return the state as Absent when the role exist' { - $result = Get-TargetResource @testParameters - $result.Ensure | Should -Not -Be 'Absent' - - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - } - - It 'Should return the members as not null' { - $result = Get-TargetResource @testParameters - $result.Members | Should -Not -Be $null - - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - } - - It 'Should return the members as string array' { - $result = Get-TargetResource @testParameters - ($result.Members -is [System.String[]]) | Should -Be $true - - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - } - - It 'Should return the same values as passed as parameters' { + It 'Should return the state as Present' { $result = Get-TargetResource @testParameters - $result.ServerName | Should -Be $testParameters.ServerName - $result.InstanceName | Should -Be $testParameters.InstanceName - $result.Database | Should -Be $testParameters.Database - $result.Name | Should -Be $testParameters.Name - - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - } - } - - Context 'When passing values to parameters and role does not exist' { - It 'Should return the state as Absent' { - $testParameters = $mockDefaultParameters - $testParameters += @{ - Database = $mockSqlDatabaseName - Name = 'unknownRoleName' - } - - $result = Get-TargetResource @testParameters - $result.Ensure | Should -Be 'Absent' + $result.Ensure | Should -Be 'Present' Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } @@ -394,15 +355,15 @@ try } } - Context 'When passing values to parameters and throwing with EnumMembers method' { - It 'Should throw the correct error' { - $mockInvalidOperationForEnumMethod = $true - $testParameters = $mockDefaultParameters - $testParameters += @{ - Database = $mockSqlDatabaseName - Name = $mockSqlDatabaseRole1 - } + Context 'When passing values to key parameters only and throwing with EnumMembers method' { + $mockInvalidOperationForEnumMethod = $true + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + } + It 'Should throw the correct error' { $errorMessage = $script:localizedData.EnumDatabaseRoleMemberNamesError -f $mockSqlDatabaseRole1, $mockSqlDatabaseName { Get-TargetResource @testParameters } | Should -Throw $errorMessage @@ -413,7 +374,7 @@ try } } - Context 'When the system is in the desired state, parameter Members is assigned a value and Ensure is set to Present' { + Context 'When passing a value to parameter Members and the role exists' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -421,13 +382,20 @@ try Members = $mockEnumMembers } - It 'Should return the state as present when the members are correct' { + It 'Should return Ensure as Present' { $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Present' Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } + It 'Should return MembersInDesiredState as True when the members are correct' { + $result = Get-TargetResource @testParameters + $result.MembersInDesiredState | Should -Be $true + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + It 'Should return the members as not null' { $result = Get-TargetResource @testParameters $result.Members | Should -Be $testParameters.Members @@ -448,12 +416,13 @@ try $result.InstanceName | Should -Be $testParameters.InstanceName $result.Database | Should -Be $testParameters.Database $result.Name | Should -Be $testParameters.Name + $result.Members | Should -Be $testParameters.Members Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } - Context 'When the system is in the desired state, parameter MembersToInclude is assigned a value and Ensure is set to Present' { + Context 'When passing a value to parameter MembersToInclude and the role exists' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -461,13 +430,20 @@ try MembersToInclude = $mockSqlServerLogin1 } - It 'Should return the state as present when the correct members exist' { + It 'Should return Ensure as Present' { $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Present' Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } + It 'Should return MembersInDesiredState as True when the correct members exist' { + $result = Get-TargetResource @testParameters + $result.MembersInDesiredState | Should -Be $true + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + It 'Should return the members as not null' { $result = Get-TargetResource @testParameters $result.Members | Should -Not -Be $null @@ -494,7 +470,7 @@ try } } - Context 'When the system is in the desired state, parameter MembersToExclude is assigned a value and Ensure is set to Present' { + Context 'When passing a value to parameter MembersToExclude and the role exists' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -502,123 +478,16 @@ try MembersToExclude = $mockSqlServerLoginTwo } - It 'Should return the state as Present when the member does not exist' { + It 'Should return Ensure as Present' { $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Present' Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - It 'Should return the same values as passed as parameters' { - $result = Get-TargetResource @testParameters - $result.ServerName | Should -Be $testParameters.ServerName - $result.InstanceName | Should -Be $testParameters.InstanceName - $result.Database | Should -Be $testParameters.Database - $result.Name | Should -Be $testParameters.Name - $result.MembersToExclude | Should -Be $testParameters.MembersToExclude - - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - } - } - - Context 'When the system is not in the desired state, parameter MembersToInclude is assigned a value, parameter Members is assigned a value, and Ensure is set to Present' { - It 'Should throw the correct error' { - $testParameters = $mockDefaultParameters - $testParameters += @{ - Database = $mockSqlDatabaseName - Name = $mockSqlDatabaseRole1 - Members = $mockEnumMembers - MembersToInclude = $mockSqlServerLogin1 - } - - $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull - - { Get-TargetResource @testParameters } | Should -Throw $errorMessage - } - - It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context - } - } - - Context 'When the system is not in the desired state, parameter MembersToExclude is assigned a value, parameter Members is assigned a value, and Ensure is set to Present' { - It 'Should throw the correct error' { - $testParameters = $mockDefaultParameters - $testParameters += @{ - Database = $mockSqlDatabaseName - Name = $mockSqlDatabaseRole1 - Members = $mockEnumMembers - MembersToExclude = $mockSqlServerLogin1 - } - - $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull - - { Get-TargetResource @testParameters } | Should -Throw $errorMessage - } - - It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context - } - - } - - Context 'When the system is not in the desired state and Ensure is set to Present' { - $testParameters = $mockDefaultParameters - $testParameters += @{ - Database = $mockSqlDatabaseName - Name = 'UnknownRoleName' - } - - It 'Should return the state as Absent when the role does not exist' { - $result = Get-TargetResource @testParameters - $result.Ensure | Should -Be 'Absent' - - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - } - - It 'Should return the members as null' { - $result = Get-TargetResource @testParameters - $result.Members | Should -Be $null - - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - } - - It 'Should return the same values as passed as parameters' { - $result = Get-TargetResource @testParameters - $result.ServerName | Should -Be $testParameters.ServerName - $result.InstanceName | Should -Be $testParameters.InstanceName - $result.Database | Should -Be $testParameters.Database - $result.Name | Should -Be $testParameters.Name - - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - } - } - - Context 'When the system is not in the desired state, parameter Members is assigned a value and Ensure is set to Present' { - $testParameters = $mockDefaultParameters - $testParameters += @{ - Database = $mockSqlDatabaseName - Name = $mockSqlDatabaseRole1 - Members = @($mockSqlServerLogin1, $mockSqlServerLogin3) - } - - It 'Should return the state as Absent when the members are correct' { - $result = Get-TargetResource @testParameters - $result.Ensure | Should -Be 'Absent' - - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - } - - It 'Should return the members as not null' { - $result = Get-TargetResource @testParameters - $result.Members | Should -Not -Be $null - - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - } - - It 'Should return the members as string array' { + It 'Should return MembersInDesiredState as True when the member does not exist' { $result = Get-TargetResource @testParameters - ($result.Members -is [System.String[]]) | Should -Be $true + $result.MembersInDesiredState | Should -Be $true Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } @@ -629,77 +498,51 @@ try $result.InstanceName | Should -Be $testParameters.InstanceName $result.Database | Should -Be $testParameters.Database $result.Name | Should -Be $testParameters.Name + $result.MembersToExclude | Should -Be $testParameters.MembersToExclude Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } - Context 'When the system is not in the desired state, parameter MembersToInclude is assigned a value and Ensure is set to Present' { + Context 'When both parameters MembersToInclude and Members are assigned a value' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName Name = $mockSqlDatabaseRole1 - MembersToInclude = $mockSqlServerLogin3 - } - - It 'Should return the state as absent when the members in the role are missing' { - $result = Get-TargetResource @testParameters - $result.Ensure | Should -Be 'Absent' - - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - } - - It 'Should return the members as not null' { - $result = Get-TargetResource @testParameters - $result.Members | Should -Not -Be $null - - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Members = $mockEnumMembers + MembersToInclude = $mockSqlServerLogin1 } - It 'Should return the members as string array' { - $result = Get-TargetResource @testParameters - ($result.Members -is [System.String[]]) | Should -Be $true + It 'Should throw the correct error' { + $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + { Get-TargetResource @testParameters } | Should -Throw $errorMessage } - It 'Should return the same values as passed as parameters' { - $result = Get-TargetResource @testParameters - $result.ServerName | Should -Be $testParameters.ServerName - $result.InstanceName | Should -Be $testParameters.InstanceName - $result.Database | Should -Be $testParameters.Database - $result.Name | Should -Be $testParameters.Name - $result.MembersToInclude | Should -Be $testParameters.MembersToInclude - - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context } } - Context 'When the system is not in the desired state, parameter MembersToExclude is assigned a value and Ensure is set to Present' { + Context 'When both parameters MembersToExclude and Members are assigned a value' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName Name = $mockSqlDatabaseRole1 + Members = $mockEnumMembers MembersToExclude = $mockSqlServerLogin1 } - It 'Should return the state as absent when the members in the role are present' { - $result = Get-TargetResource @testParameters - $result.Ensure | Should -Be 'Absent' + It 'Should throw the correct error' { + $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + { Get-TargetResource @testParameters } | Should -Throw $errorMessage } - It 'Should return the same values as passed as parameters' { - $result = Get-TargetResource @testParameters - $result.ServerName | Should -Be $testParameters.ServerName - $result.InstanceName | Should -Be $testParameters.InstanceName - $result.Database | Should -Be $testParameters.Database - $result.Name | Should -Be $testParameters.Name - $result.MembersToExclude | Should -Be $testParameters.MembersToExclude - - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context } + } Assert-VerifiableMock @@ -974,7 +817,7 @@ try } Context 'When the system is not in the desired state and Ensure is set to Absent' { - It 'Should return false when the desired database role exists' { + It 'Should return False when the desired database role exists' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -990,7 +833,7 @@ try } Context 'When the system is in the desired state and Ensure is set to Absent' { - It 'Should return true when the desired database role does not exist' { + It 'Should return True when the desired database role does not exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -1006,7 +849,7 @@ try } Context 'When the system is in the desired state and Ensure is set to Present' { - It 'Should return true when the desired database role exists' { + It 'Should return True when the desired database role exists' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -1022,7 +865,7 @@ try } Context 'When the system is not in the desired state and Ensure parameter is set to Present' { - It 'Should return false when the desired members are not in the desired database role' { + It 'Should return False when the desired members are not in the desired database role' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -1038,7 +881,7 @@ try } } - Context 'When both the parameters Members and MembersToInclude are assigned a value and Ensure is set to Present' { + Context 'When both parameters Members and MembersToInclude are assigned a value and Ensure is set to Present' { It 'Should throw the correct error' { $testParameters = $mockDefaultParameters $testParameters += @{ @@ -1051,7 +894,7 @@ try $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull - { Get-TargetResource @testParameters } | Should -Throw $errorMessage + { Test-TargetResource @testParameters } | Should -Throw $errorMessage } It 'Should call the mock function Connect-SQL' { @@ -1060,7 +903,7 @@ try } Context 'When parameter MembersToInclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { - It 'Should return true when the desired database role exists and the MembersToInclude contains members that already exist' { + It 'Should return True when the desired database role exists and the MembersToInclude contains members that already exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -1075,7 +918,7 @@ try Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - It 'Should return false when the desired database role exists and the MembersToInclude contains members that are missing' { + It 'Should return False when the desired database role exists and the MembersToInclude contains members that are missing' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -1092,7 +935,7 @@ try } Context 'When parameter MembersToInclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { - It 'Should return false when desired database role does not exist' { + It 'Should return False when desired database role does not exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -1108,7 +951,7 @@ try } } - Context 'When both the parameters Members and MembersToExclude are assigned a value and Ensure is set to Present' { + Context 'When both parameters Members and MembersToExclude are assigned a value and Ensure is set to Present' { It 'Should throw the correct error' { $testParameters = $mockDefaultParameters $testParameters += @{ @@ -1121,7 +964,7 @@ try $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull - { Get-TargetResource @testParameters } | Should -Throw $errorMessage + { Test-TargetResource @testParameters } | Should -Throw $errorMessage } It 'Should call the mock function Connect-SQL' { @@ -1130,7 +973,7 @@ try } Context 'When parameter MembersToExclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { - It 'Should return true when the desired database role exists and the MembersToExclude contains members that do not yet exist' { + It 'Should return True when the desired database role exists and the MembersToExclude contains members that do not yet exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -1145,7 +988,7 @@ try Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - It 'Should return false when the desired database role exists and the MembersToExclude contains members that already exist' { + It 'Should return False when the desired database role exists and the MembersToExclude contains members that already exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -1162,7 +1005,7 @@ try } Context 'When parameter MembersToExclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { - It 'Should return false when desired database role does not exist' { + It 'Should return False when desired database role does not exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName From 5cbccc498dcca2a08c484d349faa37fe704c7c50 Mon Sep 17 00:00:00 2001 From: Paul Shamus Date: Mon, 3 Jun 2019 09:50:00 -0700 Subject: [PATCH 07/10] Fixed formatting issue --- .../6-MembersToIncludeAndExcludeInDatabaseRole.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Examples/Resources/SqlDatabaseRole/6-MembersToIncludeAndExcludeInDatabaseRole.ps1 b/Examples/Resources/SqlDatabaseRole/6-MembersToIncludeAndExcludeInDatabaseRole.ps1 index e4a07c18c..c1fad2191 100644 --- a/Examples/Resources/SqlDatabaseRole/6-MembersToIncludeAndExcludeInDatabaseRole.ps1 +++ b/Examples/Resources/SqlDatabaseRole/6-MembersToIncludeAndExcludeInDatabaseRole.ps1 @@ -10,7 +10,8 @@ Configuration Example { - param( + param + ( [Parameter(Mandatory = $true)] [System.Management.Automation.PSCredential] $SqlAdministratorCredential From 6fe6bf9579cf679d739392cc000dcf77fda22db8 Mon Sep 17 00:00:00 2001 From: Paul Shamus Date: Mon, 3 Jun 2019 10:53:56 -0700 Subject: [PATCH 08/10] Added read-only property to schema.mof --- .../MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.schema.mof | 1 + 1 file changed, 1 insertion(+) diff --git a/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.schema.mof b/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.schema.mof index 4a2c8a01e..374fdf828 100644 --- a/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.schema.mof +++ b/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.schema.mof @@ -8,5 +8,6 @@ class MSFT_SqlDatabaseRole : OMI_BaseResource [Write, Description("The members the database role should have. This parameter will replace all the current database role members with the specified members. Can only be used when parameter Ensure is set to 'Present'.")] String Members[]; [Write, Description("The members the database role should include. This parameter will only add members to a database role. Can only be used when parameter Ensure is set to 'Present'. Can not be used at the same time as parameter Members.")] String MembersToInclude[]; [Write, Description("The members the database role should exclude. This parameter will only remove members from a database role. Can only be used when parameter Ensure is set to 'Present'. Can not be used at the same time as parameter Members.")] String MembersToExclude[]; + [Read, Description("Indicates whether the database role members are in the desired state.")] Boolean MembersInDesiredState; [Write, Description("If 'Present' (the default value) then the role will be added to the database and the role membership will be set. If 'Absent' then the role will be removed from the database."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; }; From 8f22a0e764beefe037b8a9e595501b7890d0fd48 Mon Sep 17 00:00:00 2001 From: Paul Shamus Date: Mon, 3 Jun 2019 10:54:33 -0700 Subject: [PATCH 09/10] Changed current state assumption --- .../MSFT_SqlDatabaseRole.psm1 | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.psm1 b/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.psm1 index 936e3de28..c08757b16 100644 --- a/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.psm1 +++ b/DSCResources/MSFT_SqlDatabaseRole/MSFT_SqlDatabaseRole.psm1 @@ -31,9 +31,6 @@ $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_SqlDatabaseRole' .PARAMETER MembersToExclude Specifies members the database role should exclude. - - .PARAMETER Ensure - Specifies the desired state of the role. #> function Get-TargetResource { @@ -82,7 +79,7 @@ function Get-TargetResource if ($sqlServerObject) { $membersInDesiredState = $true - $roleStatus = 'Present' + $roleStatus = 'Absent' # Check if database exists. if (-not ($sqlDatabaseObject = $sqlServerObject.Databases[$Database])) @@ -149,10 +146,8 @@ function Get-TargetResource } } } - } - else - { - $roleStatus = 'Absent' + + $roleStatus = 'Present' } } @@ -588,12 +583,6 @@ function Remove-SqlDscDatabaseRoleMember $databaseName = $SqlDatabaseObject.Name - if (-not ($SqlDatabaseObject.Roles[$Member] -or $SqlDatabaseObject.Users[$Member])) - { - $errorMessage = $script:localizedData.DatabaseRoleOrUserNotFound -f $Member, $databaseName - New-ObjectNotFoundException -Message $errorMessage - } - try { Write-Verbose -Message ( From cd515f5e7c5506ca82d7afb2e89bfac33393380c Mon Sep 17 00:00:00 2001 From: Paul Shamus Date: Mon, 3 Jun 2019 10:55:15 -0700 Subject: [PATCH 10/10] Updated unit tests to include additional tests, cleaned up context names --- Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 | 308 ++++++++++++++-------- 1 file changed, 201 insertions(+), 107 deletions(-) diff --git a/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 b/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 index afb1e4e7b..3c31cf20e 100644 --- a/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 @@ -286,7 +286,7 @@ try Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable } - Context 'When passing values to parameters and database name does not exist' { + Context 'When only key parameters have values and database name does not exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = 'unknownDatabaseName' @@ -304,7 +304,7 @@ try } } - Context 'When passing values to key parameters only and the role does not exist' { + Context 'When only key parameters have values and the role does not exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -320,7 +320,7 @@ try It 'Should return the members as null' { $result = Get-TargetResource @testParameters - $result.Members | Should -Be $null + $result.Members | Should -BeNullOrEmpty Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } @@ -336,7 +336,7 @@ try } } - Context 'When passing values to key parameters only and the role exists' { + Context 'When only key parameters have values and the role exists' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -355,7 +355,7 @@ try } } - Context 'When passing values to key parameters only and throwing with EnumMembers method' { + Context 'When only key parameters have values and throwing with EnumMembers method' { $mockInvalidOperationForEnumMethod = $true $testParameters = $mockDefaultParameters $testParameters += @{ @@ -374,7 +374,7 @@ try } } - Context 'When passing a value to parameter Members and the role exists' { + Context 'When parameter Members is assigned a value, the role exists, and the role members are in the desired state' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -389,7 +389,7 @@ try Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - It 'Should return MembersInDesiredState as True when the members are correct' { + It 'Should return MembersInDesiredState as True' { $result = Get-TargetResource @testParameters $result.MembersInDesiredState | Should -Be $true @@ -422,7 +422,57 @@ try } } - Context 'When passing a value to parameter MembersToInclude and the role exists' { + Context 'When parameter Members is assigned a value, the role exists, and the role members are not in the desired state' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + Members = @($mockSqlServerLogin1, $mockSqlServerLogin3) + } + + It 'Should return Ensure as Present' { + $result = Get-TargetResource @testParameters + $result.Ensure | Should -Be 'Present' + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return MembersInDesiredState as False' { + $result = Get-TargetResource @testParameters + $result.MembersInDesiredState | Should -Be $false + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the members as string array' { + $result = Get-TargetResource @testParameters + ($result.Members -is [System.String[]]) | Should -Be $true + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + } + + Context 'When both parameters MembersToInclude and Members are assigned a value' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + Members = $mockEnumMembers + MembersToInclude = $mockSqlServerLogin1 + } + + It 'Should throw the correct error' { + $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull + + { Get-TargetResource @testParameters } | Should -Throw $errorMessage + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + + Context 'When parameter MembersToInclude is assigned a value, the role exists, and the role members are in the desired state' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -437,7 +487,7 @@ try Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - It 'Should return MembersInDesiredState as True when the correct members exist' { + It 'Should return MembersInDesiredState as True' { $result = Get-TargetResource @testParameters $result.MembersInDesiredState | Should -Be $true @@ -446,7 +496,7 @@ try It 'Should return the members as not null' { $result = Get-TargetResource @testParameters - $result.Members | Should -Not -Be $null + $result.Members | Should -Not -BeNullOrEmpty Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } @@ -470,12 +520,12 @@ try } } - Context 'When passing a value to parameter MembersToExclude and the role exists' { + Context 'When parameter MembersToInclude is assigned a value, the role exists, and the role members are not in the desired state' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName Name = $mockSqlDatabaseRole1 - MembersToExclude = $mockSqlServerLoginTwo + MembersToInclude = $mockSqlServerLogin3 } It 'Should return Ensure as Present' { @@ -485,32 +535,28 @@ try Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - It 'Should return MembersInDesiredState as True when the member does not exist' { + It 'Should return MembersInDesiredState as False' { $result = Get-TargetResource @testParameters - $result.MembersInDesiredState | Should -Be $true + $result.MembersInDesiredState | Should -Be $false Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - It 'Should return the same values as passed as parameters' { + It 'Should return the members as string array' { $result = Get-TargetResource @testParameters - $result.ServerName | Should -Be $testParameters.ServerName - $result.InstanceName | Should -Be $testParameters.InstanceName - $result.Database | Should -Be $testParameters.Database - $result.Name | Should -Be $testParameters.Name - $result.MembersToExclude | Should -Be $testParameters.MembersToExclude + ($result.Members -is [System.String[]]) | Should -Be $true Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } - Context 'When both parameters MembersToInclude and Members are assigned a value' { + Context 'When both parameters MembersToExclude and Members are assigned a value' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName Name = $mockSqlDatabaseRole1 Members = $mockEnumMembers - MembersToInclude = $mockSqlServerLogin1 + MembersToExclude = $mockSqlServerLogin1 } It 'Should throw the correct error' { @@ -522,27 +568,71 @@ try It 'Should call the mock function Connect-SQL' { Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context } + } - Context 'When both parameters MembersToExclude and Members are assigned a value' { + Context 'When parameter MembersToExclude is assigned a value, the role exists, and the role members are in the desired state' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName Name = $mockSqlDatabaseRole1 - Members = $mockEnumMembers - MembersToExclude = $mockSqlServerLogin1 + MembersToExclude = $mockSqlServerLogin3 } - It 'Should throw the correct error' { - $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull + It 'Should return Ensure as Present' { + $result = Get-TargetResource @testParameters + $result.Ensure | Should -Be 'Present' - { Get-TargetResource @testParameters } | Should -Throw $errorMessage + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + It 'Should return MembersInDesiredState as True' { + $result = Get-TargetResource @testParameters + $result.MembersInDesiredState | Should -Be $true + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the same values as passed as parameters' { + $result = Get-TargetResource @testParameters + $result.ServerName | Should -Be $testParameters.ServerName + $result.InstanceName | Should -Be $testParameters.InstanceName + $result.Database | Should -Be $testParameters.Database + $result.Name | Should -Be $testParameters.Name + $result.MembersToExclude | Should -Be $testParameters.MembersToExclude + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + } + + Context 'When parameter MembersToExclude is assigned a value, the role exists, and the role members are not in the desired state' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + MembersToExclude = $mockSqlServerLogin2 + } + + It 'Should return Ensure as Present' { + $result = Get-TargetResource @testParameters + $result.Ensure | Should -Be 'Present' + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } + It 'Should return MembersInDesiredState as False' { + $result = Get-TargetResource @testParameters + $result.MembersInDesiredState | Should -Be $false + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should return the members as string array' { + $result = Get-TargetResource @testParameters + ($result.Members -is [System.String[]]) | Should -Be $true + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } } Assert-VerifiableMock @@ -640,7 +730,42 @@ try } } - Context 'When both the parameters Members and MembersToInclude are assigned a value and Ensure is set to Present' { + Context 'When parameter Members is assigned a value and Ensure is set to Present' { + It 'Should throw the correct error when database user does not exist' { + $mockExpectedMemberToAdd = $mockSqlServerInvalidLogin + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + Members = @($mockSqlServerInvalidLogin, $mockSqlServerLogin1, $mockSqlServerLogin2) + Ensure = 'Present' + } + + $errorMessage = $script:localizedData.DatabaseUserNotFound -f $mockSqlServerInvalidLogin, $mockSqlDatabaseName + + { Set-TargetResource @testParameters } | Should -Throw $errorMessage + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + It 'Should not throw when calling both the AddMember and DropMember methods' { + $mockExpectedMemberToAdd = $mockSqlServerLogin3 + $mockExpectedMemberToDrop = $mockSqlServerLogin2 + $testParameters = $mockDefaultParameters + $testParameters += @{ + Database = $mockSqlDatabaseName + Name = $mockSqlDatabaseRole1 + Members = @($mockSqlServerLogin1, $mockSqlServerLogin3) + Ensure = 'Present' + } + + { Set-TargetResource @testParameters } | Should -Not -Throw + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + } + + Context 'When both parameters Members and MembersToInclude are assigned a value and Ensure is set to Present' { It 'Should throw the correct error' { $testParameters = $mockDefaultParameters $testParameters += @{ @@ -659,26 +784,24 @@ try } } - Context 'When both the parameters Members and MembersToExclude are assigned a value and Ensure is set to Present' { - It 'Should throw the correct error' { + Context 'When parameter MembersToInclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { + It 'Should throw the correct error when the database user does not exist' { + $mockExpectedMemberToAdd = $mockSqlServerLogin3 $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName Name = $mockSqlDatabaseRole1 - Members = $mockEnumMembers - MembersToExclude = $mockSqlServerLogin2 + MembersToInclude = $mockSqlServerInvalidLogin Ensure = 'Present' } - $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull + $errorMessage = $script:localizedData.DatabaseUserNotFound -f $mockSqlServerInvalidLogin, $mockSqlDatabaseName { Set-TargetResource @testParameters } | Should -Throw $errorMessage Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - } - Context 'When parameter MembersToInclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { It 'Should not throw when calling the AddMember method' { $mockExpectedMemberToAdd = $mockSqlServerLogin3 $testParameters = $mockDefaultParameters @@ -693,10 +816,8 @@ try Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - } - Context 'When parameter MembersToInclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { - It 'Should not throw when calling the AddMember method' { + It 'Should throw the correct error when calling the AddMember method' { $mockInvalidOperationForAddMemberMethod = $true $mockExpectedMemberToAdd = $mockSqlServerLogin3 $testParameters = $mockDefaultParameters @@ -715,18 +836,18 @@ try } } - Context 'When parameter MembersToInclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { - It 'Should throw the correct error when the database user does not exist' { - $mockExpectedMemberToAdd = $mockSqlServerLogin3 + Context 'When both parameters Members and MembersToExclude are assigned a value and Ensure is set to Present' { + It 'Should throw the correct error' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName Name = $mockSqlDatabaseRole1 - MembersToInclude = $mockSqlServerInvalidLogin + Members = $mockEnumMembers + MembersToExclude = $mockSqlServerLogin2 Ensure = 'Present' } - $errorMessage = $script:localizedData.DatabaseUserNotFound -f $mockSqlServerInvalidLogin, $mockSqlDatabaseName + $errorMessage = $script:localizedData.MembersToIncludeAndExcludeParamMustBeNull { Set-TargetResource @testParameters } | Should -Throw $errorMessage @@ -736,7 +857,7 @@ try Context 'When parameter MembersToExclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { It 'Should not throw when calling the DropMember method' { - $mockExpectedMemberToAdd = $mockSqlServerLogin3 + $mockExpectedMemberToDrop = $mockSqlServerLogin3 $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName @@ -749,9 +870,7 @@ try Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - } - Context 'When parameter MembersToExclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { It 'Should throw the correct error when calling the DropMember method' { $mockInvalidOperationForDropMemberMethod = $true $mockExpectedMemberToDrop = $mockSqlServerLogin2 @@ -771,58 +890,37 @@ try } } - Context 'When parameter Members is assigned a value and Ensure is set to Present' { - It 'Should throw the correct error when database user does not exist' { - $mockExpectedMemberToAdd = $mockSqlServerInvalidLogin - $testParameters = $mockDefaultParameters - $testParameters += @{ - Database = $mockSqlDatabaseName - Name = $mockSqlDatabaseRole1 - Members = @($mockSqlServerInvalidLogin, $mockSqlServerLogin1, $mockSqlServerLogin2) - Ensure = 'Present' - } - - $errorMessage = $script:localizedData.DatabaseUserNotFound -f $mockSqlServerInvalidLogin, $mockSqlDatabaseName - - { Set-TargetResource @testParameters } | Should -Throw $errorMessage + Assert-VerifiableMock + } - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - } + Describe "MSFT_SqlDatabaseRole\Test-TargetResource" -Tag 'Test' { + BeforeEach { + Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable } - Context 'When Members parameter is set and Ensure parameter is set to Present' { - It 'Should not throw when calling both the AddMember and DropMember methods' { - $mockExpectedMemberToAdd = $mockSqlServerLogin3 - $mockExpectedMemberToDrop = $mockSqlServerLogin2 + Context 'When the system is not in the desired state and Ensure is set to Absent' { + It 'Should return False when the desired database role exists' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName Name = $mockSqlDatabaseRole1 - Members = @($mockSqlServerLogin1, $mockSqlServerLogin3) - Ensure = 'Present' + Ensure = 'Absent' } - { Set-TargetResource @testParameters } | Should -Not -Throw + $result = Test-TargetResource @testParameters + $result | Should -Be $false Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } } - Assert-VerifiableMock - } - - Describe "MSFT_SqlDatabaseRole\Test-TargetResource" -Tag 'Test' { - BeforeEach { - Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable - } - - Context 'When the system is not in the desired state and Ensure is set to Absent' { - It 'Should return False when the desired database role exists' { + Context 'When the system is not in the desired state and Ensure is set to Present' { + It 'Should return True when the desired database role does not exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName - Name = $mockSqlDatabaseRole1 - Ensure = 'Absent' + Name = $mockSqlDatabaseRole3 + Ensure = 'Present' } $result = Test-TargetResource @testParameters @@ -864,7 +962,7 @@ try } } - Context 'When the system is not in the desired state and Ensure parameter is set to Present' { + Context 'When parameter Members is assigned a value and Ensure parameter is set to Present' { It 'Should return False when the desired members are not in the desired database role' { $testParameters = $mockDefaultParameters $testParameters += @{ @@ -903,44 +1001,42 @@ try } Context 'When parameter MembersToInclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { - It 'Should return True when the desired database role exists and the MembersToInclude contains members that already exist' { + It 'Should return False when desired database role does not exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName - Name = $mockSqlDatabaseRole1 - MembersToInclude = $mockSqlServerLogin2 + Name = $mockSqlDatabaseRole3 + MembersToInclude = $mockSqlServerLogin1 Ensure = 'Present' } $result = Test-TargetResource @testParameters - $result | Should -Be $true + $result | Should -Be $false Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - It 'Should return False when the desired database role exists and the MembersToInclude contains members that are missing' { + It 'Should return True when the desired database role exists and the MembersToInclude contains members that already exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName Name = $mockSqlDatabaseRole1 - MembersToInclude = $mockSqlServerLogin3 + MembersToInclude = $mockSqlServerLogin2 Ensure = 'Present' } $result = Test-TargetResource @testParameters - $result | Should -Be $false + $result | Should -Be $true Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - } - Context 'When parameter MembersToInclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { - It 'Should return False when desired database role does not exist' { + It 'Should return False when the desired database role exists and the MembersToInclude contains members that are missing' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName - Name = $mockSqlDatabaseRole3 - MembersToInclude = $mockSqlServerLogin1 + Name = $mockSqlDatabaseRole1 + MembersToInclude = $mockSqlServerLogin3 Ensure = 'Present' } @@ -973,43 +1069,41 @@ try } Context 'When parameter MembersToExclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { - It 'Should return True when the desired database role exists and the MembersToExclude contains members that do not yet exist' { + It 'Should return False when desired database role does not exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName - Name = $mockSqlDatabaseRole1 + Name = $mockSqlDatabaseRole3 MembersToExclude = $mockSqlServerLogin3 Ensure = 'Present' } $result = Test-TargetResource @testParameters - $result | Should -Be $true + $result | Should -Be $false Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - It 'Should return False when the desired database role exists and the MembersToExclude contains members that already exist' { + It 'Should return True when the desired database role exists and the MembersToExclude contains members that do not yet exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName Name = $mockSqlDatabaseRole1 - MembersToExclude = $mockSqlServerLogin1 + MembersToExclude = $mockSqlServerLogin3 Ensure = 'Present' } $result = Test-TargetResource @testParameters - $result | Should -Be $false + $result | Should -Be $true Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It } - } - Context 'When parameter MembersToExclude is assigned a value, parameter Members is not assigned a value, and Ensure is set to Present' { - It 'Should return False when desired database role does not exist' { + It 'Should return False when the desired database role exists and the MembersToExclude contains members that already exist' { $testParameters = $mockDefaultParameters $testParameters += @{ Database = $mockSqlDatabaseName - Name = $mockSqlDatabaseRole3 + Name = $mockSqlDatabaseRole1 MembersToExclude = $mockSqlServerLogin1 Ensure = 'Present' }