Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b41547f
Initial plan
Copilot Aug 30, 2025
683ea11
Add core server permission commands and private functions
Copilot Aug 30, 2025
6e04b2e
Add integration tests and update CHANGELOG for new server permission …
Copilot Aug 30, 2025
f4db6ab
Update server permission commands to use State parameter and ServerPe…
Copilot Aug 30, 2025
4299b88
Merge branch 'main' into copilot/fix-e806ea1d-2077-4c68-a272-f8dea527…
johlju Aug 30, 2025
167bac9
Restructure server permission commands based on feedback - rename com…
Copilot Aug 30, 2025
24f4c67
Change Permission parameter to accept string array for easier usage
Copilot Aug 30, 2025
a1c5aab
Implement all requested improvements to server permission commands
Copilot Aug 31, 2025
50e8487
Address feedback: update server permission commands to use Login/Serv…
Copilot Aug 31, 2025
345461e
Clean up code
johlju Aug 31, 2025
aea4697
Fix blank line EOF
johlju Aug 31, 2025
3dcc4a4
Change error handling to use PSCmdlet.ThrowTerminatingError in server…
Copilot Aug 31, 2025
f78e7d6
Merge branch 'main' into copilot/fix-e806ea1d-2077-4c68-a272-f8dea527…
johlju Aug 31, 2025
3ec521e
Remove unused $principalObject variables from server permission commands
Copilot Aug 31, 2025
cb5e97a
Refactor SQL DSC Server Permission Tests
johlju Aug 31, 2025
8a3dd4d
Update integration tests
johlju Aug 31, 2025
2c337e3
Merge branch 'main' into copilot/fix-e806ea1d-2077-4c68-a272-f8dea527…
johlju Aug 31, 2025
a14e934
Remove integration tests for Get-SqlDscServerPermission
johlju Aug 31, 2025
1edcd8f
Refactor permission handling in integration and unit tests to elimina…
johlju Aug 31, 2025
7278bfe
Update CHANGELOG.md
johlju Aug 31, 2025
5c9a575
Update tests/Integration/Commands/Deny-SqlDscServerPermission.Integra…
johlju Aug 31, 2025
01d832a
Update tests/Integration/Commands/Deny-SqlDscServerPermission.Integra…
johlju Aug 31, 2025
eba49be
Remove unit test stub imports from server permission integration tests
Copilot Aug 31, 2025
bc3112f
Reorganize server permissions in SqlServerPermission enum for clarity…
johlju Aug 31, 2025
8c1aaa3
Reorganize integration test commands by grouping and adding missing r…
johlju Aug 31, 2025
546e22e
Refactor AfterAll block in Deny-SqlDscServerPermission integration te…
johlju Aug 31, 2025
a7ea287
Update Pester guidelines to consolidate requirements and best practic…
johlju Aug 31, 2025
475290e
Refactor integration tests to remove default parameter values and add…
johlju Aug 31, 2025
47586c2
Reorganize integration test commands to improve clarity and add missi…
johlju Aug 31, 2025
360c066
Remove redundant module removal command from AfterAll blocks in integ…
johlju Aug 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Enhanced workflow with proper environment variable configuration and DSCv3 verification.
- Fixed environment variable persistence by using $GITHUB_ENV instead of
job-level env declaration.
- `New-SqlDscServerPermission`
- Added new public command to grant server permissions to a principal on a SQL Server Database Engine instance.
- `Test-SqlDscServerPermission`
- Added new public command to test if server permissions for a principal are in the desired state.
- `Remove-SqlDscServerPermission`
- Added new public command to remove (revoke) server permissions from a principal on a SQL Server Database Engine instance.
- `Invoke-SqlDscServerPermissionOperation`
- Added new private function to encapsulate core logic for server permission operations.
- `Test-SqlDscServerPermissionState`
- Added new private function to test if server permissions are in the desired state.
- `Assert-SqlDscLogin`
- Added new public command to validate that a specified SQL Server principal
is a login.
Expand Down
132 changes: 132 additions & 0 deletions source/Private/Invoke-SqlDscServerPermissionOperation.ps1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot We should move the relevant logic into the Deny-, Grant-, Revoke- commands instead, then remove this private function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed the private function Invoke-SqlDscServerPermissionOperation and moved the relevant logic directly into the Grant-, Deny-, and Revoke- commands as requested. This makes the code clearer and removes unnecessary abstraction. Changes are in commit a1c5aab.

Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<#
.SYNOPSIS
Invokes a server permission operation on a SQL Server principal.

.DESCRIPTION
This private function encapsulates the core logic for granting, denying,
or revoking server permissions on a SQL Server principal. It validates
the principal exists and executes the specified permission operation.

.PARAMETER ServerObject
Specifies current server connection object.

.PARAMETER Name
Specifies the name of the principal for which the permissions are modified.

.PARAMETER Permission
Specifies the permissions.

.PARAMETER State
Specifies the state of the permission operation (Grant, Deny, Revoke).

.PARAMETER WithGrant
Specifies that the principal should also be granted the right to grant
other principals the same permission. This parameter is only valid when
parameter State is set to Grant or Revoke.

.OUTPUTS
None.
#>
function Invoke-SqlDscServerPermissionOperation
{
[CmdletBinding()]
[OutputType()]
param
(
[Parameter(Mandatory = $true)]
[Microsoft.SqlServer.Management.Smo.Server]
$ServerObject,

[Parameter(Mandatory = $true)]
[System.String]
$Name,

[Parameter(Mandatory = $true)]
[Microsoft.SqlServer.Management.Smo.ServerPermissionSet]
$Permission,

[Parameter(Mandatory = $true)]
[ValidateSet('Grant', 'Deny', 'Revoke')]
[System.String]
$State,

[Parameter()]
[System.Management.Automation.SwitchParameter]
$WithGrant
)

# Validate that the principal exists
$testSqlDscIsPrincipalParameters = @{
ServerObject = $ServerObject
Name = $Name
}

$isLogin = Test-SqlDscIsLogin @testSqlDscIsPrincipalParameters
$isRole = Test-SqlDscIsRole @testSqlDscIsPrincipalParameters

if (-not ($isLogin -or $isRole))
{
$missingPrincipalMessage = $script:localizedData.ServerPermission_MissingPrincipal -f $Name, $ServerObject.InstanceName

$PSCmdlet.ThrowTerminatingError(
[System.Management.Automation.ErrorRecord]::new(
$missingPrincipalMessage,
'ISDSP0001', # cSpell: disable-line
[System.Management.Automation.ErrorCategory]::InvalidOperation,
$Name
)
)
}

# Get the permissions names that are set to $true in the ServerPermissionSet.
$permissionName = $Permission |
Get-Member -MemberType 'Property' |
Select-Object -ExpandProperty 'Name' |
Where-Object -FilterScript {
$Permission.$_
}

switch ($State)
{
'Grant'
{
Write-Verbose -Message (
$script:localizedData.ServerPermission_GrantPermission -f ($permissionName -join ','), $Name
)

if ($WithGrant.IsPresent)
{
$ServerObject.Grant($Permission, $Name, $true)
}
else
{
$ServerObject.Grant($Permission, $Name)
}
}

'Deny'
{
Write-Verbose -Message (
$script:localizedData.ServerPermission_DenyPermission -f ($permissionName -join ','), $Name
)

$ServerObject.Deny($Permission, $Name)
}

'Revoke'
{
Write-Verbose -Message (
$script:localizedData.ServerPermission_RevokePermission -f ($permissionName -join ','), $Name
)

if ($WithGrant.IsPresent)
{
$ServerObject.Revoke($Permission, $Name, $false, $true)
}
else
{
$ServerObject.Revoke($Permission, $Name)
}
}
}
}
152 changes: 152 additions & 0 deletions source/Private/Test-SqlDscServerPermissionState.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<#
.SYNOPSIS
Tests if server permissions for a principal are in the desired state.

.DESCRIPTION
This private function tests if server permissions for a principal are
in the desired state by comparing current permissions with desired permissions.

.PARAMETER ServerObject
Specifies current server connection object.

.PARAMETER Name
Specifies the name of the principal to test.

.PARAMETER State
Specifies the desired state of the permission to be tested.

.PARAMETER Permission
Specifies the desired permissions as a ServerPermissionSet object.

.PARAMETER WithGrant
Specifies that the principal should have the right to grant other principals
the same permission. This parameter is only valid when parameter **State** is
set to 'Grant'.

.OUTPUTS
[System.Boolean]
#>
function Test-SqlDscServerPermissionState
{
[CmdletBinding()]
[OutputType([System.Boolean])]
param
(
[Parameter(Mandatory = $true)]
[Microsoft.SqlServer.Management.Smo.Server]
$ServerObject,

[Parameter(Mandatory = $true)]
[System.String]
$Name,

[Parameter(Mandatory = $true)]
[ValidateSet('Grant', 'GrantWithGrant', 'Deny')]
[System.String]
$State,

[Parameter(Mandatory = $true)]
[Microsoft.SqlServer.Management.Smo.ServerPermissionSet]
$Permission,

[Parameter()]
[System.Management.Automation.SwitchParameter]
$WithGrant
)

Write-Verbose -Message (
$script:localizedData.ServerPermission_TestingDesiredState -f $Name, $ServerObject.InstanceName
)

# Handle WithGrant parameter by adjusting the effective state
$effectiveState = $State
if ($WithGrant.IsPresent -and $State -eq 'Grant')
{
$effectiveState = 'GrantWithGrant'
}

# Get current permissions
$serverPermissionInfo = $ServerObject |
Get-SqlDscServerPermission -Name $Name -ErrorAction 'SilentlyContinue'

if (-not $serverPermissionInfo)
{
Write-Verbose -Message (
$script:localizedData.ServerPermission_PermissionNotInDesiredState -f 'All', $effectiveState, $Name
)
return $false
}

# Convert current permissions to ServerPermission objects
$currentPermissions = $serverPermissionInfo | ConvertTo-SqlDscServerPermission

# Find the current permission for the desired state
$currentPermissionForState = $currentPermissions |
Where-Object -FilterScript {
$_.State -eq $effectiveState
}

if (-not $currentPermissionForState)
{
$currentPermissionForState = [ServerPermission] @{
State = $effectiveState
Permission = @()
}
}

# Get the list of permission names that should be true in the permission set
$desiredPermissionNames = @()
$permissionProperties = $Permission | Get-Member -MemberType Property | Where-Object { $_.Name -ne 'IsEmpty' }

foreach ($property in $permissionProperties)
{
if ($Permission.$($property.Name) -eq $true)
{
$desiredPermissionNames += $property.Name
}
}

# Check if all desired permissions are present in current state
foreach ($desiredPermissionName in $desiredPermissionNames)
{
if ($desiredPermissionName -notin $currentPermissionForState.Permission)
{
Write-Verbose -Message (
$script:localizedData.ServerPermission_PermissionNotInDesiredState -f $desiredPermissionName, $effectiveState, $Name
)
return $false
}
}

# Check if current state has permissions not in desired state (unless desired is empty)
if ($desiredPermissionNames.Count -gt 0)
{
foreach ($currentPermissionName in $currentPermissionForState.Permission)
{
if ($currentPermissionName -notin $desiredPermissionNames)
{
Write-Verbose -Message (
$script:localizedData.ServerPermission_PermissionNotInDesiredState -f $currentPermissionName, $effectiveState, $Name
)
return $false
}
}
}
else
{
# If no permissions are desired, current should also be empty
if ($currentPermissionForState.Permission.Count -gt 0)
{
Write-Verbose -Message (
$script:localizedData.ServerPermission_PermissionNotInDesiredState -f ($currentPermissionForState.Permission -join ', '), $effectiveState, $Name
)
return $false
}
}

Write-Verbose -Message (
$script:localizedData.ServerPermission_InDesiredState -f $Name
)

return $true
}
Loading
Loading