Updating Azure app gateway rewrite rules with ARM template - azure

I've been learning about how ARM templates work and how there is an incremental mode. Can I do a custom deployment that will update a resource with a template that only includes what I want to update?
What I'm trying to accomplish is to update or add rewrite rules for a specific set in our app gateway
When I try the following I get InvalidTemplateDeployment errors with messages about missing template properties i.e. "0 IP configuration specified for gateway".
For my template I was trying to follow an example here https://learn.microsoft.com/en-us/azure/architecture/guide/azure-resource-manager/advanced-templates/update-resource
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
...
},
"resources": [
{
"apiVersion": "2020-06-01",
"type": "Microsoft.Resources/deployments",
"name": "updateRewriteRules",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.1",
"resources": [
{
"type": "Microsoft.Network/applicationGateways",
"apiVersion": "2020-05-01",
"name": "[parameters('applicationGatewayName')]",
"location": "[parameters('location')]",
"properties": {
"rewriteRuleSets": [
{
"name": "[parameters('rewriteSetName')]",
"properties": {
"rewriteRules": [
{
"ruleSequence": 300,
"conditions": [],
"name": "security-response-headers",
"actionSet": {
"requestHeaderConfigurations": [],
"responseHeaderConfigurations": [
{
"headerName": "Permissions-Policy",
"headerValue": "accelerometers=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()"
}
]
}
}
]
}
}
]
}
}
],
"outputs": {}
}
}
}
],
"outputs": {}
}

Looks like for app gateways you need to deploy the entire template with all its properties to make changes.
Update existing Application Gateway via ARM
Not sure where this is documented though.

Related

Azure resource manager template deployments - deletes the existing resources

When trying to add additional routes to route table in Azure using ARM template, the existing routes are getting removed/deleted. The same behavior is observed when adding new service endpoints for a subnet, post deployment the Route table and NSG are disassociated and the existing serviceend point association is removed.
Should all the resources be explicitly reference in ARM template to avoid this behavior. Is there a way this can achieve without listing/referring all the resources associated.
Below template format ----
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": { },
"functions": [],
"variables": {
"testroutetable1": "rtable1",
"testroutetable2": "rtable2",
"Subnet1": "subnet1",
"Subnet2": "subnet2",
"testvnet": "vnet1"
},
"resources": [
{
"name": "[concat(variables('testvnet'),'/',variables('Subnet1'))]",
"type": "Microsoft.Network/virtualNetworks/subnets",
"apiVersion": "2018-10-01",
"location": "East US",
"properties": {
"addressPrefix": "10.0.0.0/24",
"routeTable": {
"id": "[resourceId('Microsoft.Network/routeTables',variables('testroutetable1'))]"
}
}
},
{
"name": "[variables('testroutetable1')]",
"type": "Microsoft.Network/routeTables",
"location": "West Europe",
"apiVersion": "2019-11-01",
"properties": {
"routes": [
{
"name": "rtable1-to-xxx01",
"properties": {
"addressPrefix": "xxxxx",
"nextHopType": "VirtualAppliance",
"nextHopIpAddress": "xxxxx"
}
},
{
"name": "rtable1-to-xxx02",
"properties": {
"addressPrefix": "xxxxx",
"nextHopType": "VirtualAppliance",
"nextHopIpAddress": "xxxx"
}
}
]
}
},
{
"name": "[concat(variables('testvnet'),'/',variables('Subnet2'))]",
"type": "Microsoft.Network/virtualNetworks/subnets",
"apiVersion": "2018-10-01",
"location": "West Europe",
"properties": {
"addressPrefix": "10.0.2.0/24",
"routeTable": {
"id": "[resourceId('Microsoft.Network/routeTables',variables('testroutetable2'))]"
}
}
},
{
"name": "[variables('testroutetable2')]",
"type": "Microsoft.Network/routeTables",
"location": "east us",
"apiVersion": "2019-11-01",
"properties": {
"routes": [
{
"name": "rtable2-to-yyy01",
"properties": {
"addressPrefix": "xxxxxx",
"nextHopType": "VirtualAppliance",
"nextHopIpAddress": "xxxxx"
}
},
{
"name": "rtable2-to-yyy02",
"properties": {
"addressPrefix": "xxxxx",
"nextHopType": "VirtualAppliance",
"nextHopIpAddress": "xxxxxx"
}
}
]
}
}
],
"outputs": {}
}
If the object's property is of type array then you must provide all of its target value. This applies to security rules on the NSG, routes on the route table, etc.
This is covered by this github issue. For certain resources within a virtual network, if you declare them as either child resources of the virtual network, or as independent resources, when the ARM template is deployed, any existing resources are deleted and then the resources are created again.
However, the ARM template for virtual networks also supports deploying these resources as properties. When deploying using this method, any existing resources will not be deleted on each deployment.
Unfortunately this is a long running issue and shows no sign of being resolved in the near future.
You can add/delete a route in an existent route-table without having to reference the other routes. Simply add the route-table name to the route path like in this example:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.Network/routeTables/routes",
"apiVersion": "2019-06-01",
"name": "RouteTableName/RouteName",
"properties": {
"addressPrefix": "10.0.0.0/8",
"nextHopType": "VnetLocal"
}
}
]
}

How to wait for deploying a resource in another ResourceGroup in Arm template

We want to have two resource group, 1- shared-resource group for shared resources like app service plan, ...
2- another resource-group for other resources like web-app.
We are using nested link template which contains three level:
highest level is main deployment template file which contains everything.
next level is for calling all resource templates for each resource-group, e.g we have two template file in this level one for shared resource-group and two other resource-group, so for-example first file will call some link template to create app service plan, elastic pool and ....
second file will call link templates for deploying web-app, network and ....
last level is our generic resource templates, in this level for each resource we have a template file e.g for web-app we have a generic template which deploy web-app with some parameters.
Now the question is how we can use 'dependson' to create all shared-Resources first after that deploy all other resources?
*please note: shared resources are in different resource-group and other resources are in another resource-group so we have two resource-group which both and all included resources are deploying in main template file.
My main deployment template file is this:
// Include rg-CockpitShared and app specific resources - and share parameters
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-
01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string"
},
"cockpitEnvironment": {
"type": "string"
}
},
"variables": {
"currentTemplateUri": "[deployment().properties.templateLink.uri]",
"rgCockpitSharedUrl": "[concat(uri(variables('currentTemplateUri'), 'rg-CockpitShared/rg-CockpitShared.json'))]",
"rgCockpitSharedParametersUrl": "[concat(uri(variables('currentTemplateUri'), concat('rg-CockpitShared/rg-CockpitShared.parameters.', parameters('cockpitEnvironment'), '.json')))]",
"rgClientCmdbTemplateUrl": "[concat(uri(variables('currentTemplateUri'), 'rg-ClientCmdb/rg-ClientCmdb.json'))]",
"rgClientCmdbParametersUrl": "[concat(uri(variables('currentTemplateUri'), concat('rg-ClientCmdb/rg-ClientCmdb.parameters.', parameters('cockpitEnvironment'), '.json')))]"
},
"resources": [
{
"type": "Microsoft.Resources/deployments",
"location": "[parameters('location')]",
"apiVersion": "2019-10-01",
"name": "CockpitShared",
"tags": {
"devOwner": "aspBuild"
},
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables('rgCockpitSharedUrl')]",
"contentVersion": "1.0.0.0"
},
"parametersLink": {
"uri": "[variables('rgCockpitSharedParametersUrl')]",
"contentVersion": "1.0.0.0"
}
}
},
{
"type": "Microsoft.Resources/deployments",
"location": "[parameters('location')]",
"apiVersion": "2019-10-01",
"name": "otherResources",
"tags": {
"devOwner": "aspBuild"
},
"dependsOn": [
"CockpitShared"
],
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables('rgClientCmdbTemplateUrl')]",
"contentVersion": "1.0.0.0"
},
"parametersLink": {
"uri": "[variables('rgClientCmdbParametersUrl')]",
"contentVersion": "1.0.0.0"
}
}
}
],
"outputs": {
"rgCockpitSharedUrl": {
"type": "string",
"value": "[variables('rgCockpitSharedUrl')]"
},
"rgCockpitSharedParametersUrl": {
"type": "string",
"value": "[variables('rgCockpitSharedParametersUrl')]"
}
}
}
So What I have done here is using dependsOn = "CockpitShared" and expected that first it deploys shared resources and after that start to deploy 'otherResources', but I got error that app-service-plan is not found which means it's not wait to shared resources be deployed and I don't understand why? :(
Finally I found a solution:
I used 'reference' instead of 'dependson'.
So in my shared-Resources arm template I return app service plan name as output and used it from my main deployment file so it wait for finishing shared resources deployment first and after that start to deploy web app ...
for more detail see below code:
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string"
},
"cockpitEnvironment": {
"type": "string"
},
"storageAccountUri": {
"type": "string"
}
},
"variables": {
"rgCockpitSharedUrl": "[concat(uri(parameters('storageAccountUri'), 'rg-CockpitShared/rg-CockpitShared.json'))]",
"rgClientCmdbTemplateUrl": "[concat(uri(parameters('storageAccountUri'), 'ClientCmdb/rg-ClientCmdb/rg-ClientCmdb.json'))]"
},
"functions": [
{
"namespace": "ClientCmdb",
"members": {
"SelectValueByEnv": {
"parameters": [
{
"name": "Environment",
"type": "string"
},
{
"name": "prodValue",
"type": "string"
},
{
"name": "devValue",
"type": "string"
},
{
"name": "testValue",
"type": "string"
}
],
"output": {
"type": "string",
"value": "[if(equals(parameters('Environment'),'prod'),parameters('prodValue'),if(equals(parameters('Environment'),'dev'),parameters('devValue'),parameters('testValue')))]"
}
}
}
}
],
"resources": [
{
"type": "Microsoft.Resources/deployments",
"location": "[parameters('location')]",
"apiVersion": "2019-10-01",
"name": "CockpitShared",
"tags": {
"devOwner": "aspBuild"
},
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables('rgCockpitSharedUrl')]"
},
"parameters": {
"rgName": {
"value": "[ClientCmdb.SelectValueByEnv(parameters('cockpitEnvironment'),'rg-CockpitShared','rg-CockpitShared','rg-CockpitShared')]"
},
"location": {
"value": "[parameters('location')]"
},
"cockpitEnvironment": {
"value": "[parameters('cockpitEnvironment')]"
}
}
}
},
{
"type": "Microsoft.Resources/deployments",
"location": "[parameters('location')]",
"apiVersion": "2019-10-01",
"name": "ClientCmdb",
"tags": {
"devOwner": "aspBuild"
},
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables('rgClientCmdbTemplateUrl')]"
},
"parameters": {
"rgName": {
"value": "[ClientCmdb.SelectValueByEnv(parameters('cockpitEnvironment'),'rg-ClientCmdb','rg-ClientCmdb','rg-ClientCmdb')]"
},
"location": {
"value": "[ClientCmdb.SelectValueByEnv(parameters('cockpitEnvironment'),'northeurope','northeurope','northeurope')]"
},
"cockpitEnvironment": {
"value": "[parameters('cockpitEnvironment')]"
},
"planName": {
// Here__________________________________________________________________________________
"value": "[reference('CockpitShared').outputs.planName.value]"
}
}
}
}
],
}
so basically I pass my app service plan name from shared resource template to main template and main template gets it by 'reference' and pass it to other resources template via parameter.
Regarding to do that I had to use inline params as I was using params in seperated files...
Hope it's useful for someone else :)

Get resolved ARM template after failed deployment

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?

Nested variable and resource with copy not working

I am trying to use nested resources with copy option as mentioned in https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#serial-copy
I have defined "lbApiVersion" inside nested resource variable block but some how the nested variable block is not recognize.
below is the ARM template i am trying and this is just example in actual scenario i want to pass array to arm template and then create multiple group of resources in loop, so in that case i need the nested variable block.
ARM -
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"numberToDeploy": {
"type": "int",
"minValue": 2,
"defaultValue": 2
}
},
"resources": [
{
"apiVersion": "2015-01-01",
"type": "Microsoft.Resources/deployments",
"name": "[concat('loop-', copyIndex())]",
"copy": {
"name": "iterator",
"count": "[parameters('numberToDeploy')]",
"mode": "serial",
"batchSize": 1
},
"properties": {
"mode": "Incremental",
"template": {
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {
"lbApiVersion": "2015-06-15"
},
"resources": [
{
"apiVersion": "[variables('lbApiVersion')]",
"type": "Microsoft.Network/loadBalancers",
"name": "[concat('LB','-', copyIndex())]",
"location": "[parameters('clusterLocation')]",
"dependsOn": [
],
"properties": {
"frontendIPConfigurations": [
],
"backendAddressPools": [
],
"loadBalancingRules": [
],
"probes": [
],
"inboundNatPools": [
]
},
"tags": {
"resourceType": "Service Fabric"
}
}
],
"outputs": {
}
}
}
}
],
"outputs": {
}
}
"message": "Unable to process template language expressions for resource '/subscriptions/*************/resourceGroups/cluv2/providers/Microsoft.Resources/deployments/loop-0' at line '14' and column '10'. 'The template
variable 'lbApiVersion' is not found.
Based on my experience, when you define nested templates inline (so in the code of your existing template) they take parameter and variable values from your parent template, so just move the variable definition to your parent template
Unfortunately, you are not able to use variables and parameters in nested templates as indicated by the documentation. You can use them in external templates.
If you are trying to deploy multiple resources in an inline template, declare a variable or parameter of type object in the main template like this:
"variables" : {
"loadBalancers": [
{
"version": "2015-06-15"
},
{
"version": "2015-06-15"
}
]
}
Your copy on the Microsoft.Resources/deployments resource will look like this:
"copy": {
"name": "loadBalancerLoop",
"count": "[length(variables('loadBalancers'))]"
}
Then, in your nested resource, use the copyIndex() to grab the version
"apiVersion": "[variables('loadBalancers')[copyIndex()].version]"

Azure notification hub installantion

Fast question. How create installation using templates? Can you give an example? How construct 'InstallationTemplate' object.
When you want to create an ARM template based on the json schema of Microsoft.NotificationHubs:
Create a new project in Visual Studio 2015.
Choose for a Cloud project
Choose for Azure Resource Group
Choose Blank Template
Open azuredeploy.json
You will see this:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
},
"variables": {
},
"resources": [
],
"outputs": {
}
}
Then begin with the resources part by typing apiVersion (note that you get intellisense already):
"resources": [
{
"apiVersion": ""
}
],
After this add the type: Just select the correct value.
After the type is set. You will get intellisense on the allowed properties.
There are more parameters available. But when using the main properties and
create parameters of the hardcoded values and at the end you will get a template like:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"namespaceLocation": {
"type": "string"
},
"namespaceName": {
"type": "string"
},
"notificationHubName": {
"type": "string"
}
},
"variables": {
},
"resources": [
{
"apiVersion": "2014-09-01",
"name": "[parameters('namespaceName')]",
"type": "Microsoft.NotificationHubs/namespaces",
"location": "[parameters('namespaceLocation')]",
"properties": {
"name": "[parameters('namespaceName')]",
"namespaceType": "NotificationHub"
},
"resources": [
{
"apiVersion": "2014-09-01",
"name": "[parameters('notificationHubName')]",
"type": "Microsoft.NotificationHubs/namespaces/notificationHubs",
"location": "[parameters('namespaceLocation')]",
"dependsOn": [
"[concat('Microsoft.NotificationHubs/namespaces/', parameters('namespaceName'))]"
],
"properties": {
"name": "[parameters('notificationHubName')]"
}
}
]
}
],
"outputs": {
}
}

Resources