Skip to content

Commit

Permalink
enable naming rule overrides per resource (#42012)
Browse files Browse the repository at this point in the history
* add name rule override ability in resource

* remove lang override

* update api
  • Loading branch information
m-nash authored Feb 15, 2024
1 parent 8d20bd5 commit fbef263
Show file tree
Hide file tree
Showing 32 changed files with 222 additions and 169 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

55 changes: 47 additions & 8 deletions sdk/provisioning/Azure.Provisioning/src/Resource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal void AddDependency(Resource resource)
/// Gets the parent <see cref="Resource"/>.
/// </summary>
public Resource? Parent { get; }
private object Properties { get; }
private protected object ResourceData { get; }
/// <summary>
/// Gets the version of the resource.
/// </summary>
Expand Down Expand Up @@ -67,25 +67,64 @@ internal void AddDependency(Resource resource)
/// <param name="resourceName">The resource name.</param>
/// <param name="resourceType">The resource type.</param>
/// <param name="version">The resource version.</param>
/// <param name="properties">The resource properties</param>
/// <param name="createProperties">Lambda to create the ARM properties.</param>
/// <exception cref="ArgumentNullException">If <paramref name="scope"/> is null.</exception>
protected Resource(IConstruct scope, Resource? parent, string resourceName, ResourceType resourceType, string version, object properties)
protected Resource(IConstruct scope, Resource? parent, string resourceName, ResourceType resourceType, string version, Func<string, object> createProperties)
{
if (scope is null) throw new ArgumentNullException(nameof(scope));

var azureName = GetAzureName(scope, resourceName);
Scope = scope;
Parameters = new List<Parameter>();
Parent = parent ?? FindParentInScope(scope);
Scope.AddResource(this);
Properties = properties;
ResourceData = createProperties(azureName);
Version = version;
ParameterOverrides = new Dictionary<object, Dictionary<string, string>>();
Dependencies = new List<Resource>();
ResourceType = resourceType;
Id = Parent is null ? ResourceIdentifier.Root : Parent is ResourceGroup ? Parent.Id.AppendProviderResource(ResourceType.Namespace, ResourceType.GetLastType(), resourceName) : Parent.Id.AppendChildResource(ResourceType.GetLastType(), resourceName);
Id = Parent is null
? ResourceIdentifier.Root
: Parent is ResourceGroup
? Parent.Id.AppendProviderResource(ResourceType.Namespace, ResourceType.GetLastType(), azureName)
: Parent.Id.AppendChildResource(ResourceType.GetLastType(), azureName);
Name = GetHash();
}

/// <summary>
/// Validate and sanitize the resource name.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="resourceName">The resource name.</param>
/// <returns>Sanitized resource name.</returns>
/// <exception cref="ArgumentException">If the resource name violates rules that cannot be sanitized.</exception>
protected virtual string GetAzureName(IConstruct scope, string resourceName)
{
var span = resourceName.AsSpan();
if (!char.IsLetter(span[0]))
{
throw new ArgumentException("Resource name must start with a letter", nameof(resourceName));
}
if (!char.IsLetterOrDigit(span[span.Length - 1]))
{
throw new ArgumentException("Resource name must end with a letter or digit", nameof(resourceName));
}
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < span.Length; i++)
{
char c = span[i];
if (!char.IsLetterOrDigit(c) && c != '-')
{
continue;
}
stringBuilder.Append(c);
}

stringBuilder.Append('-');
stringBuilder.Append(scope.EnvironmentName);
return stringBuilder.ToString(0, Math.Min(stringBuilder.Length, 24));
}

/// <summary>
/// Finds the parent resource in the scope.
/// </summary>
Expand Down Expand Up @@ -127,10 +166,10 @@ private protected void AssignParameter(object instance, string propertyName, Par
/// <exception cref="ArgumentException">If the <paramref name="propertyName"/> is not found on the resources properties.</exception>
private protected Output AddOutput(string name, object instance, string propertyName, bool isLiteral = false, bool isSecure = false)
{
string? reference = GetReference(instance.GetType(), Properties.GetType(), propertyName, Name.ToCamelCase());
string? reference = GetReference(instance.GetType(), ResourceData.GetType(), propertyName, Name.ToCamelCase());

if (reference is null)
throw new ArgumentException(nameof(propertyName), $"{propertyName} was not found in the property tree for {Properties.GetType().Name}");
throw new ArgumentException(nameof(propertyName), $"{propertyName} was not found in the property tree for {ResourceData.GetType().Name}");
var result = new Output(name, reference, Scope, isLiteral, isSecure);
Scope.AddOutput(result);
return result;
Expand Down Expand Up @@ -197,7 +236,7 @@ private BinaryData SerializeModule(ModelReaderWriterOptions options)
{
bicepOptions.ParameterOverrides.Add(parameter.Key, parameter.Value);
}
var data = ModelReaderWriter.Write(Properties, bicepOptions).ToMemory();
var data = ModelReaderWriter.Write(ResourceData, bicepOptions).ToMemory();

#if NET6_0_OR_GREATER
WriteLines(0, BinaryData.FromBytes(data[2..]), stream, this);
Expand Down
8 changes: 4 additions & 4 deletions sdk/provisioning/Azure.Provisioning/src/ResourceOfT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ public abstract class Resource<T> : Resource
/// <param name="resourceName">The resouce name.</param>
/// <param name="resourceType">The resourceType.</param>
/// <param name="version">The version.</param>
/// <param name="properties">The properites.</param>
protected Resource(IConstruct scope, Resource? parent, string resourceName, ResourceType resourceType, string version, T properties)
: base(scope, parent, resourceName, resourceType, version, properties)
/// <param name="createProperties">Lambda to create the ARM properties.</param>
protected Resource(IConstruct scope, Resource? parent, string resourceName, ResourceType resourceType, string version, Func<string, T> createProperties)
: base(scope, parent, resourceName, resourceType, version, (name) => createProperties(name))
{
Properties = properties;
Properties = (T)ResourceData;
}

/// <summary>
Expand Down
11 changes: 3 additions & 8 deletions sdk/provisioning/Azure.Provisioning/src/keyvault/KeyVault.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ public class KeyVault : Resource<KeyVaultData>
/// <param name="name">The name.</param>
/// <param name="version">The version.</param>
/// <param name="location">The location.</param>
public KeyVault(IConstruct scope, string? name = null, string version = "2023-02-01", AzureLocation? location = default)
: base(scope, null, GetName(scope, name), ResourceTypeName, version, ArmKeyVaultModelFactory.KeyVaultData(
name: GetName(scope, name),
public KeyVault(IConstruct scope, string name = "kv", string version = "2023-02-01", AzureLocation? location = default)
: base(scope, null, name, ResourceTypeName, version, (name) => ArmKeyVaultModelFactory.KeyVaultData(
name: name,
resourceType: ResourceTypeName,
location: location ?? Environment.GetEnvironmentVariable("AZURE_LOCATION") ?? AzureLocation.WestUS,
properties: ArmKeyVaultModelFactory.KeyVaultProperties(
Expand All @@ -46,11 +46,6 @@ public KeyVault(IConstruct scope, string? name = null, string version = "2023-02
{
}

private static string GetName(IConstruct scope, string? name)
{
return name is null ? $"kv-{scope.EnvironmentName}" : $"{name}-{scope.EnvironmentName}";
}

/// <summary>
/// Adds an access policy to the <see cref="KeyVault"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal class KeyVaultAddAccessPolicy : Resource<KeyVaultAccessPolicyParameters
private const string ResourceTypeName = "Microsoft.KeyVault/vaults/accessPolicies";

public KeyVaultAddAccessPolicy(IConstruct scope, Parameter principalIdParameter, KeyVault? parent = default, string version = "2023-02-01", AzureLocation? location = default)
: base(scope, parent, "add", ResourceTypeName, version, ArmKeyVaultModelFactory.KeyVaultAccessPolicyParameters(
: base(scope, parent, "add", ResourceTypeName, version, (name) => ArmKeyVaultModelFactory.KeyVaultAccessPolicyParameters(
name: "add",
resourceType: ResourceTypeName,
accessPolicies: new List<KeyVaultAccessPolicy>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ public class KeyVaultSecret : Resource<KeyVaultSecretData>
/// <param name="scope">The scope.</param>
/// <param name="name">The name.</param>
/// <param name="version">The version.</param>
public KeyVaultSecret(IConstruct scope, string name, string version = "2023-02-01")
: base(scope, null, GetName(scope, name), ResourceTypeName, version, ArmKeyVaultModelFactory.KeyVaultSecretData(
name: GetName(scope, name),
public KeyVaultSecret(IConstruct scope, string name = "kvs", string version = "2023-02-01")
: base(scope, null, name, ResourceTypeName, version, (name) => ArmKeyVaultModelFactory.KeyVaultSecretData(
name: name,
resourceType: ResourceTypeName,
properties: ArmKeyVaultModelFactory.SecretProperties(
value: Guid.Empty.ToString())
Expand All @@ -39,24 +39,22 @@ public KeyVaultSecret(IConstruct scope, string name, string version = "2023-02-0
/// <param name="connectionString">The connection string.</param>
/// <param name="version">The version.</param>
public KeyVaultSecret(IConstruct scope, string name, ConnectionString connectionString, string version = "2023-02-01")
: base(scope, null, GetName(scope, name), ResourceTypeName, version, ArmKeyVaultModelFactory.KeyVaultSecretData(
name: GetName(scope, name),
: base(scope, null, name, ResourceTypeName, version, (name) => ArmKeyVaultModelFactory.KeyVaultSecretData(
name: name,
resourceType: ResourceTypeName,
properties: ArmKeyVaultModelFactory.SecretProperties(
value: connectionString.Value)
))
{
}

private static string GetName(IConstruct scope, string? name) => name is null ? $"kvs-{scope.EnvironmentName}" : name;

/// <inheritdoc/>
protected override Resource? FindParentInScope(IConstruct scope)
{
var result = base.FindParentInScope(scope);
if (result is null)
{
result = scope.GetSingleResource<KeyVault>() ?? new KeyVault(scope, "kv");
result = scope.GetSingleResource<KeyVault>() ?? new KeyVault(scope);
}
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,15 @@ public class ResourceGroup : Resource<ResourceGroupData>
/// <param name="name">The name of the resourceGroup.</param>
/// <param name="version">The version of the resourceGroup.</param>
/// <param name="location">The location of the resourceGroup.</param>
public ResourceGroup(IConstruct scope, string? name = default, string version = "2023-07-01", AzureLocation? location = default)
: base(scope, null, GetName(scope, name), ResourceType, version, ResourceManagerModelFactory.ResourceGroupData(
name: GetName(scope, name),
public ResourceGroup(IConstruct scope, string name = "rg", string version = "2023-07-01", AzureLocation? location = default)
: base(scope, null, name, ResourceType, version, (name) => ResourceManagerModelFactory.ResourceGroupData(
name: name,
resourceType: ResourceType,
tags: new Dictionary<string, string> { { "azd-env-name", scope.EnvironmentName } },
location: location ?? Environment.GetEnvironmentVariable("AZURE_LOCATION") ?? AzureLocation.WestUS))
{
}

private static string GetName(IConstruct scope, string? name) => name is null ? $"rg-{scope.EnvironmentName}" : $"{name}-{scope.EnvironmentName}";

/// <inheritdoc/>
protected override Resource? FindParentInScope(IConstruct scope)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ public class Subscription : Resource<SubscriptionData>
{
internal static readonly ResourceType ResourceType = "Microsoft.Resources/subscriptions";

private static string GetName(Guid? guid) => guid.HasValue ? guid.Value.ToString() : Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID") ?? throw new InvalidOperationException("No environment variable named 'AZURE_SUBSCRIPTION_ID' found");

/// <summary>
/// Initializes a new instance of the <see cref="Subscription"/>.
/// </summary>
Expand All @@ -28,14 +26,20 @@ public Subscription(IConstruct scope, Guid? guid = default)
: base(
scope,
scope.Root,
GetName(guid),
guid?.ToString()!,
ResourceType,
"2022-12-01",
ResourceManagerModelFactory.SubscriptionData(
id: SubscriptionResource.CreateResourceIdentifier(GetName(guid)),
subscriptionId: GetName(guid),
(name) => ResourceManagerModelFactory.SubscriptionData(
id: SubscriptionResource.CreateResourceIdentifier(name),
subscriptionId: name,
tenantId: scope.Root.Properties.TenantId))
{
}

/// <inheritdoc/>
protected override string GetAzureName(IConstruct scope, string resourceName)
{
return resourceName is not null ? resourceName : Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID") ?? throw new InvalidOperationException("No environment variable named 'AZURE_SUBSCRIPTION_ID' found");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,21 @@ public class Tenant : Resource<TenantData>
{
private const string ResourceTypeName = "Microsoft.Resources/tenants";

private static string GetName() => Environment.GetEnvironmentVariable("AZURE_TENANT_ID") ?? throw new InvalidOperationException("No environment variable named 'AZURE_TENANT_ID' found");

/// <summary>
/// Initializes a new instance of the <see cref="Tenant"/>.
/// </summary>
/// <param name="scope">The scope the tenant belongs to.</param>
/// <param name="tenantId">The tenant id.</param>
public Tenant(IConstruct scope, Guid? tenantId = null)
: base(scope, null, tenantId.HasValue ? tenantId.Value.ToString() : GetName(), ResourceTypeName, "2022-12-01", ResourceManagerModelFactory.TenantData(
tenantId: tenantId.HasValue ? tenantId.Value : Guid.Parse(GetName())))
: base(scope, null, tenantId?.ToString()!, ResourceTypeName, "2022-12-01", (name) => ResourceManagerModelFactory.TenantData(
tenantId: tenantId.HasValue ? tenantId.Value : Guid.Parse(name)))
{
}

/// <inheritdoc/>
protected override string GetAzureName(IConstruct scope, string resourceName)
{
return resourceName is not null ? resourceName : Environment.GetEnvironmentVariable("AZURE_TENANT_ID") ?? throw new InvalidOperationException("No environment variable named 'AZURE_TENANT_ID' found");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ public class DeploymentScript : Resource<AzureCliScript>
/// <param name="version">The resource version.</param>
/// <param name="location">The resource location.</param>
public DeploymentScript(IConstruct scope, string resourceName, IEnumerable<ScriptEnvironmentVariable> scriptEnvironmentVariables, string scriptContent, string version = _defaultVersion, AzureLocation? location = default)
: base(scope, null, GetName(scope, resourceName), ResourceTypeName, version, ArmResourcesModelFactory.AzureCliScript(
name: GetName(scope, resourceName),
: base(scope, null, resourceName, ResourceTypeName, version, (name) => ArmResourcesModelFactory.AzureCliScript(
name: name,
resourceType: ResourceTypeName,
location: location ?? Environment.GetEnvironmentVariable("AZURE_LOCATION") ?? AzureLocation.WestUS,
azCliVersion: "2.37.0",
Expand All @@ -51,8 +51,8 @@ public DeploymentScript(IConstruct scope, string resourceName, IEnumerable<Scrip
/// <param name="version">The resource version.</param>
/// <param name="location">The resource location.</param>
public DeploymentScript(IConstruct scope, string resourceName, Resource database, Parameter appUserPasswordSecret, Parameter sqlAdminPasswordSecret, string version = _defaultVersion, AzureLocation? location = default)
: base(scope, null, GetName(scope, resourceName), ResourceTypeName, version, ArmResourcesModelFactory.AzureCliScript(
name: GetName(scope, resourceName),
: base(scope, null, resourceName, ResourceTypeName, version, (name) => ArmResourcesModelFactory.AzureCliScript(
name: name,
resourceType: ResourceTypeName,
location: location ?? Environment.GetEnvironmentVariable("AZURE_LOCATION") ?? AzureLocation.WestUS,
azCliVersion: "2.37.0",
Expand Down Expand Up @@ -88,8 +88,6 @@ alter role db_owner add member ${APPUSERNAME}
Scope.AddParameter(sqlAdminPasswordSecret);
}

private static string GetName(IConstruct scope, string? name) => name is null ? $"deploymentScript-{scope.EnvironmentName}" : $"{name}-{scope.EnvironmentName}";

/// <inheritdoc/>
protected override Resource? FindParentInScope(IConstruct scope)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ public class SqlDatabase : Resource<SqlDatabaseData>
/// <param name="name">The name.</param>
/// <param name="version">The version.</param>
/// <param name="location">The location.</param>
public SqlDatabase(IConstruct scope, string? name = default, string version = "2022-08-01-preview", AzureLocation? location = default)
: base(scope, null, GetName(scope, name), ResourceTypeName, version, ArmSqlModelFactory.SqlDatabaseData(
name: GetName(scope, name),
public SqlDatabase(IConstruct scope, string name = "db", string version = "2022-08-01-preview", AzureLocation? location = default)
: base(scope, null, name, ResourceTypeName, version, (name) => ArmSqlModelFactory.SqlDatabaseData(
name: name,
resourceType: ResourceTypeName,
location: location ?? Environment.GetEnvironmentVariable("AZURE_LOCATION") ?? AzureLocation.WestUS))
{
Expand All @@ -39,8 +39,6 @@ public SqlDatabase(IConstruct scope, string? name = default, string version = "2
public ConnectionString GetConnectionString(Parameter passwordSecret, string userName = "appUser")
=> new ConnectionString(this, passwordSecret, userName);

private static string GetName(IConstruct scope, string? name) => name is null ? $"db-{scope.EnvironmentName}" : name;

/// <inheritdoc/>
protected override Resource? FindParentInScope(IConstruct scope)
{
Expand Down
Loading

0 comments on commit fbef263

Please sign in to comment.