Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Role assignment support #42297

Merged
merged 13 commits into from
Feb 29, 2024
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
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
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,55 @@
// 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>
{
internal static readonly ResourceType ResourceType = "Microsoft.Resources/roleAssignments";

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, '{roleDefinition}')");
}
else
{
AssignProperty(data => data.Name, $"guid('{resource.Name}', '{principalId}', '{roleDefinition}')");
}
AssignProperty(data => data.RoleDefinitionId, $"subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '{roleDefinition}')");
}

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

/// <inheritdoc />
protected override bool NeedsParent() => false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.ComponentModel;

namespace Azure.Provisioning.Authorization
{
/// <summary> Role definition. </summary>
public readonly partial struct RoleDefinition : IEquatable<RoleDefinition>
{
private readonly string _value;

/// <summary> Initializes a new instance of <see cref="RoleDefinition"/>. </summary>
/// <exception cref="ArgumentNullException"> <paramref name="value"/> is null. </exception>
public RoleDefinition(string value)
{
_value = value ?? throw new ArgumentNullException(nameof(value));
}

/// <summary>
/// Storage blob data contributor role.
/// </summary>
public static RoleDefinition StorageBlobDataContributor { get; } = new RoleDefinition("ba92f5b4-2d11-453d-a403-e96b0029c9fe");

/// <summary>
/// Storage queue data contributor role.
/// </summary>
public static RoleDefinition StorageQueueDataContributor { get; } = new RoleDefinition("974c5e8b-45b9-4653-ba55-5f855dd0fb88");

/// <summary>
/// Storage table data contributor role.
/// </summary>
public static RoleDefinition StorageTableDataContributor { get; } = new RoleDefinition("0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3");

/// <summary> Converts a string to a <see cref="RoleDefinition"/>. </summary>
public static implicit operator RoleDefinition(string value) => new RoleDefinition(value);

/// <inheritdoc />
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object? obj) => obj is RoleDefinition other && Equals(other);
/// <inheritdoc />
public bool Equals(RoleDefinition other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase);

/// <inheritdoc />
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() => _value?.GetHashCode() ?? 0;
/// <inheritdoc />
public override string ToString() => _value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static ResourceGroup AddResourceGroup(this IConstruct construct)
throw new InvalidOperationException("ResourceGroup already exists on the construct");
}

return new ResourceGroup(construct, name: construct.Configuration?.UsePromptMode == true ? ResourceGroup.AnonymousResourceGroupName : "rg");
return new ResourceGroup(construct, name: construct.Configuration?.UseInteractiveMode == true ? ResourceGroup.AnonymousResourceGroupName : "rg");
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ public StorageAccount(IConstruct scope, StorageKind kind, StorageSkuName sku, Re
sku: new StorageSku(sku),
kind: StorageKind.StorageV2))
{
if (scope.Configuration?.UsePromptMode == true)
{
AssignProperty(data => data.Name, $"toLower(take(concat('{name}', uniqueString(resourceGroup().id)), 24))");
}
AssignProperty(data => data.Name, $"toLower(take(concat('{name}', uniqueString(resourceGroup().id)), 24))");
}

/// <inheritdoc/>
Expand Down
Loading