At my current contract I’ve been doing a lot more DevOps than I have in the past, mainly because they don’t have enough specialist DevOps engineers. So us developers are encouraged to look after our own Build and Release pipelines and Azure resources. Which means over the past 18 months I’ve spent probably about a month writing ARM (Azure Resource Manager) templates.
Recently I was stuck trying to conditionally specify a property. ARM templates allow you to conditionally specify a resource, via the condition
property. But I couldn’t find an easy way to conditionally specify a property. I eventually figured out a way to do it which I couldn’t find documented anywhere.
In my case, I was deploying an API Management ARM template which is shared across our organization, and I wanted to be able to optionally set the OAuth 2.0 server for an API, if the server name is passed in as a parameter. The block in the ARM template looks like this:
{ "parameters": { "oAuthServerId": { "type": "string", "defaultValue": "", "metadata": { "description": "The ID of the OAuth 2.0 server." } } }, "resources": [ { "type": "Microsoft.ApiManagement/service/apis", "name": "[variables('apiName')]", "apiVersion": "2018-06-01-preview", "properties": { "authenticationSettings": { "oAuth2": { "authorizationServerId": "[parameters('oAuthServerId')]" } } } } ] }
Because this template is already shared across our organization, and most of the organization’s APIs are not using OAuth (yet), I added a new optional oAuthServerId parameter.
My initial naive implementation was just like the above. My hope was that for all the other APIs that aren’t using OAuth, the empty default value of the parameter would be used (“”), resulting in the following output:
"properties": { "authenticationSettings": { "oAuth2": { "authorizationServerId": "" } } }
My hope was that if the authorizationServerId is blank, then Azure would default the User Authorization to “None” (so that I don’t break existing deployments which already use this ARM template):
However, that didn’t work: Authorization server was not found.
I won’t bore you with all the things I tried in the hours that followed as I tried to get this to work. Eventually I found a solution, which is to create a variable for the entire authenticationSettings
object and conditionally specify that parent object, rather than trying to conditionally specify the child authentication.oAuth2.authorizationServerId
.
{ "parameters": { "oAuthServerId": { "type": "string", "defaultValue": "", "metadata": { "description": "The ID of the OAuth 2.0 server." } } }, "variables": { "authenticationSettings": { "oAuth2": { "authorizationServerId": "[parameters('oAuthServerId')]" } } }, "resources": [ { "type": "Microsoft.ApiManagement/service/apis", "name": "[variables('apiName')]", "apiVersion": "2018-06-01-preview", "properties": { "authenticationSettings": "[if(equals(parameters('oAuthServerId'),''), json('null'), variables('authenticationSettings'))]" } } ] }
A couple of other tips I’ve found useful for ARM template writing.
- Always test the ARM templates from Powershell on your local machine, rather than commiting and pushing the changes to Azure DevOps and waiting for DevOps to run your pipeline and eventually fail. As with all development, you’ll be much more efficient if you can shorten your feedback loop.
- Install the latest Powershell
- Install the latest Azure Powershell
- Connect to Azure at the Powershell command line with
Connect-AzAccount
- Connect to your “Development” subscription with something like
Set-AzContext -SubscriptionId f2b1b88a-xxxx-xxxx-a9e1-99a96d8b95f4
- Create a parameters file for testing your ARM template locally
- Validate your ARM template with
Test-AzResourceGroupDeployment
- Run your ARM template with
New-AzResourceGroupDeployment
- If you can’t find the name or the property of a resource, check out the (still in Preview after many years) site https://resources.azure.com/