Using Empty/Optional Parameters in Azure ARM Template - azure

I'm having issues setting up an ARM Template for Azure Web Apps in that I can't add ConnectionString parameters where it sets the values if the parameters are set, but leave blank (default) if the parameters aren't set.
Here is how it looks in the template.json file:
"connectionStrings": [
{
"name": "[parameters('connString').connName)]",
"connectionString": "[parameters('connString').string]",
"type": "[parameters('connString').connType]"
}
],
And in the parameters.json file:
"connString": {
"value": {
"connName": "",
"string": "",
"connType": ""
}
},
When running the deployment with the above it fails on "Parameter name cannot be empty"
I attempted to use an equals function to set the value as empty if the parameter is empty, but set the parameter if the parameter is filled out, however it doesn't like the empty value.
"name": "[if(equals(parameters('connString').connName,''),'',parameters('connString').connName)]"
Also attempted an empty function:
"name": "[not(empty(parameters('connString').connName))]"
However this returns "False" if empty and "True" if the parameter is set (as designed)
The deployment works fine if I set dummy values as the parameters, is it possible to set a function or something similar so if the parameter is empty it uses whatever value is sent as if the connectionStrings section wasn't present in the template? These parameters are optional but it looks like because they're in the actual template.json file its expecting a value.
Cheers
EDIT
Going to post what my end templates looked like in case someone else needs assistance.
Template File
Variables
"variables": {
"empty": []
},
Resources
"connectionStrings": "[if(empty(parameters('connString')), variables('empty'), array(parameters('connString')))]",
Parameter File
If setting a connection string
"connString": {
"value": [{
"name": "test",
"connectionString": "ufgndjkngsdgjkn",
"type": "Custom"
}]
},
If not wanting to set a connection string
"connString": {
"value": [
]
},

you should just do this:
parameters:
"connString": {
"value": {}
},
variables:
"empty": [],
template:
"connectionStrings": "[if(empty(parameters('connString')), variables('empty'), array(parameters('connString')))]"

you could try:
parameters:
"connString": [{
"value": {
"name": null,
"connectionString": null,
"type": "Custom"
}
}],
variables:
template:
"connectionStrings": "[if(empty(parameters('connString')[0].value.name), json('null'), parameters('connString'))]"

Related

BICEP - Parameter file variable assignment

I was following the repo for separate parameter file to each env as defined in the https://github.com/Azure/bicep/discussions/4586
I tried the separate parameters file for dev, stage, prod but the value assignment in main module variable remains flagged by intelligence even though it exists same param exist in the respective parameter file.
Other approach I tried is loadjson variable, but it does not show auto completion for items under subnet block as it stopes right after value.
Maybe I am overthinking and not applying the correct approach, Perhaps I should ignore intellisense and try deploying by applying parameter and hope it will auto pick correct value during the deployment param check.
Here is my parameter file and the same value applies to each env param json.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"department": {
"value": "finance"
},
"saAccountCount": {
"value": 1
},
"vmCount": {
"value": 1
},
"locationIndex": { //idenx 1 = app server, 2=AD, 3=Tool server, 4= dchp server
"value": 1
},
"appRoleIndex": { //idenx 1 = westus2, 2= westus, 3= eastus, 4=centralus, 5=uswest3
"value": 1
},
"appRole": {
"value": {
"Applicatoin Server": "ap",
"Active Directory": "dc",
"Tool server": "tool",
"DHCP server": "dhcp"
}
},
"environment": {
"value": "dev"
},
"addressPrefixes": {
"value": [
"172.16.0.0/20"
]
},
"dnsServers": {
"value": [
"1.1.1.1",
"4.4.4.4"
]
},
"locationList": {
"value": {
"westus2": "azw2",
"westus": "azw",
"Eastus": "aze",
"CentralUS": "azc",
"westus3": "azw3"
}
},
"subnets": {
"value": [
{
"name": "frontend",
"subnetPrefix": "172.16.2.0/24",
"delegation": "Microsoft.Web/serverfarms",
"privateEndpointNetworkPolicies": "disabled",
"serviceEndpoints": [
{
"service": "Microsoft.KeyVault",
"locations": [
"*"
]
},
{
"service": "Microsoft.Web",
"locations": [
"*"
]
}
]
},
{
"name": "backend",
"subnetPrefix": "172.16.3.0/24",
"delegation": "Microsoft.Web/serverfarms",
"privateEndpointNetworkPolicies": "enabled",
"serviceEndpoints": [
{
"service": "Microsoft.KeyVault",
"locations": [
"*"
]
},
{
"service": "Microsoft.Web",
"locations": [
"*"
]
},
{
"service": "Microsoft.AzureCosmosDB",
"locations": [
"*"
]
}
]
}
]
}
}
}
You appear to be attempting to deploy an Azure Resource Management (ARM) template using a parameter file.
The parameter file is used to pass values to the ARM template during deployment. The parameter file must use the same types as the ARM template and can only include values for the ARM template's parameters.
You will receive an error if the parameter file contains extra parameters that do not match the ARM template's parameters.
In the same deployment process, you can use both inline parameters and a local parameter file. If you specify a parameter's value in both the local parameter file and inline, the inline value takes priority.
Refer to create a parameter file of an ARM template
About the different parameters file for dev, stage, and prod, it's likely that the parameter file is not correctly linked to the ARM template.
You can deploy the ARM template with the parameter file to determine if it will automatically select the proper value during the deployment parameter check.
Regarding the loadjson variable, it is possible that the loadjson variable is not properly formatted.
You can double-check the loadjson variable's format to ensure it's proper.
After a workaround on this, I created a sample parameter.json file for a webapp to deploy in a production environment and that worked for me.
Note: Alternatively, You can use az deployment group create with a parameters file and deploy into Azure to avoid these conflicts.

How to reference Keyvault Secret Tags from ARM template

I have an ARM template which syncs secret value from source Keyvault into Destination one.
I also want to sync secret tags, but ARM reference that I use for 'sourceKV.secret.tags' retrieval does not work
[reference(resourceId('subscriptionId', 'resourceGroup', 'Microsoft.KeyVault/vaults/secrets', 'SourceKV', 'Secret'), '2021-04-01-preview', 'Full').tags.tagName]
any ideas what can be the issue, or what is the correct form to retrieve tags during ARM template deployment?
These work for me:
"outputs": {
"tags": {
"type": "string",
"value": "[reference('/subscriptions/xxxx/resourceGroups/yyyy/providers/Microsoft.KeyVault/vaults/zzzz/secrets/mysecret', '2022-07-01', 'Full').tags]"
},
"tagValue": {
"type": "string",
"value": "[reference('/subscriptions/xxxx/resourceGroups/yyyy/providers/Microsoft.KeyVault/vaults/zzzz/secrets/mysecret', '2022-07-01', 'Full').tags.hello]"
},
"tagValue2": {
"type": "string",
"value": "[reference(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.KeyVault/vaults/secrets', 'xxxx', 'mysecret'), '2021-04-01-preview', 'Full').tags.hello]"
}
}
Will result in:
"outputs": {
"tagValue": {
"type": "String",
"value": "world"
},
"tagValue2": {
"type": "String",
"value": "world"
},
"tags": {
"type": "Object",
"value": {
"hello": "world"
}
}
}
Also works with the API version you used. It is important that you use 'Full', otherwise you won't get the tags. Note that you can use this syntax anywhere in your template. I just used it in the outputs because it is good for testing.
As I found out it is not possible to use Reference function for setting tags property value for keyvault as valid usages state
reference func only works if it is used inside properties block or for outputs; but as tags are not part of properties instead of returning value reference fun returns just string "reference(resource...)"

Azure ARM Template for Dropdownn type Parameter

I am creating a Azure ARM template to provision VMs based on the environmnet type; so created an array type parameter as below,
"EnvironmentType": {
"type": "array",
"defaultValue": [
"Dev",
"Test",
"PreProd",
"Prod"
]
},
But on Azure portal this parameter is rendered as textbox with comma separated values as shown in below screenshot.
How to get this parameter displayed as dropdown?
How to get this parameter displayed as dropdown?
As bmoore-msft mentioned we could replace the defaultValue with allowedValues and array with string. We also could set the dropdownlist default value from the template. In your case, please have a try to use the following code. More details we could refer to the Customize the template.
"parameters": {
"EnvironmentType": {
"type": "string",
"allowedValues": [
"Dev",
"Test",
"PreProd",
"Prod"
],
"defaultValue": "Dev",
"metadata": {
"description": "The type of replication to use for the EnvironmentType."
}
}
Replace "defaultValue" with "allowedValues" and "array" with "string".

using conditional IF statements in arm templates for properties,

I have a situation where I want to only add a property to a VM if a condition is met. For example if I want to add an availability set property to a machine then do this : Below I ONLY what to execute the availability set statement if a condition is TRUE, can you do this in an ARM template? eg if a value is true then do this line, if not skip?
{
"name": "[parameters('ComputerName')]",
"type": "Microsoft.Compute/virtualMachines",
"location": "[parameters('location')]",
"apiVersion": "2017-03-30",
"dependsOn": [
"[resourceId('Microsoft.Network/networkInterfaces', variables('1stNicName'))]",
"[resourceId('Microsoft.Network/networkInterfaces', variables('2ndicName'))]"
],
"tags": {
"displayName": "[parameters('ComputerName')]"
},
"properties":
{
"availabilitySet": {
"id": "[resourceId('Microsoft.Compute/availabilitySets',variables('availabilitySetName'))]"
},
"hardwareProfile": {
"vmSize": "[parameters('serverVmSize')]"
},
"osProfile": {
"computerName": "[parameters('serverName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]"
},
As of this week (1st August 2017), ARM templates now have an IF function that can be used for variables, properties and resource parameters. It works like most other ARM functions, with syntax like:
[if(condition, true value, false value)]
The example in the documentation does exactly what you are asking, adds (or doesn't add) an availbility set to a VM:
"apiVersion": "2016-03-30",
"type": "Microsoft.Compute/virtualMachines",
"properties": {
"availabilitySet": "[if(equals(parameters('availabilitySet'),'yes'), variables('availabilitySet'), json('null'))]",
...
}
Note that the True value, the availiblity set to use, is stored as a variable rather than inline in the if statement.
"variables": {
...
"availabilitySetName": "availabilitySet1",
"availabilitySet": {
"id": "[resourceId('Microsoft.Compute/availabilitySets',variables('availabilitySetName'))]"
}
},
There is no direct way of doing this. you can use conditional to choose a value, but you cannot not set a value or pass in null (unless you want to define 2 variables for `properties').
so you can just define 2 vm resources that are exactly the same except for the property in question (so availabilitySet). One would have it and the other one would not. And you would use "condition": expression in both resources. Condition should equals to false or true to work. There are several functions in ARM templates that can evaluate input and return true or false.
Reference:
https://samcogan.com/conditions-in-arm-templates-the-right-way/
"variables": {
"loadBalancerBackendAddressPools": [
{
"id": "[parameters('lbBackendAddressPool')]"
}
]
},
Believe i have found a workable way to avoid defining two entire VM definitions for an availability set. following are from my own testing in an arm template i have made with extensive conditional controls.
this works:
"availabilitySet": {
"id": "[resourceId('Microsoft.Compute/availabilitySets',parameters('availabilitySetName'))]"
}
this does not:
"availabilitySet": "[if(equals(parameters('availabilitySet'),'yes'), variables('availabilitySet'), json('null'))]",
this also does not work due to ID being 'null':
"availabilitySet": {
"id": "[if(equals(parameters('availabilitySet'),'yes'), variables('availabilitySet'), json('null'))]"
}
I have added the following variable and parameter:
"parameters": {
"SpotInstance": {
"type": "bool",
"defaultValue": false,
"allowedValues": [
true,
false
],
"metadata": {
"description": "Should the VM be deployed as a low cost Spot Instance, this will deploy without attaching the Availability Set."
}
}
},
"variables": {
"AvailibilitySet": {
"On":{
"id":"[resourceId('Microsoft.Compute/availabilitySets/',variables('ASName'))]"
},
"Off":"[json('null')]"
}
}
then used this logic on the availability set assignment:
"availabilitySet": "[if(equals(parameters('SpotInstance'),bool('true')),variables('AvailibilitySet').Off,variables('AvailibilitySet').On)]",
if you do not define id as a property of "AvailabilitySet" this can be null, if you Do however it cannot be null.
When i try the same for loadbalancer backend pool i get errors:
12:21:49 - [ERROR] "message": "Cannot deserialize the current JSON object (e.g.
12:21:49 - [ERROR] {\"name\":\"value\"}) into type 'Microsoft.WindowsAzure.Networking.Nrp.Frontend
12:21:49 - [ERROR] .Contract.Csm.Public.ResourceReferenceHashSet`2[Microsoft.WindowsAzure.Networki
12:21:49 - [ERROR] ng.Nrp.Frontend.Contract.Csm.Public.LoadBalancerBackendAddressPool,Microsoft.Wi
12:21:49 - [ERROR] ndowsAzure.Networking.Nrp.Frontend.Contract.Csm.Public.NetworkInterfaceIpConfig
12:21:49 - [ERROR] uration]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize
12:21:49 - [ERROR] correctly.\r\nTo fix this error either change the JSON to a JSON array (e.g.
12:21:49 - [ERROR] [1,2,3]) or change the deserialized type so that it is a normal .NET type
12:21:49 - [ERROR] (e.g. not a primitive type like integer, not a collection type like an array
12:21:49 - [ERROR] or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute
12:21:49 - [ERROR] can also be added to the type to force it to deserialize from a JSON
variables
"availabilitySet": {
"id": "[resourceId('Microsoft.Compute/availabilitySets',parameters('availabilitySetName'))]"
},
"loadBalancerBackendAddressPools": {
"id": "[resourceId('Microsoft.Network/loadBalancers',parameters('LoadBalancername'))]"
},
"loadBalancerBackendAddressPools": "[if(equals(parameters('DeployavailabilitySet'),'yes'), variables('loadBalancerBackendAddressPools'), json('null'))]",

Unable to build string in ARM template

I am currently working on building a few templates for some of the basic deployments that I use daily and have them working except for the last piece. The issue is that i am using t-shirt sizing to choose a specific vhd as I am deploying and I can not see to get the syntax correct for the uri string.
I know the syntax to call the variable on its own but when I add it into the uri string, it fails.
[variables(‘vhd’)[parameters('version')]]
Parameter:
"version": {
"type": "string",
"defaultValue": "10.5",
"allowedValues": [
"10.3.1",
"10.4.1",
"10.5"
],
Variable:
"vhd": {
"10.3.1": "20170524144905.vhd",
"10.4.1": "20170524144656.vhd",
"10.5": "20170524133408.vhd"
},
String:
"vhd": {
"uri": "[concat(concat(reference(resourceId(parameters('virtualMachineName'), 'Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2015-06-15').primaryEndpoints['blob'], 'vhds/'), parameters('virtualMachineName'), '20170524133408.vhd')]"
},

Resources