Scott
Scott

Reputation: 23

How do I deploy a Application Gateway with VMs behind it

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?

Upvotes: 0

Views: 1138

Answers (1)

TobiWi
TobiWi

Reputation: 21

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

Upvotes: 1

Related Questions