Search code examples
azureazure-rm-template

How to deploy a Arm template with Linked template that contains a resource group and resources


I have a 'grandparent' master template that deploys another linked 'parent' template 4 times, each time with different group name (I've only shown the first resource):

{
    "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "resourcegroupName": {
            "type": "string",
            "metadata": {
                "description": "The name given to the group and all resources it contains by default"
            }
        },
        "resourceGroupLocation": {
            "type": "string",
            "metadata": {
                "description": "The Location of the resource group"
            }
        },
        "templateFolderUri": {
            "type": "string",
            "metadata": {
                "description": "The URI of the template component folder"
            }
        }
    },
    "functions": [],
    "variables": {},
    "resources": [
        {
            "name": "[concat(parameters('resourceGroupName'), 'Dev')]",
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2021-04-01",
            "location": "[parameters('resourceGroupLocation')]",
            "properties": {
                "mode": "Incremental",
                "templateLink": {
                    "uri": "[concat(parameters('templateFolderUri'), '/completeNovaRssDeploy.json')]",
                    "contentVersion": "1.0.0.0"
                },
                "parameters": {
                    "resourcegroupName": "[concat(parameters('resourcegroupName'), 'Dev')]",
                    "resourceGroupLocation": "[parameters('resourceGroupLocation')]",
                    "templateFolderUri": "[concat(parameters('resourcegroupName'), '/Components')]"
                }
            }
        },

        <resource repeated 3 times>

    ],
    "outputs": {}
}

The 'parent' template, also links other 'child' templates and looks like so:

{
    "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "resourcegroupName": {
            "type": "string",
            "metadata": {
                "description": "The name given to the group and all resources it contains by default"
            }
        },
        "resourceGroupLocation": {
            "type": "string",
            "metadata": {
                "description": "The Location of the resource group"
            }
        },
        "templateFolderUri": {
            "type": "string",
            "metadata": {
                "description": "The URI of the template component folder"
            }
        }
    },
    "functions": [],
    "variables": {},
    "resources": [
        {
            "comments": "Template for creating the resource group",
            "name": "[parameters('resourcegroupName')]",
            "type": "Microsoft.Resources/resourceGroups",
            "apiVersion": "2021-04-01",
            "location": "[parameters('resourceGroupLocation')]",
            "properties": {}
            
        
        },
        {
            "name": "[parameters('resourceGroupName')]",
            "type": "Microsoft.Resources/deployments",
            "resourceGroup": "[parameters('resourcegroupName')]",
            "apiVersion": "2021-04-01",
            "properties": {
                "mode": "Incremental",
                "templateLink": {
                    "uri": "[concat(parameters('templateFolderUri'), '/servicePlanCreator.json')]",
                    "contentVersion": "1.0.0.0"
                }
            }, 
            "dependsOn": [
                "[resourceId('Microsoft.Resources/resourceGroups/', parameters('resourceGroupName'))]"
            ]
        },
        {
            "name": "[concat(parameters('resourceGroupName'), 'Storage')]",
            "type": "Microsoft.Resources/deployments",
            "resourceGroup": "[parameters('resourcegroupName')]",
            "apiVersion": "2021-04-01",
            "properties": {
                "mode": "Incremental",
                "templateLink": {
                    "uri": "[concat(parameters('templateFolderUri'), '/storageAccountTemplate.json')]",
                    "contentVersion": "1.0.0.0"
                },
                "parameters": {}
            },
            "dependsOn": [
                "[resourceId('Microsoft.Resources/resourceGroups/', parameters('resourceGroupName'))]"
            ]
        },
        {
            "name": "[concat(parameters('resourceGroupName'), 'Vault')]",
            "type": "Microsoft.Resources/deployments",
            "resourceGroup": "[parameters('resourcegroupName')]",
            "apiVersion": "2021-04-01",
            "properties": {
                "mode": "Incremental",
                "templateLink": {
                    "uri": "[concat(parameters('templateFolderUri'), '/keyVaultCreator.json')]",
                    "contentVersion": "1.0.0.0"
                },
                "parameters": {}
            },
            "dependsOn": [
                "[resourceId('Microsoft.Resources/resourceGroups/', parameters('resourceGroupName'))]"
            ]
        },
        {
            "name": "[concat(parameters('resourceGroupName'), 'App')]",
            "type": "Microsoft.Resources/deployments",
            "resourceGroup": "[parameters('resourcegroupName')]",
            "apiVersion": "2021-04-01",
            "properties": {
                "mode": "Incremental",
                "templateLink": {
                    "uri": "[concat(parameters('templateFolderUri'), '/dualSlotWebApp.json')]",
                    "contentVersion": "1.0.0.0"
                },
                "parameters": {}
            },
            "dependsOn": [
                "[resourceId('Microsoft.Resources/resourceGroups/', parameters('resourceGroupName'))]",
                "[resourceId('Microsoft.Resources/deployments', parameters('resourceGroupName'))]"
            ]
        }
    ],
    "outputs": {}
}

The 'parent' template works when ran on its own, the parent template does not. I get an error for the type of each resource in the grandparent template:

##[error]Multiple error occurred: BadRequest,BadRequest,BadRequest,BadRequest. Please see details.
##[error]Details:
##[error]InvalidTemplate: The nested deployment 'NovaArmTestDev' failed validation: 'Error converting value "NovaArmTestDev" to type 'Azure.Deployments.Core.Definitions.DeploymentParameterDefinition'. Path 'properties.parameters.resourcegroupName', line 1, position 476.'.
##[error]InvalidTemplate: The nested deployment 'NovaArmTestTest' failed validation: 'Error converting value "NovaArmTestTest" to type 'Azure.Deployments.Core.Definitions.DeploymentParameterDefinition'. Path 'properties.parameters.resourcegroupName', line 1, position 479.'.
##[error]InvalidTemplate: The nested deployment 'NovaArmTestCat' failed validation: 'Error converting value "NovaArmTestCat" to type 'Azure.Deployments.Core.Definitions.DeploymentParameterDefinition'. Path 'properties.parameters.resourcegroupName', line 1, position 476.'.
##[error]InvalidTemplate: The nested deployment 'NovaArmTest' failed validation: 'Error converting value "NovaArmTest" to type 'Azure.Deployments.Core.Definitions.DeploymentParameterDefinition'. Path 'properties.parameters.resourcegroupName', line 1, position 467.'.
##[warning]Validation errors were found in the Azure Resource Manager template. This can potentially cause template deployment to fail. Task failed while creating or updating the template deployment.. Please follow https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/template-syntax
Starting Deployment.
Deployment name is completeNovaRssDeploy-20220527-130154-2824
There were errors in your deployment. Error code: DeploymentFailed.
##[error]At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.
##[error]Details:
##[error]InvalidRequestContent: The request content was invalid and could not be deserialized: 'Error converting value "NovaArmTest" to type 'Azure.Deployments.Core.Definitions.DeploymentParameterDefinition'. Path 'properties.parameters.resourcegroupName', line 1, position 266.'.
##[error]InvalidRequestContent: The request content was invalid and could not be deserialized: 'Error converting value "NovaArmTestCat" to type 'Azure.Deployments.Core.Definitions.DeploymentParameterDefinition'. Path 'properties.parameters.resourcegroupName', line 1, position 269.'.
##[error]InvalidRequestContent: The request content was invalid and could not be deserialized: 'Error converting value "NovaArmTestDev" to type 'Azure.Deployments.Core.Definitions.DeploymentParameterDefinition'. Path 'properties.parameters.resourcegroupName', line 1, position 269.'.
##[error]InvalidRequestContent: The request content was invalid and could not be deserialized: 'Error converting value "NovaArmTestTest" to type 'Azure.Deployments.Core.Definitions.DeploymentParameterDefinition'. Path 'properties.parameters.resourcegroupName', line 1, position 270.'.
##[error]Check out the troubleshooting guide to see if your issue is addressed: https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/deploy/azure-resource-group-deployment?view=azure-devops#troubleshooting
##[error]Task failed while creating or updating the template deployment.

I believe I understand why this is happening as in the 'grandparent' template I am linking the 'parent' as "type": "Microsoft.Resources/deployments" when the 'parent' template deploys a resource group and operates on the subscription scope. I also tried setting the type to "type": "Microsoft.Resources/resourceGroups" but that also did not work. I believe this can be fixed by moving the resource group from the 'parent' template to the 'grandparent' but I am wondering if there is a resource type that can more easily accommodate both these types in the same linked template.


Solution

  • When passing parameters to the linked templates, the parameters need an additional property added called "value": ....

    Fixing a portion of the example provided, the result would look like

    ...
            "parameters": {
              "resourcegroupName": {
                "value": "[concat(parameters('resourcegroupName'), 'Dev')]"
              },
              "resourceGroupLocation": {
                "value": "[parameters('resourceGroupLocation')]"
              },
              "templateFolderUri": {
                "value": "[concat(parameters('templateFolderUri'), '/Components')]"
              }
            }
    ...
    

    Reference: https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/linked-templates?tabs=azure-powershell#:~:text=To%20pass%20parameter%20values%20inline