Skip to content

Commit

Permalink
Role assignment support (#42297)
Browse files Browse the repository at this point in the history
* save

* add roleassignment

* support role assignment

* revert sln change

* updat tests

* fix

* pr fb

* regen

* Add back env var support for subs, PR fb

* regen

* fix tests

* regen
  • Loading branch information
JoshLove-msft authored Feb 29, 2024
1 parent 983d180 commit 3a83b56
Show file tree
Hide file tree
Showing 41 changed files with 569 additions and 212 deletions.
1 change: 1 addition & 0 deletions eng/Packages.Data.props
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
<PackageReference Update="Azure.ResourceManager" Version="1.11.0-alpha.20240222.6" />
<PackageReference Update="Azure.ResourceManager.AppConfiguration" Version="1.3.0-alpha.20240222.1" />
<PackageReference Update="Azure.ResourceManager.AppService" Version="1.1.0-alpha.20240222.2" />
<PackageReference Update="Azure.ResourceManager.Authorization" Version="1.2.0-alpha.20240227.2" />
<PackageReference Update="Azure.ResourceManager.KeyVault" Version="1.3.0-alpha.20240222.2" />
<PackageReference Update="Azure.ResourceManager.Resources" Version="1.8.0-alpha.20240222.2" />
<PackageReference Update="Azure.ResourceManager.Sql" Version="1.3.0-alpha.20240222.1" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public static partial class CdkExtensions
public partial class Configuration
{
public Configuration() { }
public bool UsePromptMode { get { throw null; } set { } }
public bool UseInteractiveMode { get { throw null; } set { } }
}
public abstract partial class Construct : Azure.Provisioning.IConstruct
{
Expand Down Expand Up @@ -92,6 +92,8 @@ 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 bool NeedsParent() { throw null; }
protected virtual bool NeedsScope() { throw null; }
Azure.Provisioning.Resource System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Resource>.Create(System.BinaryData data, System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; }
string System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Resource>.GetFormatFromOptions(System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; }
System.BinaryData System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Resource>.Write(System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; }
Expand Down Expand Up @@ -151,6 +153,36 @@ public enum WebSiteRuntime
Dotnetcore = 1,
}
}
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 partial class RoleAssignment : Azure.Provisioning.Resource<Azure.ResourceManager.Authorization.RoleAssignmentData>
{
internal RoleAssignment() : base (default(Azure.Provisioning.IConstruct), default(Azure.Provisioning.Resource), default(string), default(Azure.Core.ResourceType), default(string), default(System.Func<string, Azure.ResourceManager.Authorization.RoleAssignmentData>)) { }
protected override bool NeedsParent() { throw null; }
protected override bool NeedsScope() { throw null; }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct RoleDefinition : System.IEquatable<Azure.Provisioning.Authorization.RoleDefinition>
{
private readonly object _dummy;
private readonly int _dummyPrimitive;
public RoleDefinition(string value) { throw null; }
public static Azure.Provisioning.Authorization.RoleDefinition StorageBlobDataContributor { get { throw null; } }
public static Azure.Provisioning.Authorization.RoleDefinition StorageQueueDataContributor { get { throw null; } }
public static Azure.Provisioning.Authorization.RoleDefinition StorageTableDataContributor { get { throw null; } }
public bool Equals(Azure.Provisioning.Authorization.RoleDefinition other) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public override bool Equals(object? obj) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public override int GetHashCode() { throw null; }
public static implicit operator Azure.Provisioning.Authorization.RoleDefinition (string value) { throw null; }
public override string ToString() { throw null; }
}
}
namespace Azure.Provisioning.KeyVaults
{
public partial class KeyVault : Azure.Provisioning.Resource<Azure.ResourceManager.KeyVault.KeyVaultData>
Expand Down Expand Up @@ -193,7 +225,7 @@ public partial class Subscription : Azure.Provisioning.Resource<Azure.ResourceMa
public partial class Tenant : Azure.Provisioning.Resource<Azure.ResourceManager.Resources.TenantData>
{
public Tenant(Azure.Provisioning.IConstruct scope, System.Guid? tenantId = default(System.Guid?)) : base (default(Azure.Provisioning.IConstruct), default(Azure.Provisioning.Resource), default(string), default(Azure.Core.ResourceType), default(string), default(System.Func<string, Azure.ResourceManager.Resources.TenantData>)) { }
protected override string GetAzureName(Azure.Provisioning.IConstruct scope, string resourceName) { throw null; }
protected override string GetAzureName(Azure.Provisioning.IConstruct scope, string? resourceName) { throw null; }
}
}
namespace Azure.Provisioning.Resources
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public static partial class CdkExtensions
public partial class Configuration
{
public Configuration() { }
public bool UsePromptMode { get { throw null; } set { } }
public bool UseInteractiveMode { get { throw null; } set { } }
}
public abstract partial class Construct : Azure.Provisioning.IConstruct
{
Expand Down Expand Up @@ -92,6 +92,8 @@ 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 bool NeedsParent() { throw null; }
protected virtual bool NeedsScope() { throw null; }
Azure.Provisioning.Resource System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Resource>.Create(System.BinaryData data, System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; }
string System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Resource>.GetFormatFromOptions(System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; }
System.BinaryData System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Resource>.Write(System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; }
Expand Down Expand Up @@ -151,6 +153,36 @@ public enum WebSiteRuntime
Dotnetcore = 1,
}
}
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 partial class RoleAssignment : Azure.Provisioning.Resource<Azure.ResourceManager.Authorization.RoleAssignmentData>
{
internal RoleAssignment() : base (default(Azure.Provisioning.IConstruct), default(Azure.Provisioning.Resource), default(string), default(Azure.Core.ResourceType), default(string), default(System.Func<string, Azure.ResourceManager.Authorization.RoleAssignmentData>)) { }
protected override bool NeedsParent() { throw null; }
protected override bool NeedsScope() { throw null; }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct RoleDefinition : System.IEquatable<Azure.Provisioning.Authorization.RoleDefinition>
{
private readonly object _dummy;
private readonly int _dummyPrimitive;
public RoleDefinition(string value) { throw null; }
public static Azure.Provisioning.Authorization.RoleDefinition StorageBlobDataContributor { get { throw null; } }
public static Azure.Provisioning.Authorization.RoleDefinition StorageQueueDataContributor { get { throw null; } }
public static Azure.Provisioning.Authorization.RoleDefinition StorageTableDataContributor { get { throw null; } }
public bool Equals(Azure.Provisioning.Authorization.RoleDefinition other) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public override bool Equals(object? obj) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public override int GetHashCode() { throw null; }
public static implicit operator Azure.Provisioning.Authorization.RoleDefinition (string value) { throw null; }
public override string ToString() { throw null; }
}
}
namespace Azure.Provisioning.KeyVaults
{
public partial class KeyVault : Azure.Provisioning.Resource<Azure.ResourceManager.KeyVault.KeyVaultData>
Expand Down Expand Up @@ -193,7 +225,7 @@ public partial class Subscription : Azure.Provisioning.Resource<Azure.ResourceMa
public partial class Tenant : Azure.Provisioning.Resource<Azure.ResourceManager.Resources.TenantData>
{
public Tenant(Azure.Provisioning.IConstruct scope, System.Guid? tenantId = default(System.Guid?)) : base (default(Azure.Provisioning.IConstruct), default(Azure.Provisioning.Resource), default(string), default(Azure.Core.ResourceType), default(string), default(System.Func<string, Azure.ResourceManager.Resources.TenantData>)) { }
protected override string GetAzureName(Azure.Provisioning.IConstruct scope, string resourceName) { throw null; }
protected override string GetAzureName(Azure.Provisioning.IConstruct scope, string? resourceName) { throw null; }
}
}
namespace Azure.Provisioning.Resources
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<PackageReference Include="Azure.ResourceManager.AppService" />
<PackageReference Include="Azure.ResourceManager.Storage" />
<PackageReference Include="Azure.ResourceManager.AppConfiguration" />
<PackageReference Include="Azure.ResourceManager.Authorization" />
<PackageReference Include="System.ClientModel" />
</ItemGroup>

Expand Down
2 changes: 1 addition & 1 deletion sdk/provisioning/Azure.Provisioning/src/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ public class Configuration
/// <summary>
/// Whether to use prompt mode.
/// </summary>
public bool UsePromptMode { get; set; }
public bool UseInteractiveMode { get; set; }
}
}
7 changes: 1 addition & 6 deletions sdk/provisioning/Azure.Provisioning/src/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.IO;
using System.Text;
using Azure.Provisioning.Authorization;
using Azure.Provisioning.ResourceManager;
using Azure.Provisioning.Resources;

Expand All @@ -20,12 +21,6 @@ public static string ToCamelCase(this string str)
#endif
}

public static bool IsChildResource(this Resource resource)
{
//TODO: this is a bit of a hack. We should probably have a better way to determine if a resource is a child resource
return resource is DeploymentScript || (resource.Parent is not null && resource.Parent is not ResourceGroup && resource.Parent is not Subscription);
}

public static void Write(this MemoryStream stream, string value)
{
var bytes = Encoding.UTF8.GetBytes(value);
Expand Down
2 changes: 1 addition & 1 deletion sdk/provisioning/Azure.Provisioning/src/Parameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public Parameter(string name, string? description = default, object? defaultValu
/// <param name="defaultValue">The parameter defaultValue.</param>
/// <param name="isSecure">Is the parameter secure.</param>
/// <param name="isExpression">Is the parameter an expression.</param>
internal Parameter(string name, string? description = default, object? defaultValue = default, bool isSecure = false, bool isExpression = false)
internal Parameter(string name, string? description, object? defaultValue = default, bool isSecure = false, bool isExpression = false)
: this (name, description, defaultValue, isSecure)
{
IsExpression = isExpression;
Expand Down
19 changes: 17 additions & 2 deletions sdk/provisioning/Azure.Provisioning/src/Resource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
using System.Security.Cryptography;
using System.Text;
using Azure.Core;
using Azure.Provisioning.Authorization;
using Azure.Provisioning.ResourceManager;
using Azure.Provisioning.Resources;
using Azure.ResourceManager;
using Azure.ResourceManager.Authorization.Models;
using Azure.ResourceManager.Models;

namespace Azure.Provisioning
Expand Down Expand Up @@ -246,7 +248,7 @@ private BinaryData SerializeModule(ModelReaderWriterOptions options)

stream.WriteLine($"resource {Name} '{ResourceType}@{Version}' = {{");

if (this.IsChildResource() && this is not DeploymentScript && this is not Subscription)
if (NeedsParent())
{
stream.WriteLine($" parent: {Parent!.Name}");
}
Expand Down Expand Up @@ -298,7 +300,20 @@ private BinaryData SerializeModule(ModelReaderWriterOptions options)
return new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position));
}

private bool NeedsScope()
/// <summary>
/// Determines whether the resource needs a parent declaration.
/// </summary>
/// <returns>Whether the resource needs a parent.</returns>
protected virtual bool NeedsParent()
{
return this is not Subscription && Parent is not null && Parent is not (ResourceGroup or Subscription);
}

/// <summary>
/// Determines whether the resource needs a scope declaration.
/// </summary>
/// <returns>Whether the resource needs a scope.</returns>
protected virtual bool NeedsScope()
{
Debug.Assert(ModuleScope != null, "ModuleScope should not be null");

Expand Down
4 changes: 2 additions & 2 deletions sdk/provisioning/Azure.Provisioning/src/ResourceOfT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ protected Resource(
Properties = (T)ResourceData;

// Resources that have a non-RG parent do not require a location value
if (scope.Configuration?.UsePromptMode == true && Parent is ResourceGroup)
if (scope.Configuration?.UseInteractiveMode == true && Parent is ResourceGroup)
{
// We can't use the lambda overload because not all of the T's will inherit from TrackedResourceData
// TODO we may need to add a protected LocationSelector property in the future if there are exceptions to the rule
AssignParameter(Properties, "Location", new Parameter("location", defaultValue: $"{ResourceGroup.AnonymousResourceGroupName}.location", isExpression: true));
AssignParameter(Properties, "Location", new Parameter("location", null, defaultValue: $"{ResourceGroup.AnonymousResourceGroupName}.location", isExpression: true));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;

namespace Azure.Provisioning.Authorization
{
/// <summary>
/// Extension methods for authorization.
/// </summary>
public static class AuthorizationExtensions
{
/// <summary>
/// Assigns a role to the resource.
/// </summary>
/// <param name="resource">The resource.</param>
/// <param name="roleDefinition">The role definition.</param>
/// <param name="principalId">The principal ID.</param>
public static RoleAssignment AssignRole(this Resource resource, RoleDefinition roleDefinition, Guid? principalId = default)
{
return new RoleAssignment(resource, roleDefinition, principalId);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using Azure.Core;
using Azure.ResourceManager.Authorization;
using Azure.ResourceManager.Authorization.Models;

namespace Azure.Provisioning.Authorization
{
/// <summary>
/// Role assignment resource.
/// </summary>
public class RoleAssignment : Resource<RoleAssignmentData>
{
private static readonly ResourceType ResourceType = "Microsoft.Resources/roleAssignments";
private static readonly ResourceType RoleDefinitionResourceType = "Microsoft.Authorization/roleDefinitions";

private const string SubscriptionResourceIdFunction = "subscriptionResourceId";

internal RoleAssignment(
Resource resource,
RoleDefinition roleDefinition,
Guid? principalId = default)
: base(
resource.Scope,
resource,
resource.Name,
ResourceType,
"2022-04-01",
(name) => ArmAuthorizationModelFactory.RoleAssignmentData(
name: name,
principalId: principalId))
{
if (resource.Scope.Configuration?.UseInteractiveMode != true && principalId == null)
{
throw new InvalidOperationException("PrincipalId must be specified when not in interactive mode.");
}

if (principalId == null)
{
AssignParameter(data => data.PrincipalId, new Parameter("principalId"));
}

AssignProperty(
data => data.Name,
$"guid('{resource.Name}', {(principalId == null ? "principalId" : "'" + principalId + "'")}," +
$" {SubscriptionResourceIdFunction}({(resource.Scope.Configuration?.UseInteractiveMode != true ? "'" + Id.SubscriptionId + "', ": string.Empty)}" +
$"'{RoleDefinitionResourceType}', '{roleDefinition}'))");

AssignProperty(
data => data.RoleDefinitionId,
$"{SubscriptionResourceIdFunction}({(resource.Scope.Configuration?.UseInteractiveMode != true ? "'"+ Id.SubscriptionId + "', ": string.Empty)}" +
$"'{RoleDefinitionResourceType}', '{roleDefinition}')");
}

/// <inheritdoc />
protected override bool NeedsScope() => true;

/// <inheritdoc />
protected override bool NeedsParent() => false;
}
}
Loading

0 comments on commit 3a83b56

Please sign in to comment.