Get resolved ARM template after failed deployment - azure

Is it possible to get the ARM template as it was during runtime in the Azure Portal with the variables and parameters resolved?
Example below:
AzureDeploy.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environment": {
"type": "string",
"defaultValue": "dev",
},
"storageSKU": {
"type": "string",
"defaultValue": "Standard_LRS",
"allowedValues": [
"Standard_LRS",
"Standard_GRS",
"Standard_RAGRS",
"Standard_ZRS",
"Premium_LRS",
"Premium_ZRS",
"Standard_GZRS",
"Standard_RAGZRS"
]
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
}
},
"variables": {
"storageAccountName": "[concat('companyname',parameters('environment'),'sa01'))]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-04-01",
"name": "[variables('storageName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('storageSKU')]"
},
"kind": "StorageV2",
"properties": {
"supportsHttpsTrafficOnly": true
}
}
]
}
AzureDeploy.parameters.json
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environment": {
"value": "dev"
}
}
}
If this deployment was to fail on something such as the name or the SKU, would I be able to access the portal or somehow see how these values were resolved when the script was ran?
The deployment happens in a CD pipeline in AzureDevops and I have control of the variable groups etc. so I know what is being passed in but not how it resolves. In a more complex template, I have an error claiming an Id is not set on a Logic App API connection but I cannot tell if the error is due to the variable I am using in the concat function or if the value is genuinely incorrect (resolving okay according to data passed in).
If anyone is familiar with troubleshooting these through the deployments blade in Azure then you may have some tips on how to see a more detailed view.
Thanks,
Edit:
The code below triggers Intellisense in Visual Studio 2019 but has been confirmed working during deployment. No warnings in VS Code as per comments. Majority of code omitted for brevity.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environment": {
"type": "string",
"defaultValue": "dev"
},
"increment": {
"type": "string",
"defaultValue": "01"
},
"keyvaultName": {
"type": "string",
"defaultValue": "randomKeyVaultName",
"metadata": {
"description": "Keyvault Name for deployment"
}
}
},
"variables": {
"uniqueKeyVaultName": "[parameters('keyvaultName')]"
},
"resources": [
{
"type": "Microsoft.KeyVault/vaults/secrets",
"apiVersion": "2016-10-01",
"name": "[concat(variables('uniqueKeyVaultName'), '/407045A0-1B78-47B5-9090-59C0AE9A96F6')]",
"location": "northeurope",
"dependsOn": [
"[resourceId('Microsoft.Resources/deployments', 'cosmosdb_linkedtemplate')]"
],
"properties": {
"contentType": "Graph",
"value": "[concat('{''D'': ''DatabaseName'', ''U'': ''https://randomcosmosdb-',parameters('environment'),'-cdb-',parameters('increment'),'.documents.azure.com'', ''C'': ''CollectionName'', ''K'': ''',reference('cosmosdb_linkedtemplate').outputs.accountKey.value,'''}')]",
"attributes": {
"enabled": true
}
}
}
],
"outputs": {}
}

If you want to see the evaluated template there are a few things you can do to get it without deploying:
1) call the /validate api: https://learn.microsoft.com/en-us/rest/api/resources/deployments/validate -- but you need to use an older apiVersion at the moment (e.g. 2017-05-01)... the response will contain the fully evaluated template. If you have an older version of PowerShell or the CLI, you can see the response from the rest API by using the -debug switch. But keep in mind, the more recent versions of PS/CLI will use a newer apiVersion and those don't return the full template (at this time).
2) The /whatif api will also return evaluated JSON but there's a bit more to wade through if all you're after is the evaluated template: https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/template-deploy-what-if
Tha help?

Related

How can I set dependencies on child resources in nested ARM template?

I am trying to use nested templates to deploy a resource group and multiple resources within it on subscription level.
Microsoft documentation has an example of deploying resource group and storage account that I'm trying to follow. I am trying to create another inner level of dependency between a Storage Account resource and a Container resource. That is, the container should only be deployed after the deployment of the storage account is finished. Here is simplified version of my template:
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"rgName": {
"type": "string"
},
"rgLocation": {
"type": "string"
},
"storagePrefix": {
"type": "string",
"maxLength": 11
},
"containerName": {
"type": "string"
}
},
"variables": {
"storageName": "[concat(parameters('storagePrefix'), uniqueString(subscription().id, parameters('rgName')))]"
},
"resources": [
{
"type": "Microsoft.Resources/resourceGroups",
"apiVersion": "2021-04-01",
"name": "[parameters('rgName')]",
"location": "[parameters('rgLocation')]",
"properties": {}
},
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2021-04-01",
"name": "storageDeployment",
"resourceGroup": "[parameters('rgName')]",
"dependsOn": [
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('rgName'))]"
],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-04-01",
"name": "[variables('storageName')]",
"location": "[parameters('rgLocation')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2"
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2021-06-01",
"name": "[format('{0}/default/{1}', variables('storageName'), parameters('containerName'))]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageName'))]"
]
}
],
"outputs": {}
}
}
}
],
"outputs": {}
}
When I try to deploy this template using PowerShell script New-AzSubscriptionDeployment, I get the following error:
| InvalidTemplate - Long running operation failed with status 'Failed'. Additional Info:'Deployment template validation failed: 'The resource 'Microsoft.Storage/storageAccounts/myStorageAccount' is not defined in the template. Please see https://aka.ms/arm-template for usage details.'.'
I kind of know it has to do with the dependsOn part of the container resource. But how can I resolve this problem?
EDIT: The selected answer solves the problem with dependencies, however the issue still persists in cases where a value needs to be called using concat or listKeys expressions. Here's an example where setting the value for AzureWebJobsStorage throws an error in a nested template:
{
"type": "Microsoft.Web/sites",
[ ... ]
"dependsOn": [
"[variables('hostingPlanName')]",
"[variables('functionAppStorageAccountName')]"
],
"properties": {
"serverFarmId": "[variables('hostingPlanName')]",
"siteConfig": {
"appSettings": [
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('functionAppstorageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('functionAppstorageAccountName')), '2021-04-01').keys[0].value)]"
}
[ ... ]
]
}
The value for AzureWebJobsStorage causes the deployment to fail with the following error:
`Status Message: The Resource 'Microsoft.Storage/storageAccounts/stfuncaedotestfeb16g' under resource group '<null>' was not found. For more details
| please go to https://aka.ms/ARMResourceNotFoundFix (Code:ResourceNotFound) CorrelationId: 55942377-6d0f-40ec-9733-33b9c3ea13de
I tried being more verbose by using resource group name (and then subscription ID), but that didn't solve the problem.
You should be able to use:
"dependsOn": [
"[variables('storageName')]"
]
Note that will only work if there is no other resource in the template with the same name - otherwise you have to manually construct the full resourceId, like:
[format('{0}/resourceGroups/{1}/providers/Microsoft.Storage/storageAccounts/{2}', subscription().id, parameters('rgName'), variables('storageName'))]
The latter form will always work, just a bit more verbose.
A bit more detail is that the resourceId function doesn't work as you would expect at subscription scope.

ARM, how to make parameter file dependent on other parameter

I'm trying to make my parameter file a bit smarter but for the life of me can't figure out how to do so. I have a parameters.json file with 2 params: env & commonTags. env takes a string from my DevOps pipeline, and I need this parameter to fill a value in the commonTags-object parameter. See code snippet below:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"env": {
},
"location": {
"value": "westeurope"
},
"commonTags": {
"value": {
"contact":"dr#balloon.com",
"costcenter": "99999",
"env": "[parameters('env')]",
"criticality": "[parameters('env') == 'prd' ? 'high', 'low']"
}
}
}
}
The only other option I see is to set the env-specific parameters in the template file. Either by merging with the existing parameters or by setting the value of commonTags in the template file entirely. But I'd rather keep my template files free of parameter values and have these all located in a central parameter file.
Can anybody point me in the right direction? I can't seem to find anything online.
Many thanks!
You cannot nest parameters as you are trying to do here.
Instead, you can make use of conditional directly in your main arm template coupled with inline parameters. Let's take a simple Storage Account as an example.
azuredeploy.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string"
},
"env": {
"type": "string"
},
"costcenter": {
"type": "string"
},
"contact": {
"type": "string"
},
"storageAccountType": {
"type": "string",
"allowedValues": [
"Standard_LRS",
"Standard_GRS",
"Standard_ZRS",
"Premium_LRS"
],
"metadata": {
"description": "Storage Account type"
}
}
},
"variables": {
"storageAccountName": "[concat('store', uniquestring(resourceGroup().id))]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[variables('storageAccountName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('storageAccountType')]"
},
"tags": {
"contact": "[parameters('contact')]",
"costcenter": "[parameters('costcenter')]",
"env": "[parameters('env')]",
"criticality": "[if(equals(parameters('env'),'prd'),'high','low')]"
},
"kind": "StorageV2"
}
]
}
azuredeploy.parameters.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountType": {
"value": "Standard_LRS"
},
"costcenter": {
"value": "99999"
},
"contact": {
"value": "dr#balloon.com"
},
"location": {
"value": "westeurope"
}
}
}
Note that the tag names remain static - there are ways to pass them inline instead/as well - see this answer.
From these, you can then use Az CLI or Powershell to deploy your template, the parameters kept in a single place and the dynamically provide the remaining one like env.
Example using Powershell :
New-AzResourceGroupDeployment -Name $deploymentName -ResourceGroupName $rgName -TemplateFile .\azuredeploy.json -TemplateParameterFile .\azuredeploy.parameters.json -env $env

Azure arm output the IPs of an app service

I am using ARM template in azure to get the possibleOutboundIpAddresses from an app service and use the same to create allow firewall rule in mysql server
Below is the parameter I am using to fetch the ips
"parameters": {
"webAppOutboundIpAddresses": {
"value": "[split(reference(concat('Microsoft.Web/sites/',parameters('wpsitename'))).properties.possibleOutboundIpAddresses,',')]"
and below is the piece of code using in the linked template.
"type": "Microsoft.DBforMySQL/servers/firewallRules",
"apiVersion": "2017-12-01",
"name": "[concat(parameters('sqlServerName'), '/Allow WebApp Outbound IP ',copyIndex('webAppOutboundIPAddressesCopy'))]",
"properties": {
"startIpAddress": "[parameters('webAppOutboundIpAddresses')[copyIndex('webAppOutboundIPAddressesCopy')]]",
"endIpAddress": "[parameters('webAppOutboundIpAddresses')[copyIndex('webAppOutboundIPAddressesCopy')]]"
},
"copy": {
"name": "webAppOutboundIPAddressesCopy",
"count": "[length(parameters('webAppOutboundIpAddresses'))]"
But somehow the pipeline is failing and I am getting an invalid parameter error
InvalidParameterValue",
"message": "Invalid value given for parameter '{0}'. Specify a valid parameter value."
One more thing I noticed is the output IPs are showing inside square brackets like ["192.168.1.2"]
Can someone please shed some light on this? Thanks
If the resource is in the same template, you must use the reference() function and pass it the resource id or only the name:
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"appServiceName": {
"type": "string",
"minLength": 1,
"metadata": {
"description": "Specifies the name of the Azure App Service"
}
},
"appServicePlanName": {
"type": "string",
"minLength": 1
}
},
"variables": {
},
"resources": [
{
"apiVersion": "2015-08-01",
"name": "[parameters('appServiceName')]",
"type": "Microsoft.Web/sites",
"kind": "app",
"location": "[resourceGroup().location]",
"dependsOn": [],
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]",
"clientAffinityEnabled": false
},
"resources": [],
}
],
"outputs": {
"appServiceName": {
"type": "string",
"value": "reference(parameters('appServiceName'), '2016-03-01', 'Full').properties.inboundIpAddress"
},
"ipAddress": {
"type": "string",
"value": "whatingodsnamegoeshere"
}
}
}
OR
With resourced id:
reference(resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName')), '2016-03-01', 'Full').properties.inboundIpAddress
For the outbound ip addresses: refer here provided by AZToso.
"parameters": {
"webAppOutboundIpAddresses": {
"value": "[split(reference(concat('Microsoft.Web/sites/',variables('webAppName'))).possibleOutboundIpAddresses,',')]"
},
We must develop a loop to iterate through the list of potential outbound IP addresses for a Web App so that we can add each one to the SQL firewall. This is possible with ARM templates by utilizing the copy element. We must utilize the reference function to obtain the WebApp's object. However, the reference function has a restriction that prevents you from using it to change the value of the count property in a copy loop.

ARM template throws incorrect segments lengths for array of storage containers types

I am getting Template validation failed: The template resource 'reports' for type 'Microsoft.WindowsAzure.ResourceStack.Frontdoor.Common.Entities.TemplateGenericProperty`1[System.String]' at line '34' and column '79' has incorrect segment lengths. A nested resource type must have identical number of segments as its resource name. A root resource type must have segment length one greater than its resource name. Please see https://aka.ms/arm-template/#resources for usage details. when I make ARM to create containers from the array in parameters file.
Issue line: "type": "Microsoft.Storage/storageAccounts/blobServices/containers",
Here is my ARM template file.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"type": "string",
"metadata": {
"description": "The name of the storage account"
}
},
"storageContaners": {
"type": "string",
"metadata": {
"description": "The name of the blob containers"
}
}
},
"functions": [],
"variables": {
},
"resources": [
{
"name": "[parameters('storageAccountName')]",
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-04-01",
"location": "[resourceGroup().location]",
"kind": "StorageV2",
"sku": {
"name": "Standard_LRS",
"tier": "Standard"
}
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2021-04-01",
"name": "[parameters('storageContaners')]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
],
"properties": {
"publicAccess": "Blob"
}
}
],
"outputs": {}
}
Here is my ARM parameters file.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"value": "mystorageaccount"
},
"storageContaners": {
"value": "reports"
}
}
}
I have tried changing name to different types but no luck.
Can anybody please help me to figure it out the cause?
The name parameters under the nested resources must be one level less than the type.
Here type has 4 level(separated by 3 / ). So name must have 3 level (separated by 2 /).
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"name": "[concat(parameters('storageAccountName'), '/default/', parameters('storageContaners')]",
This applies when having nested resources under parent resource.

Azure ARM role assignment for System Assigned Managed Identity fails the first run

My goal is to deploy a logic app with a system managed identity and a role assignment for that identity. Preferably, this is done in one ARM template.
I have a setup that fails the first run, but succeeds successive runs. Correct me if I'm wrong, but I think that the reason for this is that the deployment of the role assignment happens before the managed identity of the logic app is "ready", hence the following error I get the first time that I deploy the template. I don't get this error the second time I deploy the template, probably because the Identity already exists at that time.
{
"code": "DeploymentFailed",
"message": "At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.",
"details": [
{
"code": "PrincipalNotFound",
"message": "Principal *** does not exist in the directory ***."
}
]
}
My template (removed logic app definition for brevity). In this case the identity of the logic app requires access to a storage account which is located in another resource group.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"logicAppName": {
"type": "string"
},
"storageAccountResourceGroup": {
"type": "String"
},
"storageAccountName": {
"type": "String"
}
},
"variables": {
"logicAppResourceId": "[resourceId('Microsoft.Logic/workflows', parameters('logicAppName'))]",
"Storage Blob Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]"
},
"resources": [
{
"type": "Microsoft.Logic/workflows",
"apiVersion": "2017-07-01",
"name": "[parameters('logicAppName')]",
"location": "[resourceGroup().location]",
"identity": {
"type": "SystemAssigned"
},
"properties": {
"state": "Enabled",
"definition": {
...
}
}
},
{
"type": "Microsoft.Resources/deployments",
"name": "[concat('RoleAssignment-', parameters('logicAppName'))]",
"apiVersion": "2020-06-01",
"resourceGroup": "[parameters('storageAccountResourceGroup')]",
"subscriptionId": "[subscription().subscriptionId]",
"dependsOn": [
"[parameters('logicAppName')]"
],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"apiVersion": "2018-09-01-preview",
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
"name": "[concat(parameters('storageAccountName'), '/Microsoft.Authorization/', guid(subscription().subscriptionId, parameters('logicAppName')))]",
"properties": {
"roleDefinitionId": "[variables('Storage Blob Data Contributor')]",
"principalId": "[reference(variables('logicAppResourceId'), '2019-05-01', 'Full').identity.principalId]"
}
}
]
}
}
}
]
}
As you can see in the template, I added a dependsOn on the logic app itself. However that doesn't seem to be sufficient.
Does anyone have a solution for this?
Thank you!
I found the answer here: https://learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments-template#new-service-principal
Deployment works consistently after adding "principalType": "ServicePrincipal"

Resources