How do I deploy a Application Gateway with VMs behind it - azure

I've been trying to deploy an Azure Application Gateway to front application I have on existing VMs and use hostnames for the pool selection. I started with this template from git https://github.com/Azure/azure-quickstart-templates/tree/master/201-application-gateway-multihosting based on the article https://github.com/Azure/azure-content/blob/master/articles/application-gateway/application-gateway-multi-site-overview.md
Here is the modifed tempate I used
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"vnetAddressPrefix": {
"type": "string",
"defaultValue": "10.0.0.0/16",
"metadata": {
"description": "Address prefix for the Virtual Network"
}
},
"subnetPrefix": {
"type": "string",
"defaultValue": "10.0.0.0/28",
"metadata": {
"description": "Gateway Subnet prefix"
}
},
"skuName": {
"type": "string",
"allowedValues": [
"Standard_Small",
"Standard_Medium",
"Standard_Large"
],
"defaultValue": "Standard_Small",
"metadata": {
"description": "Sku Name"
}
},
"capacity": {
"type": "int",
"defaultValue": 4,
"metadata": {
"description": "Number of instances"
}
},
"backendIpAddress1": {
"type": "string",
"metadata": {
"description": "IP Address for Backend Server 1"
}
},
"backendIpAddress2": {
"type": "string",
"metadata": {
"description": "IP Address for Backend Server 2"
}
},
"backendIpAddress3": {
"type": "string",
"metadata": {
"description": "IP Address for Backend Server 3"
}
},
"backendIpAddress4": {
"type": "string",
"metadata": {
"description": "IP Address for Backend Server 4"
}
},
"backendIpAddress5": {
"type": "string",
"metadata": {
"description": "IP Address for Backend Server 5"
}
},
"backendIpAddress6": {
"type": "string",
"metadata": {
"description": "IP Address for Backend Server 6"
}
},
"hostName1": {
"type": "string",
"metadata": {
"description": "HostName for listener 1"
}
},
"hostName2": {
"type": "string",
"metadata": {
"description": "HostName for listener 2"
}
},
"certData1": {
"type": "securestring",
"metadata": {
"description": "Base-64 encoded form of the .pfx file"
}
},
"certPassword1": {
"type": "securestring",
"metadata": {
"description": "Password for .pfx certificate"
}
}
},
"variables": {
"applicationGatewayName": "PortalGateway",
"publicIPAddressName": "PortalGatewayFrontendIP",
"virtualNetworkName": "PalitonNetworks-East-VirtualNetwork",
"subnetName": "GWSubnet1",
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks',variables('virtualNetworkName'))]",
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"publicIPRef": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]",
"applicationGatewayID": "[resourceId('Microsoft.Network/applicationGateways',variables('applicationGatewayName'))]",
"apiVersion": "2015-06-15"
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[variables('publicIPAddressName')]",
"location": "[resourceGroup().location]",
"properties": {
"publicIPAllocationMethod": "Dynamic"
}
},
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Network/virtualNetworks",
"name": "[variables('virtualNetworkName')]",
"location": "[resourceGroup().location]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[parameters('vnetAddressPrefix')]"
]
},
"subnets": [
{
"name": "[variables('subnetName')]",
"properties": {
"addressPrefix": "[parameters('subnetPrefix')]"
}
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"name": "[variables('applicationGatewayName')]",
"type": "Microsoft.Network/applicationGateways",
"location": "[resourceGroup().location]",
"dependsOn": [
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]",
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"
],
"properties": {
"sku": {
"name": "[parameters('skuName')]",
"tier": "Standard",
"capacity": "[parameters('capacity')]"
},
"sslCertificates": [
{
"name": "appGatewaySslCert1",
"properties": {
"data": "[parameters('certData1')]",
"password": "[parameters('certPassword1')]"
}
}
],
"gatewayIPConfigurations": [
{
"name": "appGatewayIpConfig",
"properties": {
"subnet": {
"id": "[variables('subnetRef')]"
}
}
}
],
"frontendIPConfigurations": [
{
"name": "appGatewayFrontendIP",
"properties": {
"PublicIPAddress": {
"id": "[variables('publicIPRef')]"
}
}
}
],
"frontendPorts": [
{
"name": "appGatewayFrontendPort1",
"properties": {
"Port": 443
}
},
{
"name": "appGatewayFrontendPort2",
"properties": {
"Port": 80
}
}
],
"backendAddressPools": [
{
"name": "appGatewayBackendPool1",
"properties": {
"BackendAddresses": [
{
"IpAddress": "[parameters('backendIpAddress1')]"
},
{
"IpAddress": "[parameters('backendIpAddress2')]"
},
{
"IpAddress": "[parameters('backendIpAddress3')]"
}
]
}
},
{
"name": "appGatewayBackendPool2",
"properties": {
"BackendAddresses": [
{
"IpAddress": "[parameters('backendIpAddress4')]"
},
{
"IpAddress": "[parameters('backendIpAddress5')]"
},
{
"IpAddress": "[parameters('backendIpAddress6')]"
}
]
}
}
],
"backendHttpSettingsCollection": [
{
"name": "appGatewayBackendHttpSettings",
"properties": {
"Port": 80,
"Protocol": "Http",
"CookieBasedAffinity": "Disabled"
}
},
{
"name": "appGatewayBackendHttpsSettings",
"properties": {
"Port": 443,
"Protocol": "Https",
"CookieBasedAffinity": "Disabled"
}
}
],
"httpListeners": [
{
"name": "appGatewayHttpsListener-Group1",
"properties": {
"FrontendIPConfiguration": {
"Id": "[concat(variables('applicationGatewayID'), '/frontendIPConfigurations/appGatewayFrontendIP')]"
},
"FrontendPort": {
"Id": "[concat(variables('applicationGatewayID'), '/frontendPorts/appGatewayFrontendPort1')]"
},
"Protocol": "Https",
"SslCertificate": {
"Id": "[concat(variables('applicationGatewayID'), '/sslCertificates/appGatewaySslCert1')]"
},
"HostName": "[parameters('hostName1')]",
"RequireServerNameIndication": "false"
}
},
{
"name": "appGatewayHttpsListener-Group2",
"properties": {
"FrontendIPConfiguration": {
"Id": "[concat(variables('applicationGatewayID'), '/frontendIPConfigurations/appGatewayFrontendIP')]"
},
"FrontendPort": {
"Id": "[concat(variables('applicationGatewayID'), '/frontendPorts/appGatewayFrontendPort1')]"
},
"Protocol": "Https",
"SslCertificate": {
"Id": "[concat(variables('applicationGatewayID'), '/sslCertificates/appGatewaySslCert1')]"
},
"HostName": "[parameters('hostName2')]",
"RequireServerNameIndication": "false"
}
},
{
"name": "appGatewayHttpListener-Group1",
"properties": {
"FrontendIPConfiguration": {
"Id": "[concat(variables('applicationGatewayID'), '/frontendIPConfigurations/appGatewayFrontendIP')]"
},
"FrontendPort": {
"Id": "[concat(variables('applicationGatewayID'), '/frontendPorts/appGatewayFrontendPort2')]"
},
"Protocol": "Http",
"SslCertificate": null,
"HostName": "[parameters('hostName1')]",
"RequireServerNameIndication": "false"
}
},
{
"name": "appGatewayHttpListener-Group2",
"properties": {
"FrontendIPConfiguration": {
"Id": "[concat(variables('applicationGatewayID'), '/frontendIPConfigurations/appGatewayFrontendIP')]"
},
"FrontendPort": {
"Id": "[concat(variables('applicationGatewayID'), '/frontendPorts/appGatewayFrontendPort2')]"
},
"Protocol": "Http",
"SslCertificate": null,
"HostName": "[parameters('hostName2')]",
"RequireServerNameIndication": "false"
}
}
],
"requestRoutingRules": [
{
"Name": "Group1-SSL",
"properties": {
"RuleType": "Basic",
"httpListener": {
"id": "[concat(variables('applicationGatewayID'), '/httpListeners/appGatewayHttpsListener-Group1')]"
},
"backendAddressPool": {
"id": "[concat(variables('applicationGatewayID'), '/backendAddressPools/appGatewayBackendPool1')]"
},
"backendHttpSettings": {
"id": "[concat(variables('applicationGatewayID'), '/backendHttpSettingsCollection/appGatewayBackendHttpSettings')]"
}
}
},
{
"Name": "Group2-SSL",
"properties": {
"RuleType": "Basic",
"httpListener": {
"id": "[concat(variables('applicationGatewayID'), '/httpListeners/appGatewayHttpsListener-Group2')]"
},
"backendAddressPool": {
"id": "[concat(variables('applicationGatewayID'), '/backendAddressPools/appGatewayBackendPool2')]"
},
"backendHttpSettings": {
"id": "[concat(variables('applicationGatewayID'), '/backendHttpSettingsCollection/appGatewayBackendHttpSettings')]"
}
}
},
{
"Name": "Group2-www",
"properties": {
"RuleType": "Basic",
"httpListener": {
"id": "[concat(variables('applicationGatewayID'), '/httpListeners/appGatewayHttpListener-Group1')]"
},
"backendAddressPool": {
"id": "[concat(variables('applicationGatewayID'), '/backendAddressPools/appGatewayBackendPool1')]"
},
"backendHttpSettings": {
"id": "[concat(variables('applicationGatewayID'), '/backendHttpSettingsCollection/appGatewayBackendHttpSettings')]"
}
}
},
{
"Name": "Group1-www",
"properties": {
"RuleType": "Basic",
"httpListener": {
"id": "[concat(variables('applicationGatewayID'), '/httpListeners/appGatewayHttpListener-Group2')]"
},
"backendAddressPool": {
"id": "[concat(variables('applicationGatewayID'), '/backendAddressPools/appGatewayBackendPool2')]"
},
"backendHttpSettings": {
"id": "[concat(variables('applicationGatewayID'), '/backendHttpSettingsCollection/appGatewayBackendHttpSettings')]"
}
}
}
]
}
}
]
}
As you can see I specify GWSubnet1 as the App Gateway subnet. My backend IPs are in the VMnet1 subnet under the same Virtual Network. When I deploy it fails saying that it can't delete VMnet1. VMNet1 is only indirectly referenced as the backend IP so why would it be trying remove it. GWSubnet1 is an unused empty subenet as per the deployment rules from Azure.
If I use the GUI I can create the gateway and select GWSubnet1. However using the GUI the advanced feature of putting the hostname in the listner isn't an option and therefore won't let you create multiple listners using the same front end port. I tried using the GUI and and then adding listners via Poweshell (version 3.0.0) using the following
$hostname = "example1.foo.com"
$listnername = "group2-az"
$appgwname = "PortalGateway"
$rmname = "myrmg"
$feipname = "appGatewayFrontendIP"
$fepname = "appGatewayFrontendPort"
$behttpname = "appGatewayBackendHttpSettings"
$appgw = Get-AzureRmApplicationGateway -Name $appgwname -ResourceGroupName $rmname
$bepool = Get-AzureRmApplicationGatewayBackendAddressPool -ApplicationGateway $appgw -Name "appGatewayBackendPool"
$behttp = Get-AzureRmApplicationGatewayBackendHttpSettings -ApplicationGateway $appgw -Name $behttpname
$fipc = Get-AzureRmApplicationGatewayFrontendIPConfig -Name $feipname -ApplicationGateway $appgw
$fep = Get-AzureRmApplicationGatewayFrontendPort -Name $fepname -ApplicationGateway $appgw
$result = Add-AzureRmApplicationGatewayHttpListener -ApplicationGateway $appgw -Name "appGatewayHttpListenerGroup1" -Protocol Http -FrontendIPConfiguration $fipc -FrontendPort $fep -HostName $hostname -RequireServerNameIndication false
However what appears to happen is that it doesn't add a listener it just modifies the existing default listener that is created when you create the appgateway via the GUI. It does this no matter what name I choose as the listener.
I know the deployment template works as I can create a new empty resource group and deploy it in there and it deploys. I just can't seem to get it to deploy where there are existing VMs. What is the correct way to do this?

ARM Templates are declarative and in your template there is only a single Subnet. If you deploy that template ARM will try to make it exactly as you have defined = it tries to remove any subnets in that subnet which arent defined withing itself.
That is the reason for your error. ARM tries to delete your VMnet1 and it cant do that as long as it has NICs associated with it.
Check the Documentation here:
Deploy resources with Resource Manager templates and Azure PowerShell
The interesting Part for you is:
Incremental and complete deployments
When deploying your resources, you specify that the deployment is either an incremental update or a complete update. By default, Resource Manager handles deployments as incremental updates to the resource group.
With incremental deployment, Resource Manager:
leaves unchanged resources that exist in the resource group but are not specified in the template
adds resources that are specified in the template but do not exist in the resource group
does not reprovision resources that exist in the resource group in the same condition defined in the template
reprovisions existing resources that have updated settings in the template
With complete deployment, Resource Manager:
deletes resources that exist in the resource group but are not specified in the template
adds resources that are specified in the template but do not exist in the resource group
does not reprovision resources that exist in the resource group in the same condition defined in the template
reprovisions existing resources that have updated settings in the template
To solve your Problem you need to either make the subnet config exactly represent your existing setup or your manually create the new subnet and dont define the vnet in your template.
If you create the subnet manually you can reference your existing vnet and subnet in the template like this:
"parameters": {
"existingVirtualNetworkName": {
"type": "string"
},
"existingVirtualNetworkResourceGroup": {
"type": "string"
},
"existingSubnet1Name": {
"type": "string"
},
"existingSubnet2Name": {
"type": "string"
},
}
"variables": {
"existingVnetID": "[resourceId(parameters('existingVirtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', parameters('existingVirtualNetworkName'))]",
"existingSubnet1Ref": "[concat(variables('existingVnetID'),'/subnets/', parameters('existingSubnet1Name'))]",
"existingSubnet2Ref": "[concat(variables('existingVnetID'),'/subnets/', parameters('existingSubnet2Name'))]",
}
After passing the existing RessourceGroup, Vnet and Subnetnames through parameters you can just use the variables "existingSubnet1Name" to point to the correct IDs.
The magic lies withing the [resourceId()] functions optional parameters: [subscriptionId], [resourceGroupName].
resourceId ([subscriptionId], [resourceGroupName], resourceType, resourceName1, [resourceName2]...)
Documentation: Template functions

Related

Only one SFTP Server for one Azure Resource group possible?

Is it only possible to create one on-demand SFPT Server with one Resource group in Azure?
This is a link regards to SFPT on Azure. https://learn.microsoft.com/en-us/samples/azure-samples/sftp-creation-template/sftp-on-azure/
I tried to create a second SFPT in the same Resource group, but previous SFPT got replaced with the new one.
I tried Goolging on this one, but I was not able to find the answer, so I am posting this question here.
Yes we can deploy multiple SFTP server to our Azure resource group.
But the template you are using already they have declare default variables ,Instead of that we need to declare parameters as shown in below template, So that you can use the same template multiple times.
TEMPLATE:-
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.4.63.48766",
"templateHash": "17013458610905703770"
}
},
"parameters": {
"storageAccountType": {
"type": "string",
"defaultValue": "Standard_LRS",
"metadata": {
"description": "Storage account type"
},
"allowedValues": [
"Standard_LRS",
"Standard_ZRS",
"Standard_GRS"
]
},
"storageAccountPrefix": {
"type": "string",
"defaultValue": "sftpstg",
"metadata": {
"description": "Prefix for new storage account"
}
},
"fileShareName": {
"type": "string",
"defaultValue": "sftpfileshare",
"metadata": {
"description": "Name of file share to be created"
}
},
"sftpUser": {
"type": "string",
"defaultValue": "sftp",
"metadata": {
"description": "Username to use for SFTP access"
}
},
"sftpPassword": {
"type": "securestring",
"metadata": {
"description": "Password to use for SFTP access"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Primary location for resources"
}
},
"containerGroupDNSLabel": {
"type": "string",
"defaultValue": "[uniqueString(resourceGroup().id, deployment().name)]",
"metadata": {
"description": "DNS label for container group"
}
},
"sftpContainerGroupName": {
"type": "string",
"metadata": {
"description": "cngroup for container group"
}
},
"sftpContainerName": {
"type": "string",
"metadata": {
"description": "container name"
}
}
},
"functions": [],
"variables": {
"sftpContainerImage": "atmoz/sftp:debian",
"sftpEnvVariable": "[format('{0}:{1}:1001', parameters('sftpUser'), parameters('sftpPassword'))]",
"storageAccountName": "[take(toLower(format('{0}{1}', parameters('storageAccountPrefix'), uniqueString(resourceGroup().id))), 24)]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[variables('storageAccountName')]",
"location": "[parameters('location')]",
"kind": "StorageV2",
"sku": {
"name": "[parameters('storageAccountType')]"
}
},
{
"type": "Microsoft.Storage/storageAccounts/fileServices/shares",
"apiVersion": "2019-06-01",
"name": "[toLower(format('{0}/default/{1}', variables('storageAccountName'), parameters('fileShareName')))]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
]
},
{
"type": "Microsoft.ContainerInstance/containerGroups",
"apiVersion": "2019-12-01",
"name": "[parameters('sftpContainerGroupName')]",
"location": "[parameters('location')]",
"properties": {
"containers": [
{
"name": "[parameters('sftpContainerName')]",
"properties": {
"image": "[variables('sftpContainerImage')]",
"environmentVariables": [
{
"name": "SFTP_USERS",
"secureValue": "[variables('sftpEnvVariable')]"
}
],
"resources": {
"requests": {
"cpu": 1,
"memoryInGB": 1
}
},
"ports": [
{
"port": 22,
"protocol": "TCP"
}
],
"volumeMounts": [
{
"mountPath": "[format('/home/{0}/upload', parameters('sftpUser'))]",
"name": "sftpvolume",
"readOnly": false
}
]
}
}
],
"osType": "Linux",
"ipAddress": {
"type": "Public",
"ports": [
{
"port": 22,
"protocol": "TCP"
}
],
"dnsNameLabel": "[parameters('containerGroupDNSLabel')]"
},
"restartPolicy": "OnFailure",
"volumes": [
{
"name": "sftpvolume",
"azureFile": {
"readOnly": false,
"shareName": "[parameters('fileShareName')]",
"storageAccountName": "[variables('storageAccountName')]",
"storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value]"
}
}
]
},
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
]
}
],
"outputs": {
"containerDNSLabel": {
"type": "string",
"value": "[format('{0}.{1}.azurecontainer.io', reference(resourceId('Microsoft.ContainerInstance/containerGroups', parameters('sftpContainerGroupName'))).ipAddress.dnsNameLabel, reference(resourceId('Microsoft.ContainerInstance/containerGroups', parameters('sftpContainerGroupName')), '2019-12-01', 'full').location)]"
}
}
}
Deployment details:-

Listeners and SSL certs are getting deleted while redeploying Application gateway with ARM template

I am using below ARM template to create Application Gateway. When I deploy it for the first time everything works. When I redeploy it (as a part of continuous deployment in the same env) it fails and I see the Listeners get deleted which were present previously and the SSL certificate also gets deleted.
Is there any option I can update the sub-resources present in ARM template based on a criteria something like not to update the listener if SSL cert is already present. I am updating some of the properties like creating rules and probe after creation of application gateway and not using ARM template.
ARM Template for reference-
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"applicationgateway_platform": {
"defaultValue": "",
"type": "String",
"maxLength": 6
},
"applicationgateway_location_shortname": {
"defaultValue": "",
"type": "String",
"maxLength": 3
},
"applicationgateway_project": {
"defaultValue": "",
"type": "String",
"maxLength": 6
},
"applicationgateway_environment": {
"defaultValue": "",
"type": "String",
"maxLength": 7
},
"applicationgateway_uniqueid": {
"defaultValue": "1",
"type": "String",
"maxLength": "1"
},
"vnetName": {
"type": "string",
"metadata": {
"description": "Name of the Virtual Network"
}
},
"subnetName": {
"type": "string",
"metadata": {
"description": "Name of subnet"
}
},
"vnetResourceGroup": {
"type": "string",
"defaultValue": "[resourceGroup().name]",
"metadata": {
"description": "Name of Resource group where Vnet and subnet resides"
}
},
"applicationGatewayTier": {
"type": "string",
"allowedValues": [
"Standard_v2",
"WAF_v2"
],
"defaultValue": "WAF_v2",
"metadata": {
"description": "application gateway tier"
}
},
"frontendPort": {
"type": "int",
"defaultValue": 80,
"metadata": {
"description": "application gateway front end port"
}
},
"secureFrontendPort": {
"type": "int",
"defaultValue": 443,
"metadata": {
"description": "application gateway secure front end port"
}
},
"backendPort": {
"type": "int",
"defaultValue": 80,
"metadata": {
"description": "application gateway back end port"
}
},
"applicationGatewayAutoScaleMinimumCapacity": {
"type": "int",
"defaultValue": 1,
"metadata": {
"description": "Minimum appgateway instance to be running always"
}
},
"applicationGatewayAutoScaleMaximumCapacity": {
"type": "int",
"defaultValue": 10,
"metadata": {
"description": "Maximum appgateway instance that it can scale up."
}
}
},
"variables": {
"basename": "[concat(parameters('applicationgateway_platform'), '-', parameters('applicationgateway_project'), '-', parameters('applicationgateway_location_shortname'), '-', parameters('applicationgateway_environment'))]",
"applicationGatewayName": "[concat(variables('basename'), '-ag-', parameters('applicationgateway_uniqueid'))]",
"publicIPAddressName": "[concat(variables('basename'),'-agip-',parameters('applicationgateway_uniqueid'))]",
"subnetRef": "[concat(resourceId(parameters('vnetResourceGroup'), 'Microsoft.Network/virtualNetworks', parameters('vnetName')), '/subnets/', parameters('subnetName'))]",
"publicIPRef": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]",
"webApplicationFirewallConfigurationProperties": {
"enabled": true,
"firewallMode": "Detection",
"ruleSetType": "OWASP",
"ruleSetVersion": "3.0"
},
"apiVersion": "2019-09-01"
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[variables('publicIPAddressName')]",
"location": "[resourceGroup().location]",
"sku": {
"name": "Standard"
},
"zones": [],
"properties": {
"publicIPAllocationMethod": "Static",
"dnsSettings": {
"domainNameLabel": "[variables('applicationGatewayName')]"
}
}
},
{
"apiVersion": "[variables('apiVersion')]",
"name": "[variables('applicationGatewayName')]",
"type": "Microsoft.Network/applicationGateways",
"location": "[resourceGroup().location]",
"dependsOn": [
"[variables('publicIPRef')]"
],
"properties": {
"sku": {
"name": "[parameters('applicationGatewayTier')]",
"tier": "[parameters('applicationGatewayTier')]"
},
"gatewayIPConfigurations": [
{
"name": "appGatewayIpConfig",
"properties": {
"subnet": {
"id": "[variables('subnetRef')]"
}
}
}
],
"frontendIPConfigurations": [
{
"name": "appGatewayFrontendIP",
"properties": {
"PublicIPAddress": {
"id": "[variables('publicIPRef')]"
}
}
}
],
"frontendPorts": [
{
"name": "appGatewayFrontendPort",
"properties": {
"Port": "[parameters('frontendPort')]"
}
},
{
"name": "appGatewaySecurePort",
"properties": {
"Port": "[parameters('secureFrontendPort')]"
}
}
],
"backendAddressPools": [
{
"name": "appGatewayBackendPool",
"properties": {
"BackendAddresses": []
}
}
],
"backendHttpSettingsCollection": [
{
"name": "appGatewayBackendHttpSettings",
"properties": {
"Port": "[parameters('backendPort')]",
"Protocol": "Http",
"CookieBasedAffinity": "disabled",
"requestTimeout": 20
}
}
],
"httpListeners": [
{
"name": "appGatewayHttpListener",
"properties": {
"FrontendIpConfiguration": {
"Id": "[concat(resourceId('Microsoft.Network/applicationGateways', variables('applicationGatewayName')), '/frontendIPConfigurations/appGatewayFrontendIP')]"
},
"FrontendPort": {
"Id": "[concat(resourceId('Microsoft.Network/applicationGateways', variables('applicationGatewayName')), '/frontendPorts/appGatewayFrontendPort')]"
},
"Protocol": "Http",
"SslCertificate": null
}
}
],
"requestRoutingRules": [
{
"Name": "basicRule",
"properties": {
"RuleType": "Basic",
"httpListener": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', variables('applicationGatewayName')), '/httpListeners/appGatewayHttpListener')]"
},
"backendAddressPool": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', variables('applicationGatewayName')), '/backendAddressPools/appGatewayBackendPool')]"
},
"backendHttpSettings": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', variables('applicationGatewayName')), '/backendHttpSettingsCollection/appGatewayBackendHttpSettings')]"
}
}
}
],
"enableHttp2": false,
"sslCertificates": [],
"probes": [],
"autoscaleConfiguration": {
"minCapacity": "[parameters('applicationGatewayAutoScaleMinimumCapacity')]",
"maxCapacity": "[parameters('applicationGatewayAutoScaleMaximumCapacity')]"
},
"webApplicationFirewallConfiguration": "[if(equals(toUpper(parameters('applicationGatewayTier')), 'WAF_V2'), variables('webApplicationFirewallConfigurationProperties'), json('null'))]"
}
}
]
}
By default, Resource Manager deployment uses incremental mode. In incremental mode, Resource Manager leaves unchanged resources that exist in the resource group but aren't specified in the template.
However, when redeploying an existing resource in incremental mode,
the outcome is different. Specify all properties for the resource,
not just the ones you're updating. A common misunderstanding is to
think properties that aren't specified are left unchanged. If you
don't specify certain properties, Resource Manager interprets the
update as overwriting those values.
So, if you want some properties to leave unchanged when redeploying the template, you can specify certain properties (listeners for HTTPS, rules for HTTPS, SSL certificate) in your template. Here is a quickstart template for an end to end SSL with an application gateway that you can refer to.
To update a resource in an Azure Resource Manager template, you could follow the link for more details.
First, you must reference the resource once in the template to create
it and then reference the resource by the same name to update it
later. However, if two resources have the same name in a template,
Resource Manager throws an exception. To avoid this error, specify the
updated resource in a second template that's either linked or included
as a subtemplate using the Microsoft.Resources/deployments resource
type.
Second, you must either specify the name of the existing property to
change or a new name for a property to add in the nested template. You
must also specify the original properties and their original values.
If you fail to provide the original properties and values, Resource
Manager assumes you want to create a new resource and deletes the
original resource.

Application Gateway ARM Template - Parameter for Enabling Firewall

I have a working ARM Template to deploy an Application Gateway with WAF Enabled, this is currently always enabling the Firewall and setting the Firewall Mode based on parameters.
We want to parameterize enabling the WAF so that an AGW can be deployed without WAF
The object in the properties looks like:
"webApplicationFirewallConfiguration": {
"enabled": "[parameters('applicationGateway').firewallEnabled]",
"firewallMode": "[parameters('applicationGateway').firewallMode]",
"ruleSetType": "OWASP",
"ruleSetVersion": "3.0"
}
The parameter file has these set:
"firewallEnabled": false,
"Tier": "Standard",
"skuSize": "Standard_Medium",
However on deployment it errors out trying to enable the Firewall
New-AzResourceGroupDeployment : 11:28:27 AM - Error:
Code=ApplicationGatewayFirewallCannotBeEnabledForSelectedSku;
Message=Application Gateway
/subscriptions//providers/Microsoft.Network/applicationGatewa
ys/EXAMPLE-AGW does not support WebApplicationFirewall with the
selected SKU tier Standard
It looks like it's still trying to enable the firewall even though the "enabled:" property would be false, I would assume it would ignore the rest of the properties in the object but obviously not. Can anyone see what I'm doing wrong here?
Reason for Failure: As WebApplicationFirewall is not supported for Standard Tier AppGateway, the template VALIDATION will fail even if enabled is set to false as validation sees "webApplicationFirewallConfiguration" key itself as invalid for Standard Tier.
Fix: Use Nested Templates to create a child deployment of an Application Gateway template without "webApplicationFirewallConfiguration" if firewall is disabled, else the one with "webApplicationFirewallConfiguration" if firewall is enabled along with firewall mode value in the parameters file.
Working Sample: Please find below the root template for deployment along with two templates with firewall enabled and disabled as well. Then, it has two parameters file - one for firewall enabled and other for disabled one.
To try out this sample, follow the below steps:
Upload the two Child templates in a Blob Storage.
Make this Blob Container, where templates are uploaded, Public accessible or use SAS token while creating the template's url.
Update the variables "appGatewaysTemplateWaffalse" and "appGatewaysTemplateWaftrue" in root template with urls of uploaded child templates.
Go https://portal.azure.com/#create/Microsoft.Template -> "Build your own template in the editor".
Use this updated root template with urls and the parameter file (enabled or disabled) as desired.
Root Template (VNet + Child Deployment):
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"applicationGateway": {
"type": "object",
"metadata": {
"description": "Application gateway specific information"
}
},
"virtualNetworkName": {
"type": "string",
"metadata": {
"description": "virtual network name"
}
},
"vnetAddressPrefix": {
"type": "string",
"defaultValue": "10.0.0.0/16",
"metadata": {
"description": "virtual network address range"
}
},
"subnetName": {
"type": "string",
"defaultValue": "subnet1",
"metadata": {
"description": "Subnet Name"
}
},
"subnetPrefix": {
"type": "string",
"defaultValue": "10.0.0.0/24",
"metadata": {
"description": "Subnet prefix"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
}
},
"variables": {
"subnetRef": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]",
"appGatewaysTemplateWaffalse": "https://da2.blob.core.windows.net/templates/app-gateway-waf-false.json",
"appGatewaysTemplateWaftrue": "https://da2.blob.core.windows.net/templates/app-gateway-waf-true.json"
},
"resources": [
{
"apiVersion": "2015-06-15",
"type": "Microsoft.Network/virtualNetworks",
"name": "[parameters('virtualNetworkName')]",
"location": "[parameters('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[parameters('vnetAddressPrefix')]"
]
},
"subnets": [
{
"name": "[parameters('subnetName')]",
"properties": {
"addressPrefix": "[parameters('subnetPrefix')]"
}
}
]
}
},
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2015-01-01",
"name": "azure-appGateways-non-waf-deployment",
"dependsOn": [
"[concat('Microsoft.Network/virtualNetworks/', parameters('virtualNetworkName'))]"
],
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables(concat('appGatewaysTemplateWaf',string(parameters('applicationGateway').firewallEnabled)))]"
},
"parameters": {
"applicationGateway": {
"value": "[parameters('applicationGateway')]"
},
"location": {
"value": "[parameters('location')]"
},
"subnetRef": {
"value": "[variables('subnetRef')]"
}
}
}
}
]
}
Child Template without webApplicationFirewallConfiguration:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"applicationGateway": {
"type": "object",
"metadata": {
"description": "Application gateway specific information"
}
},
"subnetRef": {
"type": "string",
"defaultValue": "subnet id",
"metadata": {
"description": "Subnet Id"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
}
},
"variables": {},
"resources": [
{
"apiVersion": "2017-06-01",
"name": "[parameters('applicationGateway').applicationGatewayName]",
"type": "Microsoft.Network/applicationGateways",
"location": "[parameters('location')]",
"dependsOn": [],
"properties": {
"sku": {
"name": "[parameters('applicationGateway').applicationGatewaySize]",
"tier": "[parameters('applicationGateway').skuTier]",
"capacity": "[parameters('applicationGateway').applicationGatewayInstanceCount]"
},
"gatewayIPConfigurations": [
{
"name": "appGatewayIpConfig",
"properties": {
"subnet": {
"id": "[parameters('subnetRef')]"
}
}
}
],
"frontendIPConfigurations": [
{
"name": "appGatewayFrontendIP",
"properties": {
"subnet": {
"id": "[parameters('subnetRef')]"
}
}
}
],
"frontendPorts": [
{
"name": "appGatewayFrontendPort",
"properties": {
"Port": "[parameters('applicationGateway').frontendPort]"
}
}
],
"backendAddressPools": [
{
"name": "appGatewayBackendPool",
"properties": {
"BackendAddresses": "[parameters('applicationGateway').backendIPAddresses]"
}
}
],
"backendHttpSettingsCollection": [
{
"name": "appGatewayBackendHttpSettings",
"properties": {
"Port": "[parameters('applicationGateway').backendPort]",
"Protocol": "Http",
"CookieBasedAffinity": "[parameters('applicationGateway').cookieBasedAffinity]"
}
}
],
"httpListeners": [
{
"name": "appGatewayHttpListener",
"properties": {
"FrontendIpConfiguration": {
"Id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateway').applicationGatewayName), '/frontendIPConfigurations/appGatewayFrontendIP')]"
},
"FrontendPort": {
"Id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateway').applicationGatewayName), '/frontendPorts/appGatewayFrontendPort')]"
},
"Protocol": "Http",
"SslCertificate": null
}
}
],
"requestRoutingRules": [
{
"Name": "rule1",
"properties": {
"RuleType": "Basic",
"httpListener": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateway').applicationGatewayName), '/httpListeners/appGatewayHttpListener')]"
},
"backendAddressPool": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateway').applicationGatewayName), '/backendAddressPools/appGatewayBackendPool')]"
},
"backendHttpSettings": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateway').applicationGatewayName), '/backendHttpSettingsCollection/appGatewayBackendHttpSettings')]"
}
}
}
]
}
}
]
}
Child Template with webApplicationFirewallConfiguration:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"applicationGateway": {
"type": "object",
"metadata": {
"description": "Application gateway specific information"
}
},
"subnetRef": {
"type": "string",
"defaultValue": "subnet id",
"metadata": {
"description": "Subnet Id"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
}
},
"variables": {},
"resources": [
{
"apiVersion": "2017-06-01",
"name": "[parameters('applicationGateway').applicationGatewayName]",
"type": "Microsoft.Network/applicationGateways",
"location": "[parameters('location')]",
"dependsOn": [],
"properties": {
"sku": {
"name": "[parameters('applicationGateway').applicationGatewaySize]",
"tier": "[parameters('applicationGateway').skuTier]",
"capacity": "[parameters('applicationGateway').applicationGatewayInstanceCount]"
},
"gatewayIPConfigurations": [
{
"name": "appGatewayIpConfig",
"properties": {
"subnet": {
"id": "[parameters('subnetRef')]"
}
}
}
],
"frontendIPConfigurations": [
{
"name": "appGatewayFrontendIP",
"properties": {
"subnet": {
"id": "[parameters('subnetRef')]"
}
}
}
],
"frontendPorts": [
{
"name": "appGatewayFrontendPort",
"properties": {
"Port": "[parameters('applicationGateway').frontendPort]"
}
}
],
"backendAddressPools": [
{
"name": "appGatewayBackendPool",
"properties": {
"BackendAddresses": "[parameters('applicationGateway').backendIPAddresses]"
}
}
],
"backendHttpSettingsCollection": [
{
"name": "appGatewayBackendHttpSettings",
"properties": {
"Port": "[parameters('applicationGateway').backendPort]",
"Protocol": "Http",
"CookieBasedAffinity": "[parameters('applicationGateway').cookieBasedAffinity]"
}
}
],
"httpListeners": [
{
"name": "appGatewayHttpListener",
"properties": {
"FrontendIpConfiguration": {
"Id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateway').applicationGatewayName), '/frontendIPConfigurations/appGatewayFrontendIP')]"
},
"FrontendPort": {
"Id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateway').applicationGatewayName), '/frontendPorts/appGatewayFrontendPort')]"
},
"Protocol": "Http",
"SslCertificate": null
}
}
],
"webApplicationFirewallConfiguration": {
"enabled": "[parameters('applicationGateway').firewallEnabled]",
"firewallMode": "[parameters('applicationGateway').firewallMode]",
"ruleSetType": "OWASP",
"ruleSetVersion": "3.0"
},
"requestRoutingRules": [
{
"Name": "rule1",
"properties": {
"RuleType": "Basic",
"httpListener": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateway').applicationGatewayName), '/httpListeners/appGatewayHttpListener')]"
},
"backendAddressPool": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateway').applicationGatewayName), '/backendAddressPools/appGatewayBackendPool')]"
},
"backendHttpSettings": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateway').applicationGatewayName), '/backendHttpSettingsCollection/appGatewayBackendHttpSettings')]"
}
}
}
]
}
}
]
}
Parameters with firewall disabled:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"applicationGateway": {
"value": {
"firewallEnabled": "false",
"skuTier": "Standard",
"applicationGatewayName": "yourappgateway",
"applicationGatewaySize": "Standard_Small",
"applicationGatewayInstanceCount": 1,
"frontendPort": 80,
"backendPort": 80,
"backendIPAddresses": [
{
"IpAddress": "10.0.0.7"
},
{
"IpAddress": "10.0.0.8"
},
{
"IpAddress": "10.0.0.9"
}
],
"cookieBasedAffinity": "Disabled"
}
},
"virtualNetworkName": {
"value": "yourvnetname"
},
"vnetAddressPrefix": {
"value": "10.0.0.0/16"
},
"subnetName": {
"value": "yoursubnet"
},
"subnetPrefix": {
"value": "10.0.0.0/24"
}
}
}
Parameters with firewall enabled:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"applicationGateway": {
"value": {
"firewallEnabled": "true",
"firewallMode": "Detection",
"skuTier": "WAF",
"applicationGatewayName": "yourappgateway",
"applicationGatewaySize": "WAF_Medium",
"applicationGatewayInstanceCount": 1,
"frontendPort": 80,
"backendPort": 80,
"backendIPAddresses": [
{
"IpAddress": "10.0.0.7"
},
{
"IpAddress": "10.0.0.8"
},
{
"IpAddress": "10.0.0.9"
}
],
"cookieBasedAffinity": "Disabled"
}
},
"virtualNetworkName": {
"value": "yourvnetname"
},
"vnetAddressPrefix": {
"value": "10.0.0.0/16"
},
"subnetName": {
"value": "yoursubnet"
},
"subnetPrefix": {
"value": "10.0.0.0/24"
}
}
}
Not sure why this is happening, but you can always do this:
"variables": {
"waffalse": {
"enabled": false
},
"waftrue": {
"enabled": true,
"firewallMode": "[parameters('applicationGateway').firewallMode]",
"ruleSetType": "OWASP",
"ruleSetVersion": "3.0"
}
}
...
"webApplicationFirewallConfiguration": "[variables(concat('waf', string(parameters('applicationGateway').firewallEnabled)))]"
so use one variable or the other depending on condition

Azure Kubernetes Service ARM template is not idempotent

I have created an ARM template to deploy an Azure Kubernetes Service instance, which I am trying to plug into a CI/CD pipeline in VSTS. On the first deployment, everything works as expected and the K8s cluster is created successfully. However, upon redeployment, the template fails the validation stage with the following error:
{
"message": "The template deployment 'Microsoft.Template' is not valid according to the validation procedure."
"details": [
{
"code":"PropertyChangeNotAllowed",
"message":"Provisioning of resource(s) for container service <cluster name> in resource group <resource group name> failed. Message:"
{
"code": "PropertyChangeNotAllowed",
"message": "Changing property 'linuxProfile.ssh.publicKeys.keyData' is not allowed.",
"target": "linuxProfile.ssh.publicKeys.keyData"
}
}
]
}
The template is therefore clearly not idempotent which completely dishonours the intended nature of ARM template deployments.
Has anyone managed to find a workaround for this?
The solution to this is to specify the SSH RSA Public Key as a template parameter and use it when configuring the Linux profile. I have posted my ARM template below:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"clusterName": {
"type": "string",
"metadata": {
"description": "The name of the Kubernetes cluster."
}
},
"location": {
"type": "string",
"metadata": {
"description": "The data center in which to deploy the Kubernetes cluster."
}
},
"dnsPrefix": {
"type": "string",
"metadata": {
"description": "DNS prefix to use with hosted Kubernetes API server FQDN."
}
},
"osDiskSizeGB": {
"defaultValue": 32,
"minValue": 0,
"maxValue": 1023,
"type": "int",
"metadata": {
"description": "Disk size (in GB) to provision for each of the agent pool nodes. This value ranges from 0 to 1023. Specifying 0 will apply the default disk size for that agentVMSize."
}
},
"agentCount": {
"defaultValue": 1,
"minValue": 1,
"maxValue": 50,
"type": "int",
"metadata": {
"description": "The number of agent nodes for the cluster."
}
},
"agentVMSize": {
"defaultValue": "Standard_D1_v2",
"type": "string",
"metadata": {
"description": "The size of the Virtual Machine."
}
},
"servicePrincipalClientId": {
"type": "securestring",
"metadata": {
"description": "The Service Principal Client ID."
}
},
"servicePrincipalClientSecret": {
"type": "securestring",
"metadata": {
"description": "The Service Principal Client Secret."
}
},
"osType": {
"defaultValue": "Linux",
"allowedValues": [
"Linux"
],
"type": "string",
"metadata": {
"description": "The type of operating system."
}
},
"kubernetesVersion": {
"defaultValue": "1.10.6",
"type": "string",
"metadata": {
"description": "The version of Kubernetes."
}
},
"enableOmsAgent": {
"defaultValue": true,
"type": "bool",
"metadata": {
"description": "boolean flag to turn on and off of omsagent addon"
}
},
"enableHttpApplicationRouting": {
"defaultValue": true,
"type": "bool",
"metadata": {
"description": "boolean flag to turn on and off of http application routing"
}
},
"networkPlugin": {
"defaultValue": "kubenet",
"allowedValues": [
"azure",
"kubenet"
],
"type": "string",
"metadata": {
"description": "Network plugin used for building Kubernetes network."
}
},
"enableRBAC": {
"defaultValue": true,
"type": "bool",
"metadata": {
"description": "Flag to turn on/off RBAC"
}
},
"logAnalyticsWorkspaceName": {
"type": "string",
"metadata": {
"description": "Name of the log analytics workspace which will be used for container analytics"
}
},
"logAnalyticsWorkspaceLocation": {
"type": "string",
"metadata": {
"description": "The data center in which the log analytics workspace is deployed"
}
},
"logAnalyticsResourceGroup": {
"type": "string",
"metadata": {
"description": "The resource group in which the log analytics workspace is deployed"
}
},
"vmAdminUsername": {
"type": "string",
"metadata": {
"description": "User name for the Linux Virtual Machines."
}
},
"sshRsaPublicKey": {
"type": "securestring",
"metadata": {
"description": "Configure all linux machines with the SSH RSA public key string. Your key should include three parts, for example: 'ssh-rsa AAAAB...snip...UcyupgH azureuser#linuxvm'"
}
}
},
"variables": {
"logAnalyticsWorkspaceId": "[resourceId(parameters('logAnalyticsResourceGroup'), 'Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]",
"containerInsightsName": "[concat(parameters('clusterName'),'-containerinsights')]"
},
"resources": [
{
"type": "Microsoft.ContainerService/managedClusters",
"name": "[parameters('clusterName')]",
"apiVersion": "2018-03-31",
"location": "[parameters('location')]",
"properties": {
"kubernetesVersion": "[parameters('kubernetesVersion')]",
"enableRBAC": "[parameters('enableRBAC')]",
"dnsPrefix": "[parameters('dnsPrefix')]",
"addonProfiles": {
"httpApplicationRouting": {
"enabled": "[parameters('enableHttpApplicationRouting')]"
},
"omsagent": {
"enabled": "[parameters('enableOmsAgent')]",
"config": {
"logAnalyticsWorkspaceResourceID": "[variables('logAnalyticsWorkspaceId')]"
}
}
},
"agentPoolProfiles": [
{
"name": "agentpool",
"osDiskSizeGB": "[parameters('osDiskSizeGB')]",
"count": "[parameters('agentCount')]",
"vmSize": "[parameters('agentVMSize')]",
"osType": "[parameters('osType')]",
"storageProfile": "ManagedDisks"
}
],
"linuxProfile": {
"adminUsername": "[parameters('vmAdminUsername')]",
"ssh": {
"publicKeys": [
{
"keyData": "[parameters('sshRsaPublicKey')]"
}
]
}
},
"servicePrincipalProfile": {
"clientId": "[parameters('servicePrincipalClientId')]",
"secret": "[parameters('servicePrincipalClientSecret')]"
},
"networkProfile": {
"networkPlugin": "[parameters('networkPlugin')]"
}
},
"dependsOn": [
"[concat('Microsoft.Resources/deployments/', 'SolutionDeployment')]"
]
},
{
"type": "Microsoft.Resources/deployments",
"name": "SolutionDeployment",
"apiVersion": "2017-05-10",
"resourceGroup": "[parameters('logAnalyticsResourceGroup')]",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"apiVersion": "2015-11-01-preview",
"type": "Microsoft.OperationsManagement/solutions",
"location": "[parameters('logAnalyticsWorkspaceLocation')]",
"name": "[variables('containerInsightsName')]",
"properties": {
"workspaceResourceId": "[variables('logAnalyticsWorkspaceId')]"
},
"plan": {
"name": "[variables('containerInsightsName')]",
"product": "OMSGallery/ContainerInsights",
"promotionCode": "",
"publisher": "Microsoft"
}
}
]
}
}
}
],
"outputs": {
"controlPlaneFQDN": {
"type": "string",
"value": "[reference(concat('Microsoft.ContainerService/managedClusters/', parameters('clusterName'))).fqdn]"
},
"sshMaster0": {
"type": "string",
"value": "[concat('ssh ', parameters('vmAdminUsername'), '#', reference(concat('Microsoft.ContainerService/managedClusters/', parameters('clusterName'))).fqdn, ' -A -p 22')]"
}
}
}

How I create a VM from a custom Image using Azure Resource Manager

I've been using the Azure Classic portal for a while and I'm able to create VMs from my custom images w/o a problem.
Now I'm trying to use the new portal, when I go to create a new VM I don't see the option to use my images. How do I create a VM from one of my images?
Here is how to do it...
Assuming your Resource group is created and my infra config are follows-
custom template uri path- https://myvmstore.blob.core.windows.net/vhds/CustomVHD.vhd
RG Name - myVMsRG
VNet Name - myVNET
VmName = mytestvm
userImageStorageAccountName = myvmstore
adminUsername = adminuser
adminPassword = PassWord123#
osDiskVhdUri = https://myvmstore.blob.core.windows.net/vhds/CustomVHD.vhd
dnsLabelPrefix = mytestvm
osType = Windows
vmSize = Standard_D2
newOrExistingVnet = existing
newOrExistingVnetName = myVNET
newOrExistingSubnetName = mySubnet
First you need json template. I have configured for myself in this format. You can copy the same code and save with name deployvm.json in your D drive. Note: If your VNET name is different from myVNET please hard code the same in the template at the same position.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"customVmName": {
"type": "string",
"metadata": {
"description": "This is the name of the your VM"
}
},
"userImageStorageAccountName": {
"type": "string",
"metadata": {
"description": "This is the name of the your storage account"
}
},
"osDiskVhdUri": {
"type": "string",
"metadata": {
"description": "Uri of the your user image"
}
},
"dnsLabelPrefix": {
"type": "string",
"metadata": {
"description": "DNS Label for the Public IP. Must be lowercase. It should match with the following regular expression: ^[a-z][a-z0-9-]{1,61}[a-z0-9]$ or it will raise an error."
}
},
"adminUserName": {
"type": "string",
"metadata": {
"description": "User Name for the Virtual Machine"
}
},
"adminPassword": {
"type": "securestring",
"metadata": {
"description": "Password for the Virtual Machine"
}
},
"osType": {
"type": "string",
"allowedValues": [
"Windows",
"Linux"
],
"metadata": {
"description": "This is the OS that your VM will be running"
}
},
"vmSize": {
"type": "string",
"metadata": {
"description": "This is the size of your VM"
}
},
"newOrExistingVnet": {
"allowedValues": [ "new", "existing" ],
"type": "string",
"metadata": {
"description": "Select if this template needs a new VNet or will reference an existing VNet"
}
},
"newOrExistingVnetName": {
"type": "string",
"defaultValue": "",
"metadata": {
"description": "New or Existing VNet Name"
}
},
"newOrExistingSubnetName": {
"type": "string",
"defaultValue": "Subnet1",
"metadata": {
"description": "Subnet Name"
}
}
},
"variables": {
"publicIPAddressName": "[parameters('customVmName')]",
"vmName": "[parameters('customVmName')]",
"nicName": "[parameters('customVmName')]",
"publicIPAddressType": "Dynamic",
"apiVersion": "2015-06-15",
"vnetID": "[resourceId('myVNET', 'Microsoft.Network/virtualNetworks', parameters('newOrExistingVnetName'))]",
"subnetRef": "[concat(variables('vnetID'),'/subnets/', parameters('newOrExistingSubnetName'))]",
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[variables('publicIPAddressName')]",
"location": "[resourceGroup().location]",
"properties": {
"publicIPAllocationMethod": "[variables('publicIPAddressType')]",
"dnsSettings": {
"domainNameLabel": "[parameters('dnsLabelPrefix')]"
}
}
},
{
"apiVersion": "2016-03-30",
"type": "Microsoft.Network/networkInterfaces",
"name": "[variables('nicName')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]"
},
"subnet": {
"id": "[variables('subnetRef')]"
}
}
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Compute/virtualMachines",
"name": "[variables('vmName')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
],
"properties": {
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"osProfile": {
"computerName": "[variables('vmName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]"
},
"storageProfile": {
"osDisk": {
"name": "[concat(variables('vmName'),'-osDisk')]",
"osType": "[parameters('osType')]",
"caching": "ReadWrite",
"createOption": "FromImage",
"image": {
"uri": "[parameters('osDiskVhdUri')]"
},
"vhd": {
"uri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/', parameters('userImageStorageAccountName')), variables('apiVersion')).primaryEndpoints.blob, 'vhds/',variables('vmName'), uniquestring(resourceGroup().id), 'osDisk.vhd')]"
}
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces',variables('nicName'))]"
}
]
},
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": "true",
"storageUri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/', parameters('userImageStorageAccountName')), variables('apiVersion')).primaryEndpoints.blob)]"
}
}
}
}
]
}
Now use below powershell code to create your VM-
$paramList =
#{
"Params1" = #{ customVmName = "mytestvm" ; userImageStorageAccountName = "myvmstore" ; adminUsername = "adminuser" ; adminPassword = "PassWord123#" ; osDiskVhdUri = "https://myvmstore.blob.core.windows.net/vhds/CustomVHD.vhd" ; dnsLabelPrefix = "mytestvm" ; osType ="Windows" ; vmSize = "Standard_D2" ; newOrExistingVnet = "existing" ; newOrExistingVnetName = "myVPN" ; newOrExistingSubnetName = "mySubnet"}
}
foreach ($keys in $paramList.Keys)
{
$paramvalues = $paramList.$keys
New-AzureRmResourceGroupDeployment -ResourceGroupName "myVMsRG" -TemplateFile "D:\deployvm.json" -TemplateParameterObject $paramValues
}
Reference- https://github.com/Azure/azure-quickstart-templates/tree/master/101-vm-from-user-image
You could use Powershell for that or an ARM Template. There is really too much code to paste here, but even if the links I post will change, its easily searchable.
Copy your Custom Images from Azure Classic portal to new Portal ( Here everything is stored under a Resource Group) Storage Account.
Now you will be able to Create new VM using your Custom images through Azure CLI or PowerShell or ARM Template.

Resources