Skip to content

Add New-AzDenyAssignment and Remove-AzDenyAssignment cmdlets#29340

Open
jruttle wants to merge 6 commits intoAzure:mainfrom
jruttle:jruttle/add-deny-assignment-cmdlets
Open

Add New-AzDenyAssignment and Remove-AzDenyAssignment cmdlets#29340
jruttle wants to merge 6 commits intoAzure:mainfrom
jruttle:jruttle/add-deny-assignment-cmdlets

Conversation

@jruttle
Copy link
Copy Markdown

@jruttle jruttle commented Mar 30, 2026

Description

Add New-AzDenyAssignment and Remove-AzDenyAssignment cmdlets for user-assigned deny assignments using the 2024-07-01-preview Authorization API.

Cmdlets Added

  • New-AzDenyAssignment — Creates a user-assigned deny assignment at subscription or resource group scope
  • Remove-AzDenyAssignment — Removes a user-assigned deny assignment by ID, name+scope, or pipeline input

Key Design Decisions (PP1 Model)

  • Principals default to "Everyone" (SystemDefined with Guid.Empty) — this is the only supported principal type in PP1
  • ExcludePrincipalId is required (at least one principal must be excluded when denying Everyone)
  • DataActions and DoNotApplyToChildScopes are not supported — tests validate these are rejected with proper errors
  • Only write/delete/action permissions can be denied (read actions are rejected by the API)

Changes

  • SDK regenerated from merged swagger PR Azure/azure-rest-api-specs#41104 (commit 31fb4b3e)
  • New files: 2 cmdlets, 1 options model, C# test harness, PS1 test scripts, 12 session recordings
  • Modified: AuthorizationClient.cs (create/remove logic), Az.Resources.psd1 (exports), SDK README (commit hash)

Tests

  • 12 LiveOnly tests covering: subscription scope, RG scope, custom ID, input file, exclude principals, DataActions (negative), ChildScopes (negative), remove by ID/name/InputObject/PassThru, full E2E CRUD cycle
  • All 12 tests pass in Record mode against live Azure
  • Session recordings included as documentation

Related

Copilot AI review requested due to automatic review settings March 30, 2026 13:07
@azure-client-tools-bot-prd
Copy link
Copy Markdown

Thanks for your contribution! The pull request validation has started. Please revisit this comment for updated status.

@NoriZC
Copy link
Copy Markdown
Contributor

NoriZC commented Mar 30, 2026

/azp run

@azure-pipelines
Copy link
Copy Markdown
Contributor

Azure Pipelines successfully started running 3 pipeline(s).

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds authoring support for user-assigned deny assignments in Az.Resources by introducing New-AzDenyAssignment and Remove-AzDenyAssignment, backed by the regenerated Authorization.Management.Sdk targeting 2024-07-01-preview.

Changes:

  • Added new cmdlets and a create-options model for deny assignment CRUD.
  • Extended AuthorizationClient / extension mapping to support new deny assignment principal model types.
  • Added LiveOnly scenario tests plus session recordings; updated SDK regeneration README and generated SDK code.

Reviewed changes

Copilot reviewed 21 out of 36 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
src/Resources/Resources/Models.Authorization/CreateDenyAssignmentOptions.cs New options model used to build deny assignment create payloads / input file format.
src/Resources/Resources/Models.Authorization/AuthorizationClientExtensions.cs Adds principal mapping overloads for the regenerated deny assignment principal types.
src/Resources/Resources/Models.Authorization/AuthorizationClient.cs Implements create/delete operations for deny assignments using regenerated SDK APIs.
src/Resources/Resources/DenyAssignments/NewAzureDenyAssignmentCommand.cs New New-AzDenyAssignment cmdlet implementation.
src/Resources/Resources/DenyAssignments/RemoveAzureDenyAssignmentCommand.cs New Remove-AzDenyAssignment cmdlet implementation.
src/Resources/Resources/Az.Resources.psd1 Exports the newly added cmdlets.
src/Resources/Resources.Test/ScenarioTests/DenyAssignmentCrudTests.ps1 Adds LiveOnly scenario test scripts for deny assignment CRUD and negative cases.
src/Resources/Resources.Test/ScenarioTests/DenyAssignmentCrudTests.cs Adds C# test harness wiring to run the new scenario scripts.
src/Resources/Resources.Test/SessionRecords/DenyAssignmentCrudTests/.json Adds session recordings for the new tests.
src/Resources/Authorization.Management.Sdk/README.md Updates swagger commit reference and input-file paths used for SDK regeneration.
src/Resources/Authorization.Management.Sdk/Generated/* Regenerated SDK with deny assignment CRUD, updated models, and supporting types.

…, typed helpers

- Fix Azure#4: Pass Force.IsPresent to ConfirmAction so -Force suppresses prompt
- Fix Azure#7/Azure#8: Remove unused ProjectResources aliases (CS8019)
- Fix Azure#1/Azure#9: Remove PrincipalId param, reject non-Everyone principals with clear error
- Fix Azure#10: Add InputFile validation for ExcludePrincipalIds
- Fix Azure#5: Remove stale scaffolded TODO comment
- Fix Azure#6: Throw when multiple DAs match by name (disambiguate by ID)
- Fix Azure#2: Replace dynamic ToPSPrincipalsCore with generic typed helper
- Fix Azure#3: Update ExcludePrincipals test to use multiple principals with per-type array

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@NoriZC
Copy link
Copy Markdown
Contributor

NoriZC commented Mar 30, 2026

/azp run

@azure-pipelines
Copy link
Copy Markdown
Contributor

Azure Pipelines successfully started running 3 pipeline(s).

Copilot AI review requested due to automatic review settings March 30, 2026 18:16
@NoriZC
Copy link
Copy Markdown
Contributor

NoriZC commented Mar 30, 2026

/azp run

@azure-pipelines
Copy link
Copy Markdown
Contributor

Azure Pipelines successfully started running 3 pipeline(s).

Copy link
Copy Markdown
Contributor

Copilot AI left a 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 24 out of 39 changed files in this pull request and generated 6 comments.

@VeryEarly
Copy link
Copy Markdown
Collaborator

@VeryEarly VeryEarly self-assigned this Mar 31, 2026
@NoriZC
Copy link
Copy Markdown
Contributor

NoriZC commented Mar 31, 2026

/azp run

@azure-pipelines
Copy link
Copy Markdown
Contributor

Azure Pipelines successfully started running 3 pipeline(s).

@jruttle
Copy link
Copy Markdown
Author

jruttle commented Mar 31, 2026

Thanks @VeryEarly - this was addressed in commit da9de1f (the 4th push). New-AzDenyAssignment now has SupportsShouldProcess = true on the [Cmdlet] attribute and the create call is wrapped in a ShouldProcess() guard. Remove-AzDenyAssignment already had it from the initial implementation.

Copilot AI review requested due to automatic review settings March 31, 2026 19:35
@NoriZC
Copy link
Copy Markdown
Contributor

NoriZC commented Mar 31, 2026

/azp run

@azure-pipelines
Copy link
Copy Markdown
Contributor

Azure Pipelines successfully started running 3 pipeline(s).

Copy link
Copy Markdown
Contributor

Copilot AI left a 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 24 out of 39 changed files in this pull request and generated 4 comments.

Comment on lines +660 to +671
var excludePrincipals = new List<DenyAssignmentPrincipal>();
var excludeIds = options.ExcludePrincipalIds ?? new List<string>();
var excludeTypes = options.ExcludePrincipalTypes;
var defaultType = options.ExcludePrincipalType ?? "User";

for (int i = 0; i < excludeIds.Count; i++)
{
var type = (excludeTypes != null && i < excludeTypes.Count)
? excludeTypes[i]
: defaultType;
excludePrincipals.Add(new DenyAssignmentPrincipal { Id = excludeIds[i], Type = type });
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

ExcludePrincipalTypes is intended to support either one type applied to all ExcludePrincipalIds or one per ID (as described by the cmdlet help/validation), but the current mapping only uses excludeTypes[i] when i < excludeTypes.Count. If the caller supplies a single ExcludePrincipalType value with multiple IDs, only the first excluded principal gets that type and the rest fall back to ExcludePrincipalType (legacy) / "User". Update the mapping so that when ExcludePrincipalTypes.Count == 1, that value is applied to every excluded principal (and consider validating mismatched counts > 1).

Copilot uses AI. Check for mistakes.
Comment on lines +133 to +150
$excludeUser = Get-TestExcludePrincipalId
$spId = "c090fe3f-66fa-4e18-8142-107d8f4cd0e4" # DenyAssignmentTestApp
$daName = "Test-DA-ExcludePrincipals-" + [Guid]::NewGuid().ToString().Substring(0, 8)

try
{
# Exclude both the user and the test service principal
$da = New-AzDenyAssignment `
-DenyAssignmentName $daName `
-Description "Test deny assignment with multiple exclude principals" `
-Scope $subscriptionScope `
-Action "Microsoft.Storage/storageAccounts/write" `
-ExcludePrincipalId @($excludeUser, $spId) `
-ExcludePrincipalType @("User", "ServicePrincipal")

Assert-NotNull $da
Assert-True { $da.ExcludePrincipals.Count -ge 2 }
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

This test claims to exclude both a user and a service principal, but $spId is set to an application/client ID value. The cmdlet parameter and API expect principal object IDs, so this may not actually be validating multi-principal exclusion (and may be tenant-dependent). Consider resolving the service principal object ID (or making it configurable via an environment variable) and ensure the assertion reflects the actual excluded principals returned.

Copilot uses AI. Check for mistakes.
"696"
]
},
"RequestBody": "{\r\n \"properties\": {\r\n \"denyAssignmentName\": \"Test-DA-ExcludePrincipals-15a41d72\",\r\n \"description\": \"Test deny assignment with multiple exclude principals\",\r\n \"permissions\": [\r\n {\r\n \"actions\": [\r\n \"Microsoft.Storage/storageAccounts/write\"\r\n ],\r\n \"notActions\": [],\r\n \"dataActions\": [],\r\n \"notDataActions\": []\r\n }\r\n ],\r\n \"doNotApplyToChildScopes\": false,\r\n \"principals\": [\r\n {\r\n \"id\": \"00000000-0000-0000-0000-000000000000\",\r\n \"type\": \"SystemDefined\"\r\n }\r\n ],\r\n \"excludePrincipals\": [\r\n {\r\n \"id\": \"1840cc0e-55b5-442d-bbf6-52c0c7e27302\",\r\n \"type\": \"User\"\r\n }\r\n ]\r\n }\r\n}",
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The session recording for the "multiple exclude principals" scenario only contains a single entry in excludePrincipals (the user). This is inconsistent with the corresponding test (Test-NewDaWithExcludePrincipals) which passes two excluded principals and asserts ExcludePrincipals.Count -ge 2. Please re-record/update this session file (or adjust the test) so the recording reflects the current scenario intent.

Suggested change
"RequestBody": "{\r\n \"properties\": {\r\n \"denyAssignmentName\": \"Test-DA-ExcludePrincipals-15a41d72\",\r\n \"description\": \"Test deny assignment with multiple exclude principals\",\r\n \"permissions\": [\r\n {\r\n \"actions\": [\r\n \"Microsoft.Storage/storageAccounts/write\"\r\n ],\r\n \"notActions\": [],\r\n \"dataActions\": [],\r\n \"notDataActions\": []\r\n }\r\n ],\r\n \"doNotApplyToChildScopes\": false,\r\n \"principals\": [\r\n {\r\n \"id\": \"00000000-0000-0000-0000-000000000000\",\r\n \"type\": \"SystemDefined\"\r\n }\r\n ],\r\n \"excludePrincipals\": [\r\n {\r\n \"id\": \"1840cc0e-55b5-442d-bbf6-52c0c7e27302\",\r\n \"type\": \"User\"\r\n }\r\n ]\r\n }\r\n}",
"RequestBody": "{\r\n \"properties\": {\r\n \"denyAssignmentName\": \"Test-DA-ExcludePrincipals-15a41d72\",\r\n \"description\": \"Test deny assignment with multiple exclude principals\",\r\n \"permissions\": [\r\n {\r\n \"actions\": [\r\n \"Microsoft.Storage/storageAccounts/write\"\r\n ],\r\n \"notActions\": [],\r\n \"dataActions\": [],\r\n \"notDataActions\": []\r\n }\r\n ],\r\n \"doNotApplyToChildScopes\": false,\r\n \"principals\": [\r\n {\r\n \"id\": \"00000000-0000-0000-0000-000000000000\",\r\n \"type\": \"SystemDefined\"\r\n }\r\n ],\r\n \"excludePrincipals\": [\r\n {\r\n \"id\": \"1840cc0e-55b5-442d-bbf6-52c0c7e27302\",\r\n \"type\": \"User\"\r\n },\r\n {\r\n \"id\": \"8c1e7f2e-4f5a-4f4c-9f3a-2c8f5d9b7a11\",\r\n \"type\": \"ServicePrincipal\"\r\n }\r\n ]\r\n }\r\n}",

Copilot uses AI. Check for mistakes.
Comment on lines +133 to +137
// PP1 client-side validation: DataActions not supported
if (DataAction != null && DataAction.Length > 0)
{
throw new PSArgumentException(
"DataActions are not supported for PP1 user-assigned deny assignments. " +
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

Client-side PP1 validation currently blocks -DataAction but not -NotDataAction, even though NotDataAction is also a data-action list and the cmdlet help says data actions are not supported in PP1. This allows unsupported input through and yields inconsistent (service-side) failures. Consider adding the same validation for NotDataAction (and optionally for input-file scenarios if consistent behavior is desired).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants