[Role] Feature: Add az role deny-assignment create/delete commands#33109
Conversation
️✔️AzureCLI-FullTest
|
|
| rule | cmd_name | rule_message | suggest_message |
|---|---|---|---|
| role deny-assignment | sub group role deny-assignment added |
|
Thank you for your contribution! We will review the pull request and get back to you soon. |
|
The git hooks are available for azure-cli and azure-cli-extensions repos. They could help you run required checks before creating the PR. Please sync the latest code with latest dev branch (for azure-cli) or main branch (for azure-cli-extensions). pip install azdev --upgrade
azdev setup -c <your azure-cli repo path> -r <your azure-cli-extensions repo path>
|
There was a problem hiding this comment.
Pull request overview
Adds first-class az role deny-assignment CRUD support (focused on PP1 user-assigned deny assignments) to align with existing role assignment workflows, including command registration, parameters, help, and tests.
Changes:
- Register
az role deny-assignment list/show/create/deletecommands and add a table transformer for list output. - Implement deny-assignment list/show/create/delete custom handlers with PP1 validation.
- Add help/params, linter exclusions for long options, and introduce a new deny-assignment test file.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
src/azure-cli/azure/cli/command_modules/role/custom.py |
Adds deny-assignment list/show/create/delete implementations and PP1 input validation. |
src/azure-cli/azure/cli/command_modules/role/commands.py |
Registers new role deny-assignment commands and list table transformer. |
src/azure-cli/azure/cli/command_modules/role/_params.py |
Defines CLI parameters for deny-assignment commands. |
src/azure-cli/azure/cli/command_modules/role/_help.py |
Adds help text and examples for the new/updated deny-assignment commands. |
src/azure-cli/azure/cli/command_modules/role/linter_exclusions.yml |
Excludes option-length lint rules for new long parameter names. |
src/azure-cli/azure/cli/command_modules/role/tests/latest/test_deny_assignment.py |
Adds scenario/live tests covering list/show and PP1 create/delete validation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ae59ca3 to
01b4d90
Compare
|
Bumped This picks up the new DenyAssignment management-plane operations ( This was the last upstream blocker on this PR — the CLI commands here now have a real published SDK surface to call against. CC for review awareness: prior reviewers / CLI mgmt module owners. |
|
Follow-up to commit …showed the platform lock files ( CI should now re-run cleanly on the new HEAD. |
…ract changes The new TypeSpec-generated 5.0.0b2 SDK introduced breaking changes that block the Full Test pipeline. Changes: * role/custom.py _resolve_role_id: pass `filter` as a keyword argument to RoleDefinitionsOperations.list (its signature is now `list(scope, *, filter=None)`). This single line was the root cause for the wide blast radius across vm/iot/aro/ams/acr/resource modules and the role module's own RoleAssignmentScenarioTest cases - all of them resolve roles by name and therefore go through this function. * role/custom.py update_role_assignment: replace `RoleAssignment.from_dict(d)` with `RoleAssignment(d)`. The new Model base class accepts a JSON mapping directly and no longer exposes from_dict. * role/custom.py create_deny_assignment: switch from passing a snake_case dict to constructing SDK model objects (DenyAssignment, DenyAssignmentProperties, DenyAssignmentPermission, DenyAssignmentPrincipal). The TypeSpec serializer would have written the raw snake_case keys to the wire instead of camelCase. Also rename `deny_client.create(...)` to `create_or_update(...)` (the method was renamed in the new SDK). * role/custom.py delete_deny_assignment: the new SDK does not expose delete_by_id for deny assignments, so parse the resource ID via a new _parse_deny_assignment_id helper (case-insensitive regex that handles subscription, resource-group, and management-group scopes) and call delete(scope, deny_assignment_id). * role/custom.py show_deny_assignment / delete_deny_assignment / create_deny_assignment: move argument validation ahead of the _auth_client_factory(...) call so validation-only error paths don't require an authenticated session. * tests/latest/test_deny_assignment.py: convert DenyAssignmentListTest to LiveScenarioTest (these hit the live API and have no recorded cassettes in the repo), and replace DenyAssignmentShowTest with a unittest-based DenyAssignmentShowValidationTest that calls show_deny_assignment() directly and asserts the CLIError - both changes remove the dependency on a logged-in session in playback CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract _build_deny_assignment_model helper to keep the local count under the project's max-locals=25 threshold (was 26/25 in azdev-style after the SDK-model refactor). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pipeline build 314889 (re-triggered 11 May) still fails because the 8 May patch
did not address every breaking change in the new TypeSpec-generated SDK. This
commit fixes the four remaining classes of issue.
* role/custom.py update_role_assignment: `RoleAssignment(some_dict)` silently
produces a model with `properties=None` (the new model wraps domain
attributes under a nested `properties` field, with `__flattened_items`
exposing them via descriptors only when `properties` is set). The old code
then read `assignment.scope` -> None and passed scope=None to the SDK URL
serializer, which raised `ValueError('No value for given attribute')`.
Read scope/name/principalType/etc. directly from the user-supplied flat
camelCase dict and send a `{"properties": {...}}` JSON body to .create()
via its JSON overload - simpler and avoids the new model's flatten/unflatten
gymnastics. Caused test_role_assignment_create_update.
* role/custom.py list_role_assignments + list_deny_assignments +
show_deny_assignment: `knack.util.todict` walks `__dict__` and
therefore returns `{}` for the new MutableMapping-based models, so the
subsequent `ra['roleDefinitionId']` / `ra['principalId']` lookups
raised `KeyError`. Added explicit _role_assignment_to_dict and
_deny_assignment_to_dict adapters that project the model back to the
legacy flat camelCase shape (with enum -> str coercion) and routed all
list/show paths through them. Caused
test_create_for_rbac_password_with_assignment.
* vm/_validators.py, ams/operations/sp.py, containerapp/_utils.py,
acs/_roleassignments.py: `RoleDefinitionsOperations.list` is now
`list(scope, *, filter=None)`, so each remaining caller that passed
filter positionally raised
`TypeError: list() takes 2 positional arguments but 3 were given`.
This is the same bug fixed in 2d2c80a for role/custom.py::_resolve_role_id;
these four call sites were missed. Caused test_vm_msi, test_vm_explicit_msi,
test_vmss_msi, test_vmss_explicit_msi, test_ams_sp_create_reset, and the
ACS / Container Apps role-resolution paths.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
While running the previously-failing tests locally to verify 005aa58, test_identity_hub continued to fail because azure-cli-core itself has its own resolve_role_id helper at src/azure-cli-core/azure/cli/core/commands/arm.py that also passed filter positionally to RoleDefinitionsOperations.list. This is the shared utility many modules call via core.commands.arm.create_role_assignment. Same one-line fix as the four module-level callers patched in 005aa58: pass filter as a keyword argument so it works with the new TypeSpec-generated SDK signature `list(scope, *, filter=None)`. Local verification (azure-mgmt-authorization 5.0.0b2 installed): * test_role_assignment_create_update PASSED (was: ValueError) * test_role_assignment_scenario PASSED * test_role_assignment_create_using_principal_type PASSED * test_role_assignment_handle_conflicted_assignments PASSED * test_create_for_rbac_password_with_assignment PASSED (was: KeyError) * test_vm_msi PASSED (was: TypeError) * test_vm_explicit_msi PASSED * test_vmss_msi PASSED * test_vmss_explicit_msi PASSED * test_identity_hub PASSED (was: TypeError - this commit's fix) * Full role test sweep: 17 passed, 2 LiveOnly skipped * Role unit tests: 14 passed test_ams_sp_create_reset still fails with VCR cassette mismatch (the cassette was recorded against azure-mgmt-authorization 4.x but dev branch bumped to 5.0.0b1 in Azure#31859 without re-recording AMS cassettes; multiple identical requests now exhaust the single recorded response). This is pre-existing on dev and outside the scope of this PR. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The azdev-style GitHub Actions check (run id 25725694563) failed flake8 on the new `_coerce` function added in 005aa58 because it followed the section comment block with only one blank line. Adding the second blank line resolves the two reported E302 errors at line 46. Verified locally with `azdev style --pep8` -> Flake8: PASSED. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
9064242 to
07b2efa
Compare
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
isra-fel
left a comment
There was a problem hiding this comment.
Hi please see the inline comment about aligning the design of assignment and deny-assignment. Also please make sure CI is green.
Aligns with the --assignee-object-id / --assignee-principal-type pattern from 'az role assignment create', per review feedback from isra-fel. Uses --principal-object-id rather than --assignee-object-id because the target of a deny assignment is semantically a 'principal being denied', not an 'assignee being granted'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
- arm.py: Coerce AAZSimpleValue to str before passing to authorization SDK (fixes TypeError 'AAZSimpleValue is not JSON serializable' in policy assignment create with managed identity) - resource: Re-record test_resource_policy_identity and test_resource_policy_identity_systemassigned cassettes against 5.0.0b2 - aro: Fix test_aro_public_cluster cassette URI mismatches (double-slash from empty parentResourcePath + casing alignment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
The TypeSpec-generated authorization SDK (5.x) nests RoleAssignment domain attributes under a 'properties' envelope, and knack.util.todict does not walk these MutableMapping models. ams list_role_assignments assumed the legacy flat shape and raised KeyError: 'roleDefinitionId', causing sp reset-credentials to exhaust its role-assignment retries and fail test_ams_sp_create_reset with a VCR cassette-exhaustion error. Project the model to the flat camelCase dict via its snake_case attributes, mirroring the role module's adapter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
…nment-commands # Conflicts: # src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/test_resource_policy_identity.yaml # src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/test_resource_policy_identity_systemassigned.yaml
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
|
This PR is one of those who updates test cases from multiple modules because of sdk update. As we agreed on instead of pinging several codeonwers I will force merge it. |
Description
Adds
az role deny-assignment createandaz role deny-assignment deletecommands for managing user-assigned deny assignments, matching the existingaz role assignmentpattern.This implements the PUT/DELETE operations added in the
Microsoft.Authorization/denyAssignmentsAPI (2024-07-01-preview), as specified in TypeSpec PR #41617.Two Assignment Modes
The
createcommand supports two modes for targeting principals:Everyone mode (default): Denies all principals at the scope. At least one excluded principal is required via
--exclude-principal-ids:Per-principal mode: Denies a specific User or ServicePrincipal via
--principal-idand--principal-type:Service Constraints
User-assigned deny assignments have specific restrictions enforced by the service:
*/readare not permittedCommands
az role deny-assignment list— List deny assignments (existing, enhanced)az role deny-assignment show— Show a deny assignment (existing, enhanced)az role deny-assignment create— Create a user-assigned deny assignmentaz role deny-assignment delete— Delete a user-assigned deny assignmentFiles Changed
commands.py— Command registration forrole deny-assignmentgroupcustom.py— Business logic with dual-mode principal handling and validation_params.py— Parameter definitions including--principal-idand--principal-type_help.py— Help text with examples for both Everyone and per-principal modeslinter_exclusions.yml— Exclusions for long parameter namestests/latest/test_deny_assignment.py— Unit tests (list, show, CRUD, per-principal, Group rejection, param validation)Dependency
Testing
Tests are included in
test_deny_assignment.py. Full end-to-end testing requires:Microsoft.Authorization/SubscriptionAllowedToOperateUserAssignedDenyAssignmentfeature flag registeredRelated
azure-mgmt-authorization 5.0.0b2— released ✅armauthorization v3.0.0-beta.3— released ✅azure-resourcemanager-authorization 2.54.0-beta.1— released ✅@azure/arm-authorization 10.0.0-beta.3— released ✅