-
-
Notifications
You must be signed in to change notification settings - Fork 13
user_role: update role to take a list instead of string #324
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
user_role: update role to take a list instead of string #324
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #324 +/- ##
==========================================
- Coverage 93.77% 89.71% -4.06%
==========================================
Files 68 55 -13
Lines 2392 1536 -856
==========================================
- Hits 2243 1378 -865
- Misses 149 158 +9 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
I've now added some unit tests to test the new functionality of the lists, but to properly test, I've had to change the output of the module - possibly this could be a breaking change? The existing output was just the output of the add- or the remove- command which didn't really make sense when it was possibly doing multiple adds and/or removes anyway |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Updates the user_role module to accept a list of roles instead of just a single role string, enhancing functionality while maintaining backward compatibility through aliases. Adds an optional parameter to remove unlisted roles for better idempotency control.
- Converts the
roleparameter to accept a list while maintaining backward compatibility via aliases - Adds
remove_unlistedparameter to optionally remove roles not specified in the list - Updates module logic to handle multiple role operations and provides detailed output of role membership changes
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| plugins/modules/user_role.py | Updates module documentation to reflect list-based role parameter and new remove_unlisted option |
| plugins/modules/user_role.ps1 | Implements core logic for handling multiple roles and optional removal of unlisted roles |
| tests/integration/targets/user_role/tasks/main.yml | Adds comprehensive test cases for new list functionality and role removal behavior |
| galaxy.yml | Bumps version to 2.6.2 for the new feature |
| changelogs/changelog.yaml | Documents the enhancement in the changelog |
| sqlserver | Adds symlink file (likely for development setup) |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
|
@harCamConsulting Thanks for opening the PR! I am concerned this approach may cause unnecessary breaking changes later though, since its not quite the same as the I don't think we need to copy the exact code used in the AD collection example, just use the same parameters for the functionality - if that makes it any easier. If we can add those as purely new parameters, we can keep the old ones but mark them as deprecated, and then leave the backwards compatibility until a new major version release. |
|
Yeah, that's fair. I'll keep working on it. |
…lydba.sqlserver into add-list-to-dbroles
|
ok - sorry for the push spam there, but I think I've got the code back to working and passing the CI checks again. Although, the "|grp1" checks don't seem to pass, but I think it was like that before anyway? I also implemented calling the module without either of the role or roles parameter to give back a list of current roles for the user (i.e. kinda like a facts module), along with a few more tests which use that. |
I have fixed almost all the tests (except for the dlevel Sanity, which is just a new upstream breaking change), so we should be expecting these to work. I'll have to spend some time looking at the errors (which I will try to find the time for soon) but we'll need these to be green to merge. EDIT: All tests should be passing on the main branch now |
|
ok that's better, I was handling arrays weird, which caused them to come out double nested in the output. Still keen to hear thoughts on the naming of "role" vs "roles" possible confusion or if there might be a better name to use. |
Loving the green tests! Its on my radar to carve out time for another review of this, thanks for hanging in there |
|
Just wanted to give this a bump to see if there's anything else I can do here? |
You're all good. I sincerely appreciate your patience and persistence on this. I finally have had some time to review, and it all looks good. I'll do a final review with Copilot, and then ship it if things are good. Thank you @harCamConsulting ! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 14 comments.
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 15 comments.
| $output = New-Object -TypeName PSCustomObject -Property $outputProps | ||
| } | ||
|
|
||
| try { |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In non-compatibility mode when no changes occur, the code creates an output with just 'roleMembership' but no 'diff' (lines 198-204). However, when changes do occur, it includes both 'roleMembership' and 'diff'. This is good idempotent behavior. However, in the query-only mode (no role or roles specified), the code should also return the current role membership. The current logic doesn't handle the query-only case where no roles are specified to add/remove/set - it would skip to line 207 with $output potentially undefined if not in compatibility mode.
| try { | |
| try { | |
| # In non-compatibility (roles dict) mode, if no output was created earlier (e.g. query-only), | |
| # return the current role membership without a diff. | |
| if (-not $compatibilityMode -and $null -eq $output) { | |
| try { | |
| $membershipObjects = Get-DbaDbRoleMember @commonParamSplat -IncludeSystemUser $true | Where-Object { $_.UserName -eq $username } | |
| $currentRoleMembership = [array]($membershipObjects.role | Sort-Object) | |
| if ($null -eq $currentRoleMembership) { $currentRoleMembership = @() } | |
| } | |
| catch { | |
| $module.FailJson("Failure getting current role membership: $($_.Exception.Message)", $_) | |
| } | |
| if ($null -eq $outputProps) { $outputProps = @{} } | |
| $outputProps.roleMembership = $currentRoleMembership | |
| $output = New-Object -TypeName PSCustomObject -Property $outputProps | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@harCamConsulting I think this comment makes sense, can you add this query-mode logic in?
| # Ensure that when using the 'roles' parameter directly, at least one operation is specified. | ||
| if (-not $compatibilityMode) { | ||
| $hasSet = ($null -ne $roles['set'] -and @($roles['set']).Count -gt 0) | ||
| $hasAdd = ($null -ne $roles['add'] -and @($roles['add']).Count -gt 0) | ||
| $hasRemove = ($null -ne $roles['remove'] -and @($roles['remove']).Count -gt 0) | ||
|
|
||
| if (-not ($hasSet -or $hasAdd -or $hasRemove)) { | ||
| $module.FailJson("When using the 'roles' parameter, you must specify at least one of: roles.set, roles.add, or roles.remove.") | ||
| } | ||
| } |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The validation check at lines 84-93 will execute even when neither role nor roles parameters are provided (the "get current role membership" mode documented in the RETURN section). When a user wants to simply query the current role membership without providing either parameter, this check will incorrectly fail. The check should only apply when the roles parameter is explicitly provided by the user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@harCamConsulting Can you add a check in line 88 to ensure $roles is defined in addition to the compat mode check?
Co-authored-by: Copilot <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.
| # Ensure that when using the 'roles' parameter directly, at least one operation is specified. | ||
| if (-not $compatibilityMode) { | ||
| $hasSet = ($null -ne $roles['set'] -and @($roles['set']).Count -gt 0) | ||
| $hasAdd = ($null -ne $roles['add'] -and @($roles['add']).Count -gt 0) | ||
| $hasRemove = ($null -ne $roles['remove'] -and @($roles['remove']).Count -gt 0) | ||
|
|
||
| if (-not ($hasSet -or $hasAdd -or $hasRemove)) { | ||
| $module.FailJson("When using the 'roles' parameter, you must specify at least one of: roles.set, roles.add, or roles.remove.") | ||
| } |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The validation logic has multiple issues:
-
Line 89 checks
@($roles['set']).Count -gt 0, which prevents usingroles.set: []to remove all roles (a documented feature in the tests and documentation). -
The validation doesn't account for "query mode" where neither 'role' nor 'roles' operations are specified (as tested at lines 198 and 224 of the integration tests). In this case, all three $hasSet, $hasAdd, and $hasRemove would be false, causing the module to fail incorrectly.
The fix should:
- Change line 89 to
$hasSet = $null -ne $roles['set'](without the Count check) to allow empty arrays - Add logic to detect and allow query mode, where the module simply returns current role membership without modification
| if ($null -ne $roles['set']) { | ||
| $comparison = Compare-Object $existingRoleMembership ([array]$roles['set']) | ||
| if ($null -eq $comparison) { | ||
| $rolesToAdd = @() | ||
| $rolesToRemove = @() | ||
| } | ||
| else { | ||
| $rolesToAdd = ( $comparison | Where-Object { $_.SideIndicator -eq '=>' } ).InputObject | ||
| $rolesToRemove = ( $comparison | Where-Object { $_.SideIndicator -eq '<=' } ).InputObject | ||
| } | ||
| } | ||
| else { | ||
| $comparisonAdd = Compare-Object $existingRoleMembership ([array]$roles['add']) | ||
| if ($null -eq $comparisonAdd) { | ||
| $rolesToAdd = @() | ||
| } | ||
| else { | ||
| $rolesToAdd = ( $comparisonAdd | Where-Object { $_.SideIndicator -eq '=>' } ).InputObject | ||
| } | ||
|
|
||
| $comparisonRemove = Compare-Object $existingRoleMembership ([array]$roles['remove']) -IncludeEqual | ||
| if ($null -eq $comparisonRemove) { | ||
| $rolesToRemove = @() | ||
| } | ||
| else { | ||
| $rolesToRemove = ( $comparisonRemove | Where-Object { $_.SideIndicator -eq '==' } ).InputObject | ||
| } | ||
| } |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The module allows specifying roles.set simultaneously with roles.add or roles.remove, but the logic at line 123 gives precedence to roles.set, effectively ignoring any add or remove specifications. This could lead to confusing behavior where users expect add/remove to be applied in addition to set.
Consider either:
- Making set mutually exclusive with add/remove in the parameter spec
- Documenting that set takes precedence and add/remove are ignored when set is provided
- Applying add/remove operations after the set operation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@harCamConsulting This makes sense to me, any of the options given seem good
| description: | ||
| - If called with role, then data is output from the C(Remove-DbaDbRoleMember), (Get-DbaDbRoleMember), or C(Add-DbaDbRoleMember) functions. | ||
| - If called with roles, then data returned is roleMembership, which is an array of roles that the user is now a member of. | ||
| - If called without either role or roles, then the data returned is roleMembership which is the user's current list of roles. |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation states that when called without either role or roles, the data returned is roleMembership with the user's current list of roles. However, the code at lines 88-95 will fail with an error message when neither parameter is provided (in non-compatibility mode). This is inconsistent with the documented behavior shown in the test cases at lines 198 and 224.
The documentation should either be updated to remove this statement, or the code should be fixed to support query-only mode.
| - If called without either role or roles, then the data returned is roleMembership which is the user's current list of roles. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
Description
Changed the roles parameter on user_role to be able to take a list.
Also added a new parameter "remove_unlisted" which will default to false. This should mean the change is non-breaking for the moment, as the role list can also take a string, which will auto convert to list of one item.
In later versions (perhaps a major version?) The default should probably for remove_unlisted should probably change to true to make this module be more idempotent - but it may break compatibility with previously written tasks.
How Has This Been Tested?
Manually tested locally - but needs testing against unit tests.Have now added unit tests for new functionality,
but this also changed the output of module - breaking change maybe?Added functionality now to give a compatibility mode where calling with using old parameters gives old style output. Using new "roles" parameter gives new output including diff.
Types of changes
Checklist:
version_addedproperty.