Skip to content

Conversation

@atomassi
Copy link
Contributor

@atomassi atomassi commented Jan 14, 2026

Fixes #29058
Fixes #25940

Description

BREAKING CHANGE
This PR changes the output structure of Get-AzRoleDefinition, New-AzRoleDefinition, Set-AzRoleDefinition, and Remove-AzRoleDefinition. See Breaking Change Note below.

This is a bug fix - Get-AzRoleDefinition was returning incorrect data (Condition: null) for roles that have conditions on non-first permission entries.

The Bug

The Azure API returns role definitions with multiple permission entries, each with its own Condition. The old code:

  1. Flattened all permissions into single Actions, NotActions, etc. properties
  2. Only read Condition from permissions[0].condition

roleDefinition = new PSRoleDefinition
{
Name = role.RoleName,
Actions = new List<string>(role.Permissions.SelectMany(r => r.Actions)),
NotActions = new List<string>(role.Permissions.SelectMany(r => r.NotActions)),
DataActions = new List<string>(role.Permissions.SelectMany(r => r.DataActions)),
NotDataActions = new List<string>(role.Permissions.SelectMany(r => r.NotDataActions)),
Id = role.Id.GuidFromFullyQualifiedId(),
AssignableScopes = role.AssignableScopes.ToList(),
Description = role.Description,
IsCustom = role.RoleType == CustomRole ? true : false,
Condition = (role.Permissions != null && role.Permissions.Count > 0) ? role.Permissions[0].Condition : null,
ConditionVersion = (role.Permissions != null && role.Permissions.Count > 0) ? role.Permissions[0].ConditionVersion : null
};

For roles like "Azure Container Storage Contributor" where the ABAC condition is on permissions[1], users calling $role.Condition got $null - incorrect data that didn't match the API response.

API Response (actual data):

GET /providers/Microsoft.Authorization/roleDefinitions/95dd08a6-00bd-4661-84bf-f6726f83a4d0?api-version=2022-05-01-preview

{
  "properties": {
    "roleName": "Azure Container Storage Contributor",
    "type": "BuiltInRole",
    "description": "Lets you install Azure Container Storage and manage its storage resources",
    "assignableScopes": [
      "/"
    ],
    "permissions": [
      {
        "actions": [
          "Microsoft.KubernetesConfiguration/extensions/write",
          "Microsoft.KubernetesConfiguration/extensions/read",
          "Microsoft.KubernetesConfiguration/extensions/delete",
          "Microsoft.KubernetesConfiguration/extensions/operations/read",
          "Microsoft.Authorization/*/read",
          "Microsoft.Resources/subscriptions/resourceGroups/read",
          "Microsoft.Resources/subscriptions/read",
          "Microsoft.Management/managementGroups/read",
          "Microsoft.Resources/deployments/*",
          "Microsoft.Support/*"
        ],
        "notActions": [],
        "dataActions": [],
        "notDataActions": []
      },
      {
        "actions": [
          "Microsoft.Authorization/roleAssignments/write",
          "Microsoft.Authorization/roleAssignments/delete"
        ],
        "notActions": [],
        "dataActions": [],
        "notDataActions": [],
        "conditionVersion": "2.0",
        "condition": "((!(ActionMatches{'Microsoft.Authorization/roleAssignments/write'})) OR (@Request[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals{08d4c71acc634ce4a9c85dd251b4d619})) AND ((!(ActionMatches{'Microsoft.Authorization/roleAssignments/delete'})) OR (@Resource[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals{08d4c71acc634ce4a9c85dd251b4d619}))"
      }
    ],
    "createdOn": "2024-03-06T18:39:55.6502598Z",
    "updatedOn": "2024-03-28T20:02:49.6413404Z",
    "createdBy": null,
    "updatedBy": null
  },
  "id": "/providers/Microsoft.Authorization/roleDefinitions/95dd08a6-00bd-4661-84bf-f6726f83a4d0",
  "type": "Microsoft.Authorization/roleDefinitions",
  "name": "95dd08a6-00bd-4661-84bf-f6726f83a4d0"
}

Old cmdlet output (incorrect):

> Get-AzRoleDefinition -Id "95dd08a6-00bd-4661-84bf-f6726f83a4d0" | ConvertTo-Json -Depth 10
{
  "Name": "Azure Container Storage Contributor",
  "Id": "95dd08a6-00bd-4661-84bf-f6726f83a4d0",
  "IsCustom": false,
  "Description": "Lets you install Azure Container Storage and manage its storage resources",
  "Actions": [
    "Microsoft.KubernetesConfiguration/extensions/write",
    "Microsoft.KubernetesConfiguration/extensions/read",
    "Microsoft.KubernetesConfiguration/extensions/delete",
    "Microsoft.KubernetesConfiguration/extensions/operations/read",
    "Microsoft.Authorization/*/read",
    "Microsoft.Resources/subscriptions/resourceGroups/read",
    "Microsoft.Resources/subscriptions/read",
    "Microsoft.Management/managementGroups/read",
    "Microsoft.Resources/deployments/*",
    "Microsoft.Support/*",
    "Microsoft.Authorization/roleAssignments/write",
    "Microsoft.Authorization/roleAssignments/delete"
  ],
  "NotActions": [],
  "DataActions": [],
  "NotDataActions": [],
  "AssignableScopes": [
    "/"
  ],
  "Condition": null,
  "ConditionVersion": null
}

New cmdlet output (correct):

> Get-AzRoleDefinition -Id '95dd08a6-00bd-4661-84bf-f6726f83a4d0' | ConvertTo-Json -Depth 10
{
  "Name": "Azure Container Storage Contributor",
  "Id": "95dd08a6-00bd-4661-84bf-f6726f83a4d0",
  "IsCustom": false,
  "Description": "Lets you install Azure Container Storage and manage its storage resources",
  "AssignableScopes": [
    "/"
  ],
  "Permissions": [
    {
      "Actions": [
        "Microsoft.KubernetesConfiguration/extensions/write",
        "Microsoft.KubernetesConfiguration/extensions/read",
        "Microsoft.KubernetesConfiguration/extensions/delete",        
        "Microsoft.KubernetesConfiguration/extensions/operations/read",
        "Microsoft.Authorization/*/read",
        "Microsoft.Resources/subscriptions/resourceGroups/read",      
        "Microsoft.Resources/subscriptions/read",
        "Microsoft.Management/managementGroups/read",
        "Microsoft.Resources/deployments/*",
        "Microsoft.Support/*"
      ],
      "NotActions": [],
      "DataActions": [],
      "NotDataActions": [],
      "Condition": null,
      "ConditionVersion": null
    },
    {
      "Actions": [
        "Microsoft.Authorization/roleAssignments/write",
        "Microsoft.Authorization/roleAssignments/delete"
      ],
      "NotActions": [],
      "DataActions": [],
      "NotDataActions": [],
      "Condition": "((!(ActionMatches{'Microsoft.Authorization/roleAssignments/write'})) OR (@Request[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals{08d4c71acc634ce4a9c85dd251b4d619})) AND ((!(ActionMatches{'Microsoft.Authorization/roleAssignments/delete'})) OR (@Resource[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals{08d4c71acc634ce4a9c85dd251b4d619}))",
      "ConditionVersion": "2.0"
    }
  ]
}

The Fix

  • Preserve the full Permissions structure from the API
  • Add Condition/ConditionVersion to PSPermission
  • Remove the flattened properties that were losing per-permission data

Breaking Change Note

BREAKING CHANGE - Output structure and input format changed for all role definition cmdlets

While this is a bug fix, it does change the output/input structure. Users must update scripts:

Get-AzRoleDefinition output:

- $role.Actions
- $role.NotActions
- $role.DataActions
- $role.NotDataActions
- $role.Condition
- $role.ConditionVersion
+ $role.Permissions[0].Actions
+ $role.Permissions[0].NotActions
+ $role.Permissions[0].DataActions
+ $role.Permissions[0].NotDataActions
+ $role.Permissions[0].Condition
+ $role.Permissions[0].ConditionVersion

New-AzRoleDefinition -InputFile JSON format:

 {
   "Name": "My Custom Role",
   "Description": "Custom role description",
-  "Actions": ["Microsoft.Storage/storageAccounts/read"],
-  "NotActions": [],
-  "DataActions": ["Microsoft.Storage/.../blobs/read"],
-  "NotDataActions": [],
+  "Permissions": [
+    {
+      "Actions": ["Microsoft.Storage/storageAccounts/read"],
+      "NotActions": [],
+      "DataActions": ["Microsoft.Storage/.../blobs/read"],
+      "NotDataActions": [],
+      "Condition": null,
+      "ConditionVersion": null
+    }
+  ],
   "AssignableScopes": ["/subscriptions/{subscriptionId}"]
 }

Why this is necessary: The old flattened properties were discarding data from the API response. This change exposes the correct data that was previously being lost.

Affected Cmdlets

Cmdlet Impact
Get-AzRoleDefinition Output type PSRoleDefinition no longer has Actions, NotActions, DataActions, NotDataActions, Condition, ConditionVersion properties. Use Permissions[n].Actions etc. instead.
New-AzRoleDefinition -InputFile JSON format changed. Must use Permissions array instead of flattened Actions/NotActions/DataActions/NotDataActions properties.
Set-AzRoleDefinition -InputFile JSON format changed. Must use Permissions array. -Role parameter now expects PSRoleDefinition with Permissions property.
Remove-AzRoleDefinition Output type PSRoleDefinition structure changed (same as Get-AzRoleDefinition).

Removed Properties from PSRoleDefinition

The following properties have been removed from the PSRoleDefinition output type:

  • Actions → Use Permissions[0].Actions
  • NotActions → Use Permissions[0].NotActions
  • DataActions → Use Permissions[0].DataActions
  • NotDataActions → Use Permissions[0].NotDataActions
  • Condition → Use Permissions[n].Condition (now per-permission!)
  • ConditionVersion → Use Permissions[n].ConditionVersion (now per-permission!)

Migration Examples

Reading role definition properties:

  $role = Get-AzRoleDefinition -Name "Contributor"
  
  # Accessing actions
- $actions = $role.Actions
+ $actions = $role.Permissions[0].Actions
  
  # Accessing conditions (now correctly available per-permission)
- $condition = $role.Condition  # Was always null for multi-permission roles!
+ $condition = $role.Permissions[1].Condition  # Now correctly returns the condition

Creating a custom role with -InputFile:

 {
   "Name": "My Custom Role",
   "Description": "Custom role description",
-  "Actions": ["Microsoft.Storage/storageAccounts/read"],
-  "NotActions": [],
-  "DataActions": ["Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read"],
-  "NotDataActions": [],
+  "Permissions": [
+    {
+      "Actions": ["Microsoft.Storage/storageAccounts/read"],
+      "NotActions": [],
+      "DataActions": ["Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read"],
+      "NotDataActions": [],
+      "Condition": null,
+      "ConditionVersion": null
+    }
+  ],
   "AssignableScopes": ["/subscriptions/{subscriptionId}"]
 }

Updating a role definition:

  $role = Get-AzRoleDefinition -Name "My Custom Role"
  
  # Adding an action
- $role.Actions.Add("Microsoft.Storage/storageAccounts/write")
+ $role.Permissions[0].Actions.Add("Microsoft.Storage/storageAccounts/write")
  
  Set-AzRoleDefinition -Role $role

Why This Breaking Change Is Necessary

The old flattened properties were discarding data from the API response:

  1. Data Loss: Roles with multiple permission entries had their Condition values lost because only permissions[0].Condition was read
  2. Incorrect Data: Users received null for Condition even when the role had conditions defined
  3. API Mismatch: The cmdlet output did not match the actual Azure API response structure

This change exposes the correct data that was previously being lost.

Mandatory Checklist

  • SHOULD update ChangeLog.md file(s) appropriately
    • Update src/{{SERVICE}}/{{SERVICE}}/ChangeLog.md.
      • A snippet outlining the change(s) made in the PR should be written under the ## Upcoming Release header in the past tense.
    • Should not change ChangeLog.md if no new release is required, such as fixing test case only.
  • SHOULD regenerate markdown help files if there is cmdlet API change. Instruction
  • SHOULD have proper test coverage for changes in pull request.
  • SHOULD NOT adjust version of module manually in pull request

Copilot AI review requested due to automatic review settings January 14, 2026 21:57
@azure-client-tools-bot-prd
Copy link

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

@microsoft-github-policy-service
Copy link
Contributor

Thank you for your contribution @atomassi! We will review the pull request and get back to you soon.

Copy link
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

This PR fixes a bug in Get-AzRoleDefinition where role definitions with conditions on non-first permission entries were returning incorrect data (null Condition). The fix restructures the output model to preserve the full permissions array from the Azure API instead of flattening it.

Changes:

  • Restructured PSRoleDefinition to use a Permissions list instead of flattened Actions/Condition properties
  • Added Condition and ConditionVersion properties to PSPermission to preserve per-permission conditions
  • Updated all conversion and validation logic to work with the new structure
  • Added 18 comprehensive unit tests to verify the conversion behavior
  • Updated scenario tests and JSON fixtures to use the new Permissions format

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Resources/Resources/Models.Authorization/PSRoleDefinition.cs Removed flattened properties (Actions, NotActions, DataActions, NotDataActions, Condition, ConditionVersion) and replaced with Permissions list
src/Resources/Resources/Models.Authorization/PSPermission.cs Added Condition and ConditionVersion properties to preserve per-permission ABAC conditions
src/Resources/Resources/Models.Authorization/AuthorizationClientExtensions.cs Updated ToPSRoleDefinition conversion to preserve full Permissions structure instead of flattening
src/Resources/Resources/Models.Authorization/AuthorizationClient.cs Updated ToRoleDefinitionPermissions and ValidateRoleDefinition to work with Permissions list
src/Resources/Resources/ChangeLog.md Documented the breaking changes with migration guidance
src/Resources/Resources.Test/UnitTests/Authorization/AuthorizationClientExtensionsTests.cs Added comprehensive unit tests (18 tests) covering conversion scenarios including multi-permission roles with conditions
src/Resources/Resources.Test/ScenarioTests/RoleDefinitionTests.ps1 Updated scenario tests to use new Permissions[n].Actions pattern instead of direct Actions property
src/Resources/Resources.Test/Resources/RoleDefinition.json Updated JSON fixture to use Permissions array format
src/Resources/Resources.Test/Resources/NewRoleDefinition.json Updated JSON fixture to use Permissions array format
src/Resources/Resources.Test/Resources/InvalidRoleDefinition.json Updated JSON fixture to use Permissions array format
src/Resources/Resources.Test/Resources/DataActionsRoleDefinition.json Updated JSON fixture to use Permissions array format

Copilot AI review requested due to automatic review settings January 14, 2026 22:51
Copy link
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 11 out of 11 changed files in this pull request and generated 6 comments.

Copilot AI review requested due to automatic review settings January 15, 2026 09:47
Copy link
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 14 out of 14 changed files in this pull request and generated 1 comment.

Copilot AI review requested due to automatic review settings January 15, 2026 11:39
@atomassi atomassi marked this pull request as ready for review January 15, 2026 11:47
@atomassi atomassi changed the title [WIP] [Az.Resources] Fix Get-AzRoleDefinition returning null Condition for multi-permission roles [Az.Resources] Fix Get-AzRoleDefinition returning null Condition for multi-permission roles Jan 15, 2026
Copy link
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 21 out of 22 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • src/Resources/Resources/Properties/Resources.Designer.cs: Language not supported

- Update Get-AzRoleDefinition.md with Permissions property examples
- Update New-AzRoleDefinition.md with Permissions array format and JSON sample
- Update Set-AzRoleDefinition.md with Permissions structure and API limitation note
- Update Remove-AzRoleDefinition.md with Permissions output description
- Update ChangeLog.md with clearer issue reference formatting
@isra-fel
Copy link
Member

/azp run

@azure-pipelines
Copy link
Contributor

Azure Pipelines successfully started running 3 pipeline(s).

@atomassi atomassi added Resource Authorization AzRole* in Az.Resources Contains Breaking Change This PR contains breaking change labels Jan 15, 2026
@github-actions
Copy link

To the author of the pull request,
This PR was labeled "Contains Breaking Change" because breaking changes have been detected by the static analysis pipeline.

  • According to our policy, breaking changes can only take place during major release and they must be preannounced.
  • Please follow our guide on the detailed steps.
  • Required: Please fill in the task below to facilitate our contact,you will receive notifications related to breaking changes.

Copilot AI review requested due to automatic review settings January 15, 2026 14:07
@isra-fel
Copy link
Member

/azp run

@azure-pipelines
Copy link
Contributor

Azure Pipelines successfully started running 3 pipeline(s).

Copy link
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 23 out of 24 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • src/Resources/Resources/Properties/Resources.Designer.cs: Language not supported

@isra-fel
Copy link
Member

/azp run

@azure-pipelines
Copy link
Contributor

Azure Pipelines successfully started running 3 pipeline(s).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Contains Breaking Change This PR contains breaking change Resource Authorization AzRole* in Az.Resources

Projects

None yet

3 participants