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() {