diff --git a/sdk/provisioning/Azure.Provisioning/src/ModuleConstruct.cs b/sdk/provisioning/Azure.Provisioning/src/ModuleConstruct.cs
index f740ee7a68b0c..7d6a0194becef 100644
--- a/sdk/provisioning/Azure.Provisioning/src/ModuleConstruct.cs
+++ b/sdk/provisioning/Azure.Provisioning/src/ModuleConstruct.cs
@@ -206,7 +206,10 @@ private void WriteParameters(MemoryStream stream)
{
continue;
}
- string defaultValue = parameter.DefaultValue is null ? string.Empty : $" = '{parameter.DefaultValue}'";
+ string defaultValue =
+ parameter.DefaultValue is null ?
+ string.Empty :
+ parameter.IsExpression ? $" = {parameter.DefaultValue}" : $" = '{parameter.DefaultValue}'";
if (parameter.IsSecure)
stream.WriteLine($"@secure()");
diff --git a/sdk/provisioning/Azure.Provisioning/src/Parameter.cs b/sdk/provisioning/Azure.Provisioning/src/Parameter.cs
index 02102d5005dcf..b940ad3065255 100644
--- a/sdk/provisioning/Azure.Provisioning/src/Parameter.cs
+++ b/sdk/provisioning/Azure.Provisioning/src/Parameter.cs
@@ -29,6 +29,10 @@ public readonly struct Parameter
/// Gets a value indicating whether the parameter is secure.
///
public bool IsSecure { get; }
+ ///
+ /// Gets a value indicating whether the parameter is an expression.
+ ///
+ internal bool IsExpression { get; }
internal bool IsFromOutput => Output != null;
internal bool IsLiteral => Output?.IsLiteral ?? false;
@@ -74,7 +78,19 @@ public Parameter(string name, string? description = default, object? defaultValu
DefaultValue = defaultValue;
IsSecure = isSecure;
}
-
+ ///
+ /// Initializes a new instance of the .
+ ///
+ /// The parameter name.
+ /// The parameter description.
+ /// The parameter defaultValue.
+ /// Is the parameter secure.
+ /// Is the parameter an expression.
+ internal Parameter(string name, string? description = default, object? defaultValue = default, bool isSecure = false, bool isExpression = false)
+ : this (name, description, defaultValue, isSecure)
+ {
+ IsExpression = isExpression;
+ }
internal string GetParameterString(IConstruct parentScope)
{
// If the parameter is not from an output, use the parameter name.
diff --git a/sdk/provisioning/Azure.Provisioning/src/Resource.cs b/sdk/provisioning/Azure.Provisioning/src/Resource.cs
index 04e7bbdec2f98..6cd183bb4d3eb 100644
--- a/sdk/provisioning/Azure.Provisioning/src/Resource.cs
+++ b/sdk/provisioning/Azure.Provisioning/src/Resource.cs
@@ -151,6 +151,7 @@ protected virtual string GetAzureName(IConstruct scope, string resourceName)
/// The to assign.
private protected void AssignParameter(object instance, string propertyName, Parameter parameter)
{
+ ValidateOverrideCanBeAdded(instance, propertyName);
if (ParameterOverrides.TryGetValue(instance, out var overrides))
{
overrides[propertyName] = parameter;
@@ -167,6 +168,7 @@ private protected void AssignParameter(object instance, string propertyName, Par
private protected void AssignProperty(object instance, string propertyName, string propertyValue)
{
+ ValidateOverrideCanBeAdded(instance, propertyName);
if (PropertyOverrides.TryGetValue(instance, out var overrides))
{
overrides[propertyName] = propertyValue;
@@ -177,6 +179,18 @@ private protected void AssignProperty(object instance, string propertyName, stri
}
}
+ private void ValidateOverrideCanBeAdded(object instance, string name)
+ {
+ if ((PropertyOverrides.TryGetValue(instance, out var instancePropertyOverrides) &&
+ instancePropertyOverrides.ContainsKey(name)) ||
+ (ParameterOverrides.TryGetValue(instance, out var instanceParameterOverrides) &&
+ instanceParameterOverrides.ContainsKey(name)))
+ {
+ throw new InvalidOperationException(
+ $"A parameter or property override was already defined for property {name} in type {instance.GetType()}");
+ }
+ }
+
///
/// Adds an output to the resource.
///
diff --git a/sdk/provisioning/Azure.Provisioning/src/ResourceOfT.cs b/sdk/provisioning/Azure.Provisioning/src/ResourceOfT.cs
index beaf0d16889fd..b67fd4e92f0bc 100644
--- a/sdk/provisioning/Azure.Provisioning/src/ResourceOfT.cs
+++ b/sdk/provisioning/Azure.Provisioning/src/ResourceOfT.cs
@@ -51,7 +51,7 @@ protected Resource(
{
// 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
- AssignProperty(Properties, "Location", $"{ResourceGroup.AnonymousResourceGroupName}.location");
+ AssignParameter(Properties, "Location", new Parameter("location", defaultValue: $"{ResourceGroup.AnonymousResourceGroupName}.location", isExpression: true));
}
}
diff --git a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/StorageBlobDefaults/resources/rg_TEST_module/rg_TEST_module.bicep b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/StorageBlobDefaults/resources/rg_TEST_module/rg_TEST_module.bicep
index 6ac855ffd0958..237c99e435bcd 100644
--- a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/StorageBlobDefaults/resources/rg_TEST_module/rg_TEST_module.bicep
+++ b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/StorageBlobDefaults/resources/rg_TEST_module/rg_TEST_module.bicep
@@ -1,6 +1,6 @@
-resource storageAccount_hWQR13jUf 'Microsoft.Storage/storageAccounts@2022-09-01' = {
- name: 'photoacct71d9792260684fd'
+resource storageAccount_rJRF56wLn 'Microsoft.Storage/storageAccounts@2022-09-01' = {
+ name: 'photoacctdbd29b86eb654ef'
location: 'westus'
sku: {
name: 'Premium_LRS'
@@ -10,8 +10,8 @@ resource storageAccount_hWQR13jUf 'Microsoft.Storage/storageAccounts@2022-09-01'
}
}
-resource blobService_UnN8ydpj5 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = {
- parent: storageAccount_hWQR13jUf
+resource blobService_wGH0Fapvd 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = {
+ parent: storageAccount_rJRF56wLn
name: 'default'
properties: {
}
diff --git a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/StorageBlobDefaultsInPromptMode/main.bicep b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/StorageBlobDefaultsInPromptMode/main.bicep
index 5c8b1c572ae90..21b06a8aa946a 100644
--- a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/StorageBlobDefaultsInPromptMode/main.bicep
+++ b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/StorageBlobDefaultsInPromptMode/main.bicep
@@ -1,9 +1,12 @@
targetScope = 'resourceGroup'
+@description('')
+param location string = resourceGroup().location
-resource storageAccount_Ls2Cd9uGu 'Microsoft.Storage/storageAccounts@2022-09-01' = {
+
+resource storageAccount_1JetI2q6o 'Microsoft.Storage/storageAccounts@2022-09-01' = {
name: toLower(take(concat('photoAcct', uniqueString(resourceGroup().id)), 24))
- location: resourceGroup().location
+ location: location
sku: {
name: 'Premium_LRS'
}
@@ -12,8 +15,8 @@ resource storageAccount_Ls2Cd9uGu 'Microsoft.Storage/storageAccounts@2022-09-01'
}
}
-resource blobService_OdYIZd2T2 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = {
- parent: storageAccount_Ls2Cd9uGu
+resource blobService_vupTLRqwy 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = {
+ parent: storageAccount_1JetI2q6o
name: 'default'
properties: {
}
diff --git a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/StorageBlobDropDown/resources/rg_TEST_module/rg_TEST_module.bicep b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/StorageBlobDropDown/resources/rg_TEST_module/rg_TEST_module.bicep
index a0ea74f4191de..e47a69c839aaa 100644
--- a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/StorageBlobDropDown/resources/rg_TEST_module/rg_TEST_module.bicep
+++ b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/StorageBlobDropDown/resources/rg_TEST_module/rg_TEST_module.bicep
@@ -1,6 +1,6 @@
-resource storageAccount_dx0zUTplq 'Microsoft.Storage/storageAccounts@2022-09-01' = {
- name: 'photoacctc31a9d6584644ae'
+resource storageAccount_3S3yTe8Uk 'Microsoft.Storage/storageAccounts@2022-09-01' = {
+ name: 'photoacct0cba824071e046a'
location: 'westus'
sku: {
name: 'Premium_LRS'
@@ -10,8 +10,8 @@ resource storageAccount_dx0zUTplq 'Microsoft.Storage/storageAccounts@2022-09-01'
}
}
-resource blobService_PYZrLaKZo 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = {
- parent: storageAccount_dx0zUTplq
+resource blobService_Mv9WBXOiS 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = {
+ parent: storageAccount_3S3yTe8Uk
name: 'default'
properties: {
deleteRetentionPolicy: {
diff --git a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL1/resources/rg_TEST_module/rg_TEST_module.bicep b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL1/resources/rg_TEST_module/rg_TEST_module.bicep
index 4373889cae372..52fc3eba727de 100644
--- a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL1/resources/rg_TEST_module/rg_TEST_module.bicep
+++ b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL1/resources/rg_TEST_module/rg_TEST_module.bicep
@@ -142,7 +142,6 @@ resource sqlServer_RgHXTrwDE 'Microsoft.Sql/servers@2022-08-01-preview' = {
resource sqlDatabase_6M6mjEKjO 'Microsoft.Sql/servers/databases@2022-08-01-preview' = {
parent: sqlServer_RgHXTrwDE
name: 'db-TEST'
- location: 'westus'
properties: {
}
}
diff --git a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL2/resources/rg_TEST_module/rg_TEST_module.bicep b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL2/resources/rg_TEST_module/rg_TEST_module.bicep
index ffc5a4bf44398..3b66661aa5b88 100644
--- a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL2/resources/rg_TEST_module/rg_TEST_module.bicep
+++ b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL2/resources/rg_TEST_module/rg_TEST_module.bicep
@@ -142,7 +142,6 @@ resource sqlServer_RgHXTrwDE 'Microsoft.Sql/servers@2022-08-01-preview' = {
resource sqlDatabase_6M6mjEKjO 'Microsoft.Sql/servers/databases@2022-08-01-preview' = {
parent: sqlServer_RgHXTrwDE
name: 'db-TEST'
- location: 'westus'
properties: {
}
}
diff --git a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL3/resources/rg_TEST_module/rg_TEST_module.bicep b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL3/resources/rg_TEST_module/rg_TEST_module.bicep
index 3ed5ce32d85f9..3415a0a67044b 100644
--- a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL3/resources/rg_TEST_module/rg_TEST_module.bicep
+++ b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL3/resources/rg_TEST_module/rg_TEST_module.bicep
@@ -145,7 +145,6 @@ resource sqlServer_RgHXTrwDE 'Microsoft.Sql/servers@2022-08-01-preview' = {
resource sqlDatabase_6M6mjEKjO 'Microsoft.Sql/servers/databases@2022-08-01-preview' = {
parent: sqlServer_RgHXTrwDE
name: 'db-TEST'
- location: 'westus'
properties: {
}
}
diff --git a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL3ResourceGroupScope/main.bicep b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL3ResourceGroupScope/main.bicep
index 307f58507edf4..23760c5737698 100644
--- a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL3ResourceGroupScope/main.bicep
+++ b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL3ResourceGroupScope/main.bicep
@@ -1,5 +1,8 @@
targetScope = 'resourceGroup'
+@description('')
+param location string = resourceGroup().location
+
@secure()
@description('SQL Server administrator password')
param sqlAdminPassword string
@@ -11,7 +14,7 @@ param appUserPassword string
resource appServicePlan_PxkuWnuWL 'Microsoft.Web/serverfarms@2021-02-01' = {
name: 'appServicePlan-TEST'
- location: resourceGroup().location
+ location: location
sku: {
name: 'B1'
}
@@ -22,7 +25,7 @@ resource appServicePlan_PxkuWnuWL 'Microsoft.Web/serverfarms@2021-02-01' = {
resource keyVault_zomsD2kWf 'Microsoft.KeyVault/vaults@2023-02-01' = {
name: 'kv-TEST'
- location: resourceGroup().location
+ location: location
tags: {
'key': 'value'
}
@@ -81,7 +84,7 @@ resource keyVaultSecret_YNErVycWe 'Microsoft.KeyVault/vaults/secrets@2023-02-01'
resource webSite_IGuzwfciS 'Microsoft.Web/sites@2021-02-01' = {
name: 'frontEnd-TEST'
- location: resourceGroup().location
+ location: location
kind: 'app,linux'
properties: {
serverFarmId: '/subscriptions/subscription()/resourceGroups/resourceGroup()/providers/Microsoft.Web/serverfarms/appServicePlan-TEST'
@@ -134,7 +137,7 @@ resource webSiteConfigLogs_GwVSHGFxS 'Microsoft.Web/sites/config@2021-02-01' = {
resource sqlServer_2CRay8gJr 'Microsoft.Sql/servers@2022-08-01-preview' = {
name: 'sqlserver-TEST'
- location: resourceGroup().location
+ location: location
properties: {
administratorLogin: 'sqladmin'
administratorLoginPassword: sqlAdminPassword
@@ -162,7 +165,7 @@ resource sqlFirewallRule_MTg5B9jZr 'Microsoft.Sql/servers/firewallRules@2020-11-
resource deploymentScript_qloqQ8wU0 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
name: 'cliScript-TEST'
- location: resourceGroup().location
+ location: location
kind: 'AzureCLI'
properties: {
cleanupPreference: 'OnSuccess'
@@ -212,7 +215,7 @@ SCRIPT_END
resource webSite_TR8bo87ZZ 'Microsoft.Web/sites@2021-02-01' = {
name: 'backEnd-TEST'
- location: resourceGroup().location
+ location: location
kind: 'app,linux'
properties: {
serverFarmId: '/subscriptions/subscription()/resourceGroups/resourceGroup()/providers/Microsoft.Web/serverfarms/appServicePlan-TEST'
diff --git a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL3SpecificSubscription/resources/rg_TEST_module/rg_TEST_module.bicep b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL3SpecificSubscription/resources/rg_TEST_module/rg_TEST_module.bicep
index a6508b8c3d1b4..6385533de29ba 100644
--- a/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL3SpecificSubscription/resources/rg_TEST_module/rg_TEST_module.bicep
+++ b/sdk/provisioning/Azure.Provisioning/tests/Infrastructure/WebSiteUsingL3SpecificSubscription/resources/rg_TEST_module/rg_TEST_module.bicep
@@ -145,7 +145,6 @@ resource sqlServer_zjdvvB2wl 'Microsoft.Sql/servers@2022-08-01-preview' = {
resource sqlDatabase_U7NzorRJT 'Microsoft.Sql/servers/databases@2022-08-01-preview' = {
parent: sqlServer_zjdvvB2wl
name: 'db-TEST'
- location: 'westus'
properties: {
}
}
diff --git a/sdk/provisioning/Azure.Provisioning/tests/ProvisioningTests.cs b/sdk/provisioning/Azure.Provisioning/tests/ProvisioningTests.cs
index 342e268c54dae..52a27b4f1e57f 100644
--- a/sdk/provisioning/Azure.Provisioning/tests/ProvisioningTests.cs
+++ b/sdk/provisioning/Azure.Provisioning/tests/ProvisioningTests.cs
@@ -218,6 +218,34 @@ public async Task StorageBlobDefaultsInPromptMode()
await ValidateBicepAsync(promptMode: true);
}
+ [Test]
+ public void CannotAddLocationParameterInPromptMode()
+ {
+ var infra = new TestInfrastructure(configuration: new Configuration { UsePromptMode = true });
+ var sa = infra.AddStorageAccount(name: "photoAcct", sku: StorageSkuName.PremiumLrs, kind: StorageKind.BlockBlobStorage);
+ Assert.Throws(() =>
+ sa.AssignParameter(d => d.Location, new Parameter("myLocationParam")));
+ }
+
+ [Test]
+ public void CannotOverrideSamePropertyMoreThanOnce()
+ {
+ var infra = new TestInfrastructure();
+ var sa = infra.AddStorageAccount(name: "photoAcct", sku: StorageSkuName.PremiumLrs, kind: StorageKind.BlockBlobStorage);
+
+ sa.AssignParameter(d => d.Kind, new Parameter("skuParam"));
+ Assert.Throws(() =>
+ sa.AssignProperty(d => d.Kind, StorageKind.BlockBlobStorage.ToString()));
+ Assert.Throws(() =>
+ sa.AssignParameter(d => d.Kind, new Parameter("skuParam")));
+
+ sa.AssignProperty(d => d.AccessTier, StorageAccountAccessTier.Cool.ToString());
+ Assert.Throws(() =>
+ sa.AssignProperty(d => d.AccessTier, StorageAccountAccessTier.Cool.ToString()));
+ Assert.Throws(() =>
+ sa.AssignParameter(d => d.AccessTier, new Parameter("tierParam")));
+ }
+
[Test]
public async Task StorageBlobDropDown()
{