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

enable naming rule overrides per resource #42012

Merged
merged 4 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

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