From e6f2d0dc3ccb901e302e12133904602f5ffcecc4 Mon Sep 17 00:00:00 2001 From: jolov Date: Fri, 1 Mar 2024 12:15:35 -0800 Subject: [PATCH 1/2] Ensure unique bicep names for role assignments and expose principalType parameter --- .../api/Azure.Provisioning.net6.0.cs | 4 +++- .../api/Azure.Provisioning.netstandard2.0.cs | 4 +++- .../Azure.Provisioning/src/Resource.cs | 12 +++++++--- .../authorization/AuthorizationExtensions.cs | 10 ++++++-- .../src/authorization/RoleAssignment.cs | 22 ++++++++++++++---- .../RoleAssignmentWithParameter/main.bicep | 23 ++++++++++++++++++- .../rg_TEST_module/rg_TEST_module.bicep | 23 ++++++++++++++++++- .../main.bicep | 23 ++++++++++++++++++- .../tests/ProvisioningTests.cs | 7 ++++++ 9 files changed, 113 insertions(+), 15 deletions(-) diff --git a/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.net6.0.cs b/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.net6.0.cs index 64323fef1c662..85389c2d48ed1 100644 --- a/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.net6.0.cs +++ b/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.net6.0.cs @@ -92,6 +92,7 @@ protected Resource(Azure.Provisioning.IConstruct scope, Azure.Provisioning.Resou public string Version { get { throw null; } } protected virtual Azure.Provisioning.Resource? FindParentInScope(Azure.Provisioning.IConstruct scope) { throw null; } protected virtual string GetAzureName(Azure.Provisioning.IConstruct scope, string resourceName) { throw null; } + protected virtual string GetBicepName(Azure.Provisioning.Resource resource) { throw null; } protected virtual bool NeedsParent() { throw null; } protected virtual bool NeedsScope() { throw null; } Azure.Provisioning.Resource System.ClientModel.Primitives.IPersistableModel.Create(System.BinaryData data, System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; } @@ -157,11 +158,12 @@ namespace Azure.Provisioning.Authorization { public static partial class AuthorizationExtensions { - public static Azure.Provisioning.Authorization.RoleAssignment AssignRole(this Azure.Provisioning.Resource resource, Azure.Provisioning.Authorization.RoleDefinition roleDefinition, System.Guid? principalId = default(System.Guid?)) { throw null; } + public static Azure.Provisioning.Authorization.RoleAssignment AssignRole(this Azure.Provisioning.Resource resource, Azure.Provisioning.Authorization.RoleDefinition roleDefinition, System.Guid? principalId = default(System.Guid?), Azure.ResourceManager.Authorization.Models.RoleManagementPrincipalType? principalType = default(Azure.ResourceManager.Authorization.Models.RoleManagementPrincipalType?)) { throw null; } } public partial class RoleAssignment : Azure.Provisioning.Resource { internal RoleAssignment() : base (default(Azure.Provisioning.IConstruct), default(Azure.Provisioning.Resource), default(string), default(Azure.Core.ResourceType), default(string), default(System.Func)) { } + protected override string GetBicepName(Azure.Provisioning.Resource resource) { throw null; } protected override bool NeedsParent() { throw null; } protected override bool NeedsScope() { throw null; } } diff --git a/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.netstandard2.0.cs b/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.netstandard2.0.cs index 64323fef1c662..85389c2d48ed1 100644 --- a/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.netstandard2.0.cs +++ b/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.netstandard2.0.cs @@ -92,6 +92,7 @@ protected Resource(Azure.Provisioning.IConstruct scope, Azure.Provisioning.Resou public string Version { get { throw null; } } protected virtual Azure.Provisioning.Resource? FindParentInScope(Azure.Provisioning.IConstruct scope) { throw null; } protected virtual string GetAzureName(Azure.Provisioning.IConstruct scope, string resourceName) { throw null; } + protected virtual string GetBicepName(Azure.Provisioning.Resource resource) { throw null; } protected virtual bool NeedsParent() { throw null; } protected virtual bool NeedsScope() { throw null; } Azure.Provisioning.Resource System.ClientModel.Primitives.IPersistableModel.Create(System.BinaryData data, System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; } @@ -157,11 +158,12 @@ namespace Azure.Provisioning.Authorization { public static partial class AuthorizationExtensions { - public static Azure.Provisioning.Authorization.RoleAssignment AssignRole(this Azure.Provisioning.Resource resource, Azure.Provisioning.Authorization.RoleDefinition roleDefinition, System.Guid? principalId = default(System.Guid?)) { throw null; } + public static Azure.Provisioning.Authorization.RoleAssignment AssignRole(this Azure.Provisioning.Resource resource, Azure.Provisioning.Authorization.RoleDefinition roleDefinition, System.Guid? principalId = default(System.Guid?), Azure.ResourceManager.Authorization.Models.RoleManagementPrincipalType? principalType = default(Azure.ResourceManager.Authorization.Models.RoleManagementPrincipalType?)) { throw null; } } public partial class RoleAssignment : Azure.Provisioning.Resource { internal RoleAssignment() : base (default(Azure.Provisioning.IConstruct), default(Azure.Provisioning.Resource), default(string), default(Azure.Core.ResourceType), default(string), default(System.Func)) { } + protected override string GetBicepName(Azure.Provisioning.Resource resource) { throw null; } protected override bool NeedsParent() { throw null; } protected override bool NeedsScope() { throw null; } } diff --git a/sdk/provisioning/Azure.Provisioning/src/Resource.cs b/sdk/provisioning/Azure.Provisioning/src/Resource.cs index 0cf18028d4ca8..567077af3268f 100644 --- a/sdk/provisioning/Azure.Provisioning/src/Resource.cs +++ b/sdk/provisioning/Azure.Provisioning/src/Resource.cs @@ -389,13 +389,19 @@ private string GetAlphaNumeric(string base64Hash, int chars) return sb.ToString(); } - private static string GetScopedName(Resource resource, string scopedName) + private string GetScopedName(Resource resource, string scopedName) { Resource? parent = resource.Parent; - - return parent is null || parent is Tenant ? scopedName : GetScopedName(parent, $"{parent.Id.Name}_{scopedName}"); + return parent is null || parent is Tenant ? scopedName : GetScopedName(parent, $"{GetBicepName(parent)}_{scopedName}"); } + /// + /// Gets the name of the resource for Bicep. + /// + /// The resource. + /// The name to use for Bicep + protected virtual string GetBicepName(Resource resource) => resource.Id.Name; + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "bicep"; } } diff --git a/sdk/provisioning/Azure.Provisioning/src/authorization/AuthorizationExtensions.cs b/sdk/provisioning/Azure.Provisioning/src/authorization/AuthorizationExtensions.cs index b6e481343adb5..0ed5102c3231e 100644 --- a/sdk/provisioning/Azure.Provisioning/src/authorization/AuthorizationExtensions.cs +++ b/sdk/provisioning/Azure.Provisioning/src/authorization/AuthorizationExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using Azure.ResourceManager.Authorization.Models; namespace Azure.Provisioning.Authorization { @@ -16,9 +17,14 @@ public static class AuthorizationExtensions /// The resource. /// The role definition. /// The principal ID. - public static RoleAssignment AssignRole(this Resource resource, RoleDefinition roleDefinition, Guid? principalId = default) + /// The principal type + public static RoleAssignment AssignRole( + this Resource resource, + RoleDefinition roleDefinition, + Guid? principalId = default, + RoleManagementPrincipalType? principalType = default) { - return new RoleAssignment(resource, roleDefinition, principalId); + return new RoleAssignment(resource, roleDefinition, principalId, principalType); } } } diff --git a/sdk/provisioning/Azure.Provisioning/src/authorization/RoleAssignment.cs b/sdk/provisioning/Azure.Provisioning/src/authorization/RoleAssignment.cs index 525fbb81d3d80..f38c42f750f72 100644 --- a/sdk/provisioning/Azure.Provisioning/src/authorization/RoleAssignment.cs +++ b/sdk/provisioning/Azure.Provisioning/src/authorization/RoleAssignment.cs @@ -21,16 +21,20 @@ public class RoleAssignment : Resource internal RoleAssignment( Resource resource, RoleDefinition roleDefinition, - Guid? principalId = default) + Guid? principalId = default, + RoleManagementPrincipalType? principalType = default) : base( resource.Scope, resource, + // will be overriden resource.Name, ResourceType, "2022-04-01", (name) => ArmAuthorizationModelFactory.RoleAssignmentData( name: name, - principalId: principalId)) + principalId: principalId, + roleDefinitionId: ResourceIdentifier.Parse($"/providers/{RoleDefinitionResourceType}/{roleDefinition}"), + principalType: principalType ?? RoleManagementPrincipalType.ServicePrincipal)) { if (resource.Scope.Configuration?.UseInteractiveMode != true && principalId == null) { @@ -44,9 +48,7 @@ internal RoleAssignment( AssignProperty( data => data.Name, - $"guid('{resource.Name}', {(principalId == null ? "principalId" : "'" + principalId + "'")}," + - $" {SubscriptionResourceIdFunction}({(resource.Scope.Configuration?.UseInteractiveMode != true ? "'" + Id.SubscriptionId + "', ": string.Empty)}" + - $"'{RoleDefinitionResourceType}', '{roleDefinition}'))"); + GetBicepName(resource)); AssignProperty( data => data.RoleDefinitionId, @@ -54,6 +56,16 @@ internal RoleAssignment( $"'{RoleDefinitionResourceType}', '{roleDefinition}')"); } + /// + protected override string GetBicepName(Resource resource) + { + var data = (RoleAssignmentData)ResourceData; + return + $"guid('{resource.Name}', {(data.PrincipalId == null ? "principalId" : "'" + data.PrincipalId + "'")}," + + $" {SubscriptionResourceIdFunction}({(resource.Scope.Configuration?.UseInteractiveMode != true ? "'" + Id.SubscriptionId + "', " : string.Empty)}" + + $"'{RoleDefinitionResourceType}', '{data.RoleDefinitionId.Name}'))"; + } + /// protected override bool NeedsScope() => true; diff --git a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/RoleAssignmentWithParameter/main.bicep b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/RoleAssignmentWithParameter/main.bicep index 1ef583594d484..d9c79ada0a00c 100644 --- a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/RoleAssignmentWithParameter/main.bicep +++ b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/RoleAssignmentWithParameter/main.bicep @@ -25,11 +25,32 @@ resource blobService_lnEDXlX5c 'Microsoft.Storage/storageAccounts/blobServices@2 } } -resource roleAssignment_ZBWGKDk4O 'Microsoft.Authorization/roleAssignments@2022-04-01' = { +resource roleAssignment_MDGXC4QYi 'Microsoft.Authorization/roleAssignments@2022-04-01' = { scope: storageAccount_YRiDhR43q name: guid('storageAccount_YRiDhR43q', principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')) properties: { roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') principalId: principalId + principalType: 'ServicePrincipal' + } +} + +resource roleAssignment_4qiHESxvp 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: storageAccount_YRiDhR43q + name: guid('storageAccount_YRiDhR43q', principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')) + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88') + principalId: principalId + principalType: 'ServicePrincipal' + } +} + +resource roleAssignment_eQvFjtjRE 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: storageAccount_YRiDhR43q + name: guid('storageAccount_YRiDhR43q', '00000000-0000-0000-0000-000000000000', subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')) + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3') + principalId: '00000000-0000-0000-0000-000000000000' + principalType: 'User' } } diff --git a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/RoleAssignmentWithoutParameter/resources/rg_TEST_module/rg_TEST_module.bicep b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/RoleAssignmentWithoutParameter/resources/rg_TEST_module/rg_TEST_module.bicep index fb0bb12d80a76..d0a408cceac7b 100644 --- a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/RoleAssignmentWithoutParameter/resources/rg_TEST_module/rg_TEST_module.bicep +++ b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/RoleAssignmentWithoutParameter/resources/rg_TEST_module/rg_TEST_module.bicep @@ -17,11 +17,32 @@ resource blobService_NVMDcYVF9 'Microsoft.Storage/storageAccounts/blobServices@2 } } -resource roleAssignment_lDOSGTMrV 'Microsoft.Authorization/roleAssignments@2022-04-01' = { +resource roleAssignment_SI6hy0Shx 'Microsoft.Authorization/roleAssignments@2022-04-01' = { scope: storageAccount_melvnlpF2 name: guid('storageAccount_melvnlpF2', '00000000-0000-0000-0000-000000000000', subscriptionResourceId('00000000-0000-0000-0000-000000000000', 'Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')) properties: { roleDefinitionId: subscriptionResourceId('00000000-0000-0000-0000-000000000000', 'Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') principalId: '00000000-0000-0000-0000-000000000000' + principalType: 'ServicePrincipal' + } +} + +resource roleAssignment_FiYmmG8xx 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: storageAccount_melvnlpF2 + name: guid('storageAccount_melvnlpF2', '00000000-0000-0000-0000-000000000000', subscriptionResourceId('00000000-0000-0000-0000-000000000000', 'Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')) + properties: { + roleDefinitionId: subscriptionResourceId('00000000-0000-0000-0000-000000000000', 'Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88') + principalId: '00000000-0000-0000-0000-000000000000' + principalType: 'ServicePrincipal' + } +} + +resource roleAssignment_cl79LHWP1 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: storageAccount_melvnlpF2 + name: guid('storageAccount_melvnlpF2', '00000000-0000-0000-0000-000000000000', subscriptionResourceId('00000000-0000-0000-0000-000000000000', 'Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')) + properties: { + roleDefinitionId: subscriptionResourceId('00000000-0000-0000-0000-000000000000', 'Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3') + principalId: '00000000-0000-0000-0000-000000000000' + principalType: 'User' } } diff --git a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/RoleAssignmentWithoutParameterInteractiveMode/main.bicep b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/RoleAssignmentWithoutParameterInteractiveMode/main.bicep index 35e3f3ea49b8d..e0252b65c3779 100644 --- a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/RoleAssignmentWithoutParameterInteractiveMode/main.bicep +++ b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/RoleAssignmentWithoutParameterInteractiveMode/main.bicep @@ -22,11 +22,32 @@ resource blobService_lnEDXlX5c 'Microsoft.Storage/storageAccounts/blobServices@2 } } -resource roleAssignment_ZBWGKDk4O 'Microsoft.Authorization/roleAssignments@2022-04-01' = { +resource roleAssignment_S6jH5ugdS 'Microsoft.Authorization/roleAssignments@2022-04-01' = { scope: storageAccount_YRiDhR43q name: guid('storageAccount_YRiDhR43q', '00000000-0000-0000-0000-000000000000', subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')) properties: { roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') principalId: '00000000-0000-0000-0000-000000000000' + principalType: 'ServicePrincipal' + } +} + +resource roleAssignment_7MQXhfyRx 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: storageAccount_YRiDhR43q + name: guid('storageAccount_YRiDhR43q', '00000000-0000-0000-0000-000000000000', subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')) + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88') + principalId: '00000000-0000-0000-0000-000000000000' + principalType: 'ServicePrincipal' + } +} + +resource roleAssignment_eQvFjtjRE 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: storageAccount_YRiDhR43q + name: guid('storageAccount_YRiDhR43q', '00000000-0000-0000-0000-000000000000', subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')) + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3') + principalId: '00000000-0000-0000-0000-000000000000' + principalType: 'User' } } diff --git a/sdk/provisioning/Azure.Provisioning/tests/ProvisioningTests.cs b/sdk/provisioning/Azure.Provisioning/tests/ProvisioningTests.cs index 3f5759edd7f34..2fb726cfcc95c 100644 --- a/sdk/provisioning/Azure.Provisioning/tests/ProvisioningTests.cs +++ b/sdk/provisioning/Azure.Provisioning/tests/ProvisioningTests.cs @@ -20,6 +20,7 @@ using Azure.Provisioning.AppConfiguration; using Azure.Provisioning.Authorization; using Azure.ResourceManager; +using Azure.ResourceManager.Authorization.Models; using Azure.ResourceManager.Resources; using Azure.ResourceManager.Resources.Models; using Azure.ResourceManager.Storage.Models; @@ -217,6 +218,8 @@ public async Task RoleAssignmentWithParameter() var storageAccount = infra.AddStorageAccount(name: "photoAcct", sku: StorageSkuName.PremiumLrs, kind: StorageKind.BlockBlobStorage); infra.AddBlobService(); storageAccount.AssignRole(RoleDefinition.StorageBlobDataContributor); + storageAccount.AssignRole(RoleDefinition.StorageQueueDataContributor); + storageAccount.AssignRole(RoleDefinition.StorageTableDataContributor, Guid.Empty, RoleManagementPrincipalType.User); infra.Build(GetOutputPath()); await ValidateBicepAsync(BinaryData.FromObjectAsJson(new { principalId = new { value = Guid.Empty }}), interactiveMode: true); @@ -229,6 +232,8 @@ public async Task RoleAssignmentWithoutParameter() var storageAccount = infra.AddStorageAccount(name: "photoAcct", sku: StorageSkuName.PremiumLrs, kind: StorageKind.BlockBlobStorage); infra.AddBlobService(); storageAccount.AssignRole(RoleDefinition.StorageBlobDataContributor, Guid.Empty); + storageAccount.AssignRole(RoleDefinition.StorageQueueDataContributor, Guid.Empty); + storageAccount.AssignRole(RoleDefinition.StorageTableDataContributor, Guid.Empty, RoleManagementPrincipalType.User); infra.Build(GetOutputPath()); await ValidateBicepAsync(); @@ -241,6 +246,8 @@ public async Task RoleAssignmentWithoutParameterInteractiveMode() var storageAccount = infra.AddStorageAccount(name: "photoAcct", sku: StorageSkuName.PremiumLrs, kind: StorageKind.BlockBlobStorage); infra.AddBlobService(); storageAccount.AssignRole(RoleDefinition.StorageBlobDataContributor, Guid.Empty); + storageAccount.AssignRole(RoleDefinition.StorageQueueDataContributor, Guid.Empty); + storageAccount.AssignRole(RoleDefinition.StorageTableDataContributor, Guid.Empty, RoleManagementPrincipalType.User); infra.Build(GetOutputPath()); await ValidateBicepAsync(interactiveMode: true); From f27d0cd7118273bfa1ab127a993da1d0df2ba8b6 Mon Sep 17 00:00:00 2001 From: jolov Date: Fri, 1 Mar 2024 12:18:02 -0800 Subject: [PATCH 2/2] ref docs --- .../src/authorization/AuthorizationExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/provisioning/Azure.Provisioning/src/authorization/AuthorizationExtensions.cs b/sdk/provisioning/Azure.Provisioning/src/authorization/AuthorizationExtensions.cs index 0ed5102c3231e..cb4644602f078 100644 --- a/sdk/provisioning/Azure.Provisioning/src/authorization/AuthorizationExtensions.cs +++ b/sdk/provisioning/Azure.Provisioning/src/authorization/AuthorizationExtensions.cs @@ -16,8 +16,8 @@ public static class AuthorizationExtensions /// /// The resource. /// The role definition. - /// The principal ID. - /// The principal type + /// The principal ID. If not specified, a principalId parameter will be added to the resulting bicep module. + /// The principal type. If not specified, ServicePrincipal is used. public static RoleAssignment AssignRole( this Resource resource, RoleDefinition roleDefinition,