Skip to main content Explore View all products (200+) Microsoft Foundry Azure Copilot GitHub Copilot Azure Kubernetes Service (AKS) Azure Cosmos DB Azure Database for PostgreSQL Azure Arc Microsoft Fabric Linux virtual machines in Azure Foundry Models Foundry Agent Service Foundry IQ Foundry Tools Foundry Control Plane Observability in Foundry Control Plane Azure OpenAI in Foundry Models Azure Speech in Foundry Tools Azure Machine Learning View all databases Azure Cosmos DB Azure DocumentDB Azure SQL Azure Database for PostgreSQL Azure Managed Redis Microsoft Fabric Azure Databricks Linux virtual machines in Azure Windows Server on Azure Azure Functions Azure Virtual Machine Scale Sets Azure API Management Azure Container Apps Azure Kubernetes Service (AKS) Azure Kubernetes Fleet Manager Azure Container Registry Azure Red Hat OpenShift Azure Container Instances Azure Container Storage Azure Arc Azure Local Microsoft Defender for Cloud Azure Monitor Microsoft Sentinel Azure Migrate View all solutions (40+) Cloud solutions for small and medium businesses Cloud migration and modernization center Data analytics for AI Azure Databases AI apps and agents Microsoft Marketplace Microsoft Sovereign Cloud AI apps and agents Responsible AI with Azure AI Infrastructure Data analytics for AI Machine learning operations (MLOps) Low-code application development on Azure Integration Services Serverless computing DevOps Migration and modernization center .NET apps migration Databases on Azure Linux on Azure Oracle on Azure SAP on the Microsoft Cloud Adaptive cloud High-performance computing (HPC) Infrastructure as a service (IaaS) Resiliency Azure Essentials Frontier Accelerate for Azure FinOps on Azure Microsoft Marketplace Azure pricing overview Create an Azure account Free Azure services Flexible purchase options Pricing calculator FinOps on Azure Maximize ROI from AI Azure savings plans Azure reservations Azure Hybrid Benefit Virtual Machines Azure SQL Microsoft Foundry Microsoft Fabric Azure Kubernetes Service (AKS) Microsoft Defender for Cloud View more Software Development Companies Microsoft Marketplace Find a partner Resources for Azure partners Get started with Azure Customer stories Analyst reports, white papers, and e-books Videos Learn more about cloud computing Documentation Explore Azure portal Developer resources Quickstart templates Resources for startups Developer community Students Azure for partners Blog Events and Webinars Learn Support Contact Sales Get started with Azure Sign in

In this blog post, I will walk you through some of the new capabilities we have in our template language expressions for Azure Resource Manager templates.

Background

A common ask from customers is, “how can we use conditions in our ARM templates, so if a user selects parameter A, then resource A is created. If not, resource B should be created?”.  The only way you could achieve this, would be by using nested templates and have a mainTemplate to manipulate the deployment graph.

A common pattern can be seen below, where the user will select either new or existing.

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "newOrExisting": {
            "type": "String",
            "allowedValues": [
                "new",
                "existing"
            ]
        }
    },
    "variables": {
        "templatelink": "[concat('https://raw.githubusercontent.com/krnese/ARM/master/', concat(parameters('newOrExisting'),'StorageAccount.json'))]"
    },
    "resources": [
        {
            "apiVersion": "2017-05-10",
            "name": "nestedTemplate",
            "type": "Microsoft.Resources/deployments",
            "properties": {
                "mode": "incremental",
                "templateLink": {
                    "uri": "[variables('templatelink')]",
                    "contentVersion": "1.0.0.0"
                },
                ...

In the variables declaration, the link to the template is constructed based on the parameter input.

The example shows that the URI to the template will either be

https://raw.githubusercontent.com/krnese/ARM/master/newStorageAccount.json

or

https://raw.githubusercontent.com/krnese/ARM/master/existingStorageAccount.json

This approach does work and you will have a successful deployment of the template, regardless whether the user selected new or existing in the parameter selection. However, the approach of having nested templates could indeed lead to many templates over time. Where some of the templates ended up being completely empty just to avoid having a failure in the deployment graph. Further, the complexity would grow as you would normally use other resource types – besides a storage account, and potentially have multiple conditions involved.

Another technique that also was frequently used, was to manipulate complex variables based on input parameters to determine certain properties for a resource. The example below shows how ARM could navigate within the complex variables to either create Linux or Windows virtual machines, based on the parameter platform, which allowed Windows or Linux as input.

"osType": "[variables(concat('osType',parameters('platform')))]",        
        "osTypeWindows": {
            "imageOffer": "WindowsServer",
            "imageSku": "2016-Datacenter",
            "imagepublisher": "MicrosoftWindowsServer"
        },
        "osTypeLinux": {
            "imageOffer": "UbuntuServer",
            "imageSku": "12.04.5-LTS",
            "imagepublisher": "Canonical"
        },

Needless to say, we have heard from customers that this should be simplified, so that the template language should support and handle conditions more easily.

Introducing support for conditions

As we always appreciate the feedback from our customers, we are glad to remind you (announced at //BUILD this year) we have added support for conditions on resources, as well as many more capabilities. These include logical and comparison functions that can be used in the template language when handling conditions.

I will show a practical example of how the new capabilities can be leveraged in ARM templates. A typical example today, where you want your users to select whether a virtual machine should be Windows or Linux based, would require a complex variable being manipulated as demonstrated earlier in this blog post. In addition, if your user needs to decide if the virtual machine should go into production or not, by having an availability set as an optional resource would require nested templates that either deployed the resource or are empty.

In total, this would require at least 3 templates (remember that 2 of them would be nested templates).

By using conditions and functions, we can now accomplish this with a single template! Not to mention a lot less JSON. Let’s start by walking through some of the parameters we are using in the sample template, and explain the steps we have taken.

"parameters": {
        "vmNamePrefix": {
            "type": "string",
            "defaultValue": "VM",
            "metadata": {
                "description": "Assign a prefix for the VM you will create."
            }
        },
        "production": {
            "type": "string",
            "allowedValues": [
                "Yes",
                "No"
            ],
            "metadata": {
                "description": "Select whether the VM should be in production or not."
            }
        },
        "platform": {
            "type": "string",
            "allowedValues": [
                "WinSrv",
                "Linux"
            ],
            "metadata": {
                "description": "Select the OS type to deploy."
            }
        },
        "pwdOrssh": {
            "type": "securestring",
            "metadata": {
                "description": "If Windows, specify the password for the OS username. If Linux, provide the SSH."
            }
        },

Besides assigning the virtual machine a prefix, we also have parameters for production and platform.

For production, the user can simply select yes or no. For yes, we want to ensure that the virtual machine being created gets associated with an availability set, since this resource needs to be in place prior to the virtual machine creation process. To support this, we have added the following resource to the template:

{
            "condition": "[equals(parameters('production'), 'Yes')]",
            "type": "Microsoft.Compute/availabilitySets",
            "apiVersion": "2017-03-30",
            "name": "[variables('availabilitySetName')]",
            "location": "[resourceGroup().location]",
            "properties": {
                "platformFaultDomainCount": 2,
                "platformUpdateDomainCount": 3
            },
            "sku": {
                "name": "Aligned"
            }
        },

Note the condition property. We are using a comparison function, equals(arg1, arg2), which will check whether two values equal each other. In this case, if the parameter production equals yes, ARM will process this resource during runtime. If not true (No being selected), it will not be provisioned.

For the virtual machine resource in our template, we have declared the reference to the availability set based on the condition we introduced.

"properties": {
                "availabilitySet": "[if(equals(parameters('production'), 'Yes'), variables('availabilitySetId'), json('null'))]",

We’re using if() which is one of our logical functions. This function takes three arguments. The first is the condition (Boolean), which is the value to check whether it is true or false. The second argument will be the true value, followed by the third argument which is false. The net result of this, would be to associate the virtual machine. When the user selects yes for the production parameter, then the virtual machine will get associated with the availability set declared in our template, which has already been created due to the condition. If the user selects no, the availability set won’t be created, hence there won’t be any association from the virtual machine resource.

We also have a parameter platform which decides if we are creating a Windows or Linux virtual machine. To simplify the language expression throughout the template, we added the values for Linux and Windows into our variables section.

"windowsOffer": "WindowsServer",
        "windowsSku": "2016-Datacenter",
        "windowsPublisher": "MicrosoftWindowsServer",
        "linuxOffer": "UbuntuServer",
        "linuxSku": "12.04.5-LTS",
        "linuxPublisher": "Canonical",

On the virtual machine resource, more specifically within the storageProfile section where we distinguish between the image being used, we are referring to our variables for Windows or Linux.

"storageProfile": {
                    "imageReference": {
                        "publisher": "[if(equals(parameters('platform'), 'WinSrv'), variables('windowsPublisher'), variables('linuxPublisher'))]",
                        "offer": "[if(equals(parameters('platform'), 'WinSrv'), variables('windowsOffer'), variables('linuxOffer'))]",
                        "version": "latest",
                        "sku": "[if(equals(parameters('platform'), 'WinSrv'), variables('windowsSku'), variables('linuxSku'))]"
                    },

If a user selects WinSrv for the platform parameter, we will grab the values for the variables pointing to the Windows image. If not and Linux is selected, we refer to those variables instead. The result would either be a virtual machine using Windows Server 2016 or Ubuntu.

Last but not least, we also have our output section in the template, which will provide the user with some instructions based on their selection.

"outputs": {
        "vmEndpoint": {
            "type": "string",
            "value": "[reference(concat(variables('pNicName'))).dnsSettings.fqdn]"
        },
        "platform": {
            "type": "string",
            "value": "[parameters('platform')]"
        },
        "connectionInfo": {
            "type": "string",
            "value": "[if(equals(parameters('platform'), 'WinSrv'), 'Use RDP to connect to the VM', 'Use SSH to connect to the VM')]"
        }
    }

If the user deploys a Windows Server, they will see the following output:

text

If the user deploys Linux, they will see this:

text

Summary

We believe, by introducing support for conditions on the resources you declare in your templates and enhancements to the template language itself with logical and comparison functions, we have set the scene for much simpler templates. You can now move away from the workarounds you previously had when implementing conditions, and should hopefully achieve much more flexibility while deploying complex apps, resources, and topologies in Azure.

The full template is available here.

WE ARE MICROSOFT

Explore Microsoft Foundry

The future of AI starts here. Envision your next great AI app with the latest technologies. Get started with Azure.