diff --git a/CHANGELOG.md b/CHANGELOG.md index 852febfba3..bb4c722c06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -102,6 +102,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 setup.exe ([issue #1171](https://github.com/dsccommunity/SqlServerDsc/issues/1171)). - SqlRSSetup - The DSC resource has been refactored into a class-based resource. +- `Set-SqlDscServerPermission` + - Added support for assigning permissions to a server role. ### Fixed diff --git a/source/Public/Set-SqlDscServerPermission.ps1 b/source/Public/Set-SqlDscServerPermission.ps1 index a77fde14cf..f44120dd58 100644 --- a/source/Public/Set-SqlDscServerPermission.ps1 +++ b/source/Public/Set-SqlDscServerPermission.ps1 @@ -1,9 +1,9 @@ <# .SYNOPSIS - Set permission for a login. + Set permission for a server principal. .DESCRIPTION - This command sets the permissions for a existing login on a SQL Server + This command sets the permissions for a existing principal on a SQL Server Database Engine instance. .PARAMETER ServerObject @@ -94,14 +94,15 @@ function Set-SqlDscServerPermission $ConfirmPreference = 'None' } - $testSqlDscIsLoginParameters = @{ + $testSqlDscIsPrincipalParameters = @{ ServerObject = $ServerObject Name = $Name } - $isLogin = Test-SqlDscIsLogin @testSqlDscIsLoginParameters + $isLogin = Test-SqlDscIsLogin @testSqlDscIsPrincipalParameters + $isRole = Test-SqlDscIsRole @testSqlDscIsPrincipalParameters - if ($isLogin) + if ($isLogin -or $isRole) { # Get the permissions names that are set to $true in the ServerPermissionSet. $permissionName = $Permission | diff --git a/source/en-US/SqlServerDsc.strings.psd1 b/source/en-US/SqlServerDsc.strings.psd1 index ac5689b44f..6e0839c7e7 100644 --- a/source/en-US/SqlServerDsc.strings.psd1 +++ b/source/en-US/SqlServerDsc.strings.psd1 @@ -25,7 +25,7 @@ ConvertFrom-StringData @' IsDatabasePrincipal_DatabaseMissing = The database '{0}' cannot be found. ## Get-SqlDscServerPermission, Set-SqlDscServerPermission - ServerPermission_MissingPrincipal = The principal '{0}' is not a login on the instance '{1}'. + ServerPermission_MissingPrincipal = The principal '{0}' is not a login nor role on the instance '{1}'. ## Set-SqlDscServerPermission ServerPermission_IgnoreWithGrantForStateDeny = The parameter WithGrant cannot be used together with the state Deny, the parameter WithGrant is ignored. diff --git a/tests/Unit/Public/Set-SqlDscServerPermission.Tests.ps1 b/tests/Unit/Public/Set-SqlDscServerPermission.Tests.ps1 index 1a033a1032..a921616df9 100644 --- a/tests/Unit/Public/Set-SqlDscServerPermission.Tests.ps1 +++ b/tests/Unit/Public/Set-SqlDscServerPermission.Tests.ps1 @@ -59,6 +59,10 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { return $false } + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $false + } + $script:mockDefaultParameters = @{ Name = 'UnknownUser' State = 'Grant' @@ -76,8 +80,8 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { } } - Context 'When the principal exist' { - Context 'When using parameter Confirm with value $false' { + Context 'When the login exists' { + Context 'When using parameter Confirm with value $false for a login' { BeforeAll { $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | Add-Member -MemberType 'ScriptMethod' -Name 'Deny' -Value { @@ -88,6 +92,10 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { return $true } + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $false + } + $script:mockDefaultParameters = @{ Confirm = $false Name = 'DOMAIN\MyLogin' @@ -110,7 +118,7 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { } } - Context 'When using parameter Force' { + Context 'When using parameter Force for a login' { BeforeAll { $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | Add-Member -MemberType 'ScriptMethod' -Name 'Deny' -Value { @@ -122,6 +130,10 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { return $true } + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $false + } + $script:mockDefaultParameters = @{ Force = $true Name = 'DOMAIN\MyLogin' @@ -144,7 +156,7 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { } } - Context 'When using parameter WhatIf' { + Context 'When using parameter WhatIf for a login' { BeforeAll { $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | Add-Member -MemberType 'ScriptMethod' -Name 'Deny' -Value { @@ -156,6 +168,10 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { return $true } + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $false + } + $script:mockDefaultParameters = @{ WhatIf = $true Name = 'DOMAIN\MyLogin' @@ -178,7 +194,7 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { } } - Context 'When permission should be granted' { + Context 'When permission should be granted for a login' { BeforeAll { $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | Add-Member -MemberType 'ScriptMethod' -Name 'Grant' -Value { @@ -189,6 +205,10 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { return $true } + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $false + } + $script:mockDefaultParameters = @{ Confirm = $false Name = 'DOMAIN\MyLogin' @@ -220,7 +240,7 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { } } - Context 'When permission should be granted and using parameter WithGrant' { + Context 'When permission should be granted for a login and using parameter WithGrant' { BeforeAll { $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | Add-Member -MemberType 'ScriptMethod' -Name 'Grant' -Value { @@ -247,6 +267,10 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { return $true } + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $false + } + $script:mockDefaultParameters = @{ Confirm = $false Name = 'DOMAIN\MyLogin' @@ -279,7 +303,7 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { } } - Context 'When permission should be revoked' { + Context 'When permission for a login should be revoked' { BeforeAll { $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | Add-Member -MemberType 'ScriptMethod' -Name 'Revoke' -Value { @@ -290,6 +314,10 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { return $true } + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $false + } + $script:mockDefaultParameters = @{ Confirm = $false Name = 'DOMAIN\MyLogin' @@ -321,7 +349,7 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { } } - Context 'When permission should be revoked and using parameter WithGrant' { + Context 'When permission for a login should be revoked and using parameter WithGrant' { BeforeAll { $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | Add-Member -MemberType 'ScriptMethod' -Name 'Revoke' -Value { @@ -350,6 +378,10 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { return $true } + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $false + } + $script:mockDefaultParameters = @{ Confirm = $false Name = 'DOMAIN\MyLogin' @@ -382,7 +414,7 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { } } - Context 'When permission should be denied' { + Context 'When permission for a login should be denied' { BeforeAll { $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | Add-Member -MemberType 'ScriptMethod' -Name 'Deny' -Value { @@ -393,6 +425,10 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { return $true } + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $false + } + $script:mockDefaultParameters = @{ Confirm = $false Name = 'DOMAIN\MyLogin' @@ -445,4 +481,406 @@ Describe 'Set-SqlDscServerPermission' -Tag 'Public' { } } } + + Context 'When the role exists' { + Context 'When using parameter Confirm with value $false for a role' { + BeforeAll { + $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | + Add-Member -MemberType 'ScriptMethod' -Name 'Deny' -Value { + $script:mockMethodDenyCallCount += 1 + } -PassThru -Force + + Mock -CommandName Test-SqlDscIsLogin -MockWith { + return $false + } + + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $true + } + + $script:mockDefaultParameters = @{ + Confirm = $false + Name = 'JuniorDBA' + State = 'Deny' + Permission = [Microsoft.SqlServer.Management.Smo.ServerPermissionSet] @{ + ConnectSql = $true + } + } + } + + BeforeEach { + $script:mockMethodDenyCallCount = 0 + } + + It 'Should call the correct mocked method' { + { Set-SqlDscServerPermission -ServerObject $mockServerObject @mockDefaultParameters } | + Should -Not -Throw + + $script:mockMethodDenyCallCount | Should -Be 1 + } + } + + Context 'When using parameter Force for a role' { + BeforeAll { + $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | + Add-Member -MemberType 'ScriptMethod' -Name 'Deny' -Value { + $script:mockMethodDenyCallCount += 1 + } -PassThru -Force + + + Mock -CommandName Test-SqlDscIsLogin -MockWith { + return $false + } + + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $true + } + + $script:mockDefaultParameters = @{ + Force = $true + Name = 'JuniorDBA' + State = 'Deny' + Permission = [Microsoft.SqlServer.Management.Smo.ServerPermissionSet] @{ + ConnectSql = $true + } + } + } + + BeforeEach { + $script:mockMethodDenyCallCount = 0 + } + + It 'Should call the correct mocked method' { + { Set-SqlDscServerPermission -ServerObject $mockServerObject @mockDefaultParameters } | + Should -Not -Throw + + $script:mockMethodDenyCallCount | Should -Be 1 + } + } + + Context 'When using parameter WhatIf for a role' { + BeforeAll { + $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | + Add-Member -MemberType 'ScriptMethod' -Name 'Deny' -Value { + $script:mockMethodDenyCallCount += 1 + } -PassThru -Force + + + Mock -CommandName Test-SqlDscIsLogin -MockWith { + return $false + } + + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $true + } + + $script:mockDefaultParameters = @{ + WhatIf = $true + Name = 'JuniorDBA' + State = 'Deny' + Permission = [Microsoft.SqlServer.Management.Smo.ServerPermissionSet] @{ + ConnectSql = $true + } + } + } + + BeforeEach { + $script:mockMethodDenyCallCount = 0 + } + + It 'Should not call the mocked method' { + { Set-SqlDscServerPermission -ServerObject $mockServerObject @mockDefaultParameters } | + Should -Not -Throw + + $script:mockMethodDenyCallCount | Should -Be 0 + } + } + + Context 'When permission for a role should be granted' { + BeforeAll { + $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | + Add-Member -MemberType 'ScriptMethod' -Name 'Grant' -Value { + $script:mockMethodGrantCallCount += 1 + } -PassThru -Force + + Mock -CommandName Test-SqlDscIsLogin -MockWith { + return $false + } + + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $true + } + + $script:mockDefaultParameters = @{ + Confirm = $false + Name = 'JuniorDBA' + State = 'Grant' + Permission = [Microsoft.SqlServer.Management.Smo.ServerPermissionSet] @{ + ConnectSql = $true + } + } + } + + BeforeEach { + $script:mockMethodGrantCallCount = 0 + } + + It 'Should call the correct mocked method' { + { Set-SqlDscServerPermission -ServerObject $mockServerObject @mockDefaultParameters } | + Should -Not -Throw + + $script:mockMethodGrantCallCount | Should -Be 1 + } + + Context 'When passing ServerObject over the pipeline' { + It 'Should call the correct mocked method' { + { $mockServerObject | Set-SqlDscServerPermission @mockDefaultParameters } | + Should -Not -Throw + + $script:mockMethodGrantCallCount | Should -Be 1 + } + } + } + + Context 'When permission for a role should be granted and using parameter WithGrant' { + BeforeAll { + $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | + Add-Member -MemberType 'ScriptMethod' -Name 'Grant' -Value { + param + ( + [Parameter()] + $Permission, + + [Parameter()] + $Name, + + [Parameter()] + $WithGrant + ) + + if ($WithGrant) + { + $script:mockMethodGrantUsingWithGrantCallCount += 1 + } + } -PassThru -Force + + + Mock -CommandName Test-SqlDscIsLogin -MockWith { + return $false + } + + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $true + } + + $script:mockDefaultParameters = @{ + Confirm = $false + Name = 'JuniorDBA' + State = 'Grant' + WithGrant = $true + Permission = [Microsoft.SqlServer.Management.Smo.ServerPermissionSet] @{ + ConnectSql = $true + } + } + } + + BeforeEach { + $script:mockMethodGrantUsingWithGrantCallCount = 0 + } + + It 'Should call the correct mocked method' { + { Set-SqlDscServerPermission -ServerObject $mockServerObject @mockDefaultParameters } | + Should -Not -Throw + + $script:mockMethodGrantUsingWithGrantCallCount | Should -Be 1 + } + + Context 'When passing ServerObject over the pipeline' { + It 'Should call the correct mocked method' { + { $mockServerObject | Set-SqlDscServerPermission @mockDefaultParameters } | + Should -Not -Throw + + $script:mockMethodGrantUsingWithGrantCallCount | Should -Be 1 + } + } + } + + Context 'When permission for a role should be revoked' { + BeforeAll { + $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | + Add-Member -MemberType 'ScriptMethod' -Name 'Revoke' -Value { + $script:mockMethodRevokeCallCount += 1 + } -PassThru -Force + + Mock -CommandName Test-SqlDscIsLogin -MockWith { + return $false + } + + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $true + } + + $script:mockDefaultParameters = @{ + Confirm = $false + Name = 'JuniorDBA' + State = 'Revoke' + Permission = [Microsoft.SqlServer.Management.Smo.ServerPermissionSet] @{ + ConnectSql = $true + } + } + } + + BeforeEach { + $script:mockMethodRevokeCallCount = 0 + } + + It 'Should call the correct mocked method' { + { Set-SqlDscServerPermission -ServerObject $mockServerObject @mockDefaultParameters } | + Should -Not -Throw + + $script:mockMethodRevokeCallCount | Should -Be 1 + } + + Context 'When passing ServerObject over the pipeline' { + It 'Should call the correct mocked method' { + { $mockServerObject | Set-SqlDscServerPermission @mockDefaultParameters } | + Should -Not -Throw + + $script:mockMethodRevokeCallCount | Should -Be 1 + } + } + } + + Context 'When permission for a role should be revoked and using parameter WithGrant' { + BeforeAll { + $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | + Add-Member -MemberType 'ScriptMethod' -Name 'Revoke' -Value { + param + ( + [Parameter()] + $Permission, + + [Parameter()] + $Name, + + [Parameter()] + $RevokeGrant, + + [Parameter()] + $Cascade + ) + + if (-not $RevokeGrant -and $Cascade) + { + $script:mockMethodRevokeUsingWithGrantCallCount += 1 + } + } -PassThru -Force + + Mock -CommandName Test-SqlDscIsLogin -MockWith { + return $false + } + + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $true + } + + $script:mockDefaultParameters = @{ + Confirm = $false + Name = 'JuniorDBA' + State = 'Revoke' + WithGrant = $true + Permission = [Microsoft.SqlServer.Management.Smo.ServerPermissionSet] @{ + ConnectSql = $true + } + } + } + + BeforeEach { + $script:mockMethodRevokeUsingWithGrantCallCount = 0 + } + + It 'Should call the correct mocked method' { + { Set-SqlDscServerPermission -ServerObject $mockServerObject @mockDefaultParameters } | + Should -Not -Throw + + $script:mockMethodRevokeUsingWithGrantCallCount | Should -Be 1 + } + + Context 'When passing ServerObject over the pipeline' { + It 'Should call the correct mocked method' { + { $mockServerObject | Set-SqlDscServerPermission @mockDefaultParameters } | + Should -Not -Throw + + $script:mockMethodGrantUsingWithGrantCallCount | Should -Be 1 + } + } + } + + Context 'When permission for a role should be denied' { + BeforeAll { + $mockServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' | + Add-Member -MemberType 'ScriptMethod' -Name 'Deny' -Value { + $script:mockMethodDenyCallCount += 1 + } -PassThru -Force + + Mock -CommandName Test-SqlDscIsLogin -MockWith { + return $false + } + + Mock -CommandName Test-SqlDscIsRole -MockWith { + return $true + } + + $script:mockDefaultParameters = @{ + Confirm = $false + Name = 'JuniorDBA' + State = 'Deny' + Permission = [Microsoft.SqlServer.Management.Smo.ServerPermissionSet] @{ + ConnectSql = $true + } + } + } + + BeforeEach { + $script:mockMethodDenyCallCount = 0 + } + + It 'Should call the correct mocked method' { + { Set-SqlDscServerPermission -ServerObject $mockServerObject @mockDefaultParameters } | + Should -Not -Throw + + $script:mockMethodDenyCallCount | Should -Be 1 + } + + Context 'When passing ServerObject over the pipeline' { + It 'Should call the correct mocked method' { + { $mockServerObject | Set-SqlDscServerPermission @mockDefaultParameters } | + Should -Not -Throw + + $script:mockMethodDenyCallCount | Should -Be 1 + } + } + + Context 'When passing WithGrant' { + BeforeAll { + Mock -CommandName Write-Warning + } + + It 'Should output the correct warning message and return the correct values' { + $mockWarningMessage = InModuleScope -ScriptBlock { + $script:localizedData.ServerPermission_IgnoreWithGrantForStateDeny + } + + { $mockServerObject | Set-SqlDscServerPermission -WithGrant @mockDefaultParameters } | + Should -Not -Throw + + $script:mockMethodDenyCallCount | Should -Be 1 + + Should -Invoke -CommandName 'Write-Warning' -ParameterFilter { + $Message -eq $mockWarningMessage + } + } + } + } + } }