18

How do i identify the azure resource is exists or not in the ARM templates by the resource type and identifier

abatishchev
  • 98,240
  • 88
  • 296
  • 433
sacratees
  • 225
  • 1
  • 2
  • 6
  • ARM templates are declarative in nature (so in theory this isn't needed) - what's your scenario, there may be another way to do what you're thinking... – bmoore-msft Jul 08 '19 at 13:04
  • https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/conditional-resource-deployment – RClemens Mar 22 '23 at 00:12

4 Answers4

17

It is actually kind of possible. You can use resource group tags to mark a current deployed version and skip deployment if the tag is set. All this could be achieved via linked template.
Note that we don't check for resource existence per se but we still allow writing ARM template that could contain one time initialization templates. The last will restore the resource if resource group was deleted and resources were lost (given that you created the resource group again). You can extend this to support per-resource tags which will be more useful in some cases.

The template that starts the deployment may look like this:

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "DeploymentTemplateLink": {
            "type": "string"
        },
        "DeploymentVersion": {
            "defaultValue": 1,
            "type": "int"
        }
    },
    "variables": {
      "rgWithDefaultVersion": {
          "tags": {
             "Version": "0"
          }
      }
    },
    "resources": [
        {
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "name": "DeploymentTemplate",
            "condition": "[less(int(union(variables('rgWithDefaultVersion'), resourceGroup()).tags['Version']), parameters('DeploymentVersion'))]",
            "properties": {
                "mode": "Incremental",
                "templateLink": {
                    "uri": "[parameters('DeploymentTemplateLink')]",
                    "contentVersion": "1.0.0.0"
                },
                "parameters": {
                    "DeploymentVersion": {
                        "value": "[parameters('DeploymentVersion')]"
                    }
                }
            }
        }
    ]
}

The linked template's condition looks into tags and returns true only if current version (stored in the tag) is less than the requested one. You don't actually have to maintain versioning: just don't set the DeploymentVersion parameter and it will deploy only for the first time. If you decide to redeploy anyway you have always an option to increase the version, which will cause deployment of the linked template (aka "main deployment").

The main deployment template is on you, but it should contain a tags resource in order to maintain the logic.

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "DeploymentVersion": {
            "defaultValue": 1,
            "type": "int"
        }
    },
    "variables": {},
    "resources": [
        {
            "type": "Microsoft.Resources/tags",
            "name": "default",
            "apiVersion": "2019-10-01",
            "dependsOn": [],
            "properties": {
                "tags": {
                    "Version": "[string(parameters('DeploymentVersion'))]"
                }
            }
        }
    ]
}

Remark for those who didn't understand the union() and rgWithDefaultVersion thing. ARM template deployment will fail if referenced object doesn't contain a property. In our case we have two such properties: 'tags' and 'Version'. 'Tags' will exist only if particular resource group has or ever had tags. 'Version' will exist only after we already wrote it once (in the main deployment). Therefore before we access them we perform union() operation on returned object with a proper default one, ensuring that we can safely access the mentioned properties.

Gustavo Mori
  • 8,319
  • 3
  • 38
  • 52
Bennie Tamir
  • 806
  • 7
  • 8
  • 1
    Very nice, and just what I needed today.. and I wasn't even the one who asked the question. Thanks! – Razoll May 01 '20 at 04:07
  • That's a great workaround, thank you! The trick with `union` is marvelous – Vladislav Jul 15 '20 at 12:52
  • Nice answer Benny! is there a way to PATCH tags? (i.e. `merge`, instead of `replace`) When having multiple resources effected by the state kept as RG tags, it becomes a problem to main the tags without losing some due to some race conditions when running ARM template steps in parallel instead of sequence. – johni Jul 28 '20 at 12:11
  • 1
    Mate, are you relalized the way you did are useless complexity? Are you going to apply the conditions to each resource in ARM templates? Secondary, if the real resourse has configuration drift, it would be never rollbacked, if the tag of version is not changed on that resource. – BMW Apr 26 '21 at 01:42
  • 1
    i mean, this is pretty much what I mentioned in my answer, but (similarly to @BMW) I discourage this approach. templates are meant to not really care about state that is present now. they should just "make it so". you've got git to track previous states. – 4c74356b41 Apr 26 '21 at 15:49
  • While this is exactly what I was looking for, I am wondering if there isn't a simpler way to use tags to control whether we deploy or not. – LFN Jul 06 '23 at 23:09
5

there is no way of doing that in an arm template. you can use some external source (like powershell) to determine that and pass in parameter with appropriate value, alternatively you can use tags to figure that out (have a tag that represents an existence\absence of a resource).

4c74356b41
  • 69,186
  • 6
  • 100
  • 141
2

Resource Manager provides the following functions for getting resource values: Resource functions for Azure Resource Manager templates

You could wrap your template with a piece of powershell\whatever, that would determine if the resource exists, and pass in the parameter value depending on that and use a conditional statement in the template that would decide what to do based on the input (but the input has to come from elsewhere)

-5

I needed a solution to this recently to basically do an incremental update to a SQL server. Since you can't do this; the template will fail with a NameAlreadyExists error. So I needed to check the resource doesn't exist and only create if it doesn't.

Add a "condition" check for the azure resource id exists; don't create if it does.

{
 ...
 "condition": "[empty(resourceId('[resourceGroup().id]', 'Microsoft.SQL/servers', parameters('serverName')))]",
 ...
}

You can do this for any resource type.

user1930735
  • 23
  • 1
  • 3
  • This does not work, because the `resourceId` function does not actually check if a resource exists, it simply returns what the ID would be for the given resource group, type and name. See the docs for more - https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-functions-resource#resourceid – David Gard Sep 05 '19 at 10:04
  • 4
    this answer is straight out wrong. who upvotes this? – 4c74356b41 Oct 16 '19 at 06:45
  • 2
    It should be noted that the reason this approach is wrong is because when a resource ID cannot be found, this results in an error, not an empty string. If an empty string were returned, then it looks like it would work. – The Gilbert Arenas Dagger Apr 27 '20 at 22:30