2
votes

I'm trying to provision an App Service web app via PowerShell and an ARM template and connect the web app to a virtual network via a virtual network gateway (both also provisioned via ARM).

The template is reasonably straightforward. I have a single network security group to cut the vnet off from the public Internet.

{
            "comments": "NSG - DENY Internet",
            "type": "Microsoft.Network/networkSecurityGroups",
            "name": "[parameters('private_nsg')]",
            "apiVersion": "2016-03-30",
            "location": "[parameters('location')]",
            "properties": {
                "securityRules": [
                    {
                        "name": "allow-ssh",
                        "properties": {
                            "protocol": "TCP",
                            "sourcePortRange": "*",
                            "destinationPortRange": "22",
                            "sourceAddressPrefix": "Internet",
                            "destinationAddressPrefix": "*",
                            "access": "Allow",
                            "priority": 100,
                            "direction": "Inbound"
                        }
                    },
                    {
                        "name": "deny-internet",
                        "properties": {
                            "protocol": "TCP",
                            "sourcePortRange": "*",
                            "destinationPortRange": "*",
                            "sourceAddressPrefix": "Internet",
                            "destinationAddressPrefix": "*",
                            "access": "Deny",
                            "priority": 200,
                            "direction": "Inbound"
                        }
                    }
                ]
            },
            "resources": [],
            "dependsOn": []
        },

and then the virtual network itself with a subnet belonging to the above nsg and also a GateWay subnet.

{
            "comments": "VNet 1",
            "type": "Microsoft.Network/virtualNetworks",
            "name": "[parameters('vnet1')]",
            "apiVersion": "2016-03-30",
            "location": "[parameters('location')]",
            "properties": {
                "addressSpace": {
                    "addressPrefixes": [
                        "10.0.0.0/16"
                    ]
                },
                "subnets": [
                    {
                        "name": "private1",
                        "properties": {
                            "addressPrefix": "10.0.1.0/24",
                            "networkSecurityGroup": {
                                "id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('private_nsg'))]"
                            }
                        }
                    },
                    {
                        "name": "GatewaySubnet",
                        "properties": {
                            "addressPrefix": "10.0.100.0/24"
                        }
                    }
                ]
            },
            "resources": [],
            "dependsOn": [
                "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('private_nsg'))]"
            ]
        },

I then create an IP address for a virtual network gateway

{
            "comments": "Gateway 1 IP",
            "type": "Microsoft.Network/publicIPAddresses",
            "name": "[parameters('gateway1_ip')]",
            "apiVersion": "2016-03-30",
            "location": "[parameters('location')]",
            "properties": {
                "publicIPAllocationMethod": "Dynamic",
                "idleTimeoutInMinutes": 4
            },
            "resources": [],
            "dependsOn": []
        },

And the gateway itself

{
            "comments": "VNet1 Gateway",
            "apiVersion": "2015-05-01-preview",
            "type": "Microsoft.Network/virtualNetworkGateways",
            "name": "[parameters('gateway1')]",
            "location": "[parameters('location')]",
            "properties": {
                "ipConfigurations": [
                    {
                        "properties": {
                            "privateIPAllocationMethod": "Dynamic",
                            "subnet": {
                                "id": ""[concat(variables('vnet1_ref'),'/subnets/','GatewaySubnet')]""
                            },
                            "publicIPAddress": {
                                "id": "[resourceId('Microsoft.Network/publicIPAddresses',parameters('gateway1_ip'))]"
                            }
                        },
                        "name": "vnetGatewayConfig"
                    }
                ],
                "gatewayType": "Vpn",
                "vpnType": "RouteBased",
                "vpnClientConfiguration": {
                    "vpnClientAddressPool": {
                        "addressPrefixes": [ "192.168.2.0/24"]
                    }
                },
                "enableBgp": false
            },
            "dependsOn": [
                "[concat('Microsoft.Network/publicIPAddresses/', parameters('gateway1_ip'))]",
                "[concat('Microsoft.Network/virtualNetworks/', parameters('vnet1'))]"
            ]
        },

I also create a Linux VM and put it inside the private subnet and then create an App Service web app (and Plan) that knows how to perform a GET request against the Linux VM's private (10...*) IP.

Once the template deploys, I run some PowerShell based on this other Stack Overflow answer.

$app1 = Get-AzureRmWebApp -ResourceGroupName $ResourceGroupName -Name $parameters["appsvc1_name"]
$app1Config = Get-AzureRmResource -ResourceName "$($app1.Name)/web" -ResourceType "Microsoft.Web/sites/config" -ResourceGroupName $resourceGroupName -ApiVersion 2015-08-01
$vnet = Get-AzureRmVirtualNetwork -ResourceGroupName $resourceGroupName -Name "$resourceGroupName-Net"
$gateway = (Get-AzureRmVirtualNetworkGateway -ResourceGroupName $ResourceGroupName)[0]
$networkProperties = @{ "vnetResourceId" = $vnet.Id }
$networkConnection = New-AzureRmResource -ResourceGroupName $resourceGroupName -Location $resourceGroupLocation -Properties $networkProperties -ResourceName "$($app1.Name)/$($vnet.Name)" -ResourceType "Microsoft.Web/sites/virtualNetworkConnections" -ApiVersion 2015-08-01 -Force
$null = Add-AzureRmVpnClientRootCertificate -ResourceGroupName $resourceGroupName -VpnClientRootCertificateName "AppServiceCertificate.cer" -PublicCertData $networkConnection.Properties.CertBlob -VirtualNetworkGatewayName $gateway.Name

At this point, if I look at my app in the portal, it looks like it's connected to the gateway (the portal says "Connected" in green and the "Certificate Status" is "Certificates are synced"). However, my app can't connect to the private VM.

If I remove the network connection in the portal and then reconnect it in the portal, my app can connect to the VM. This leads me to believe that the gateway is configured correctly and it's just the connection that's gone wrong.

The only thing I'm missing from the previous StackOverflow answer is:

$vpnPackageUri = Get-AzureRmVpnClientPackage -ResourceGroupName $resourceGroupName -VirtualNetworkGatewayName $gateway.Name -ProcessorArchitecture Amd64
$vpnPackageUri = $vpnPackageUri.Replace('"', "")
$vpnPackageProperties = @{ "vnetName" = $vnet.Name; "vpnPackageUri" = $vpnPackageUri }
$networkConnectionVPN = New-AzureRmResource -ResourceGroupName $resourceGroupName -Location $resourceGroupLocation -ResourceName "$($app1.Name)/$($vnet.Name)/primary" -ResourceType "Microsoft.Web/sites/virtualNetworkConnections/gateways" -ApiVersion 2015-08-01 -Force 

Unfortunately, the last command errors out with a generic error message:

New-AzureRmResource : {"Message":"An error has occurred."}
At line:1 char:25
+ $networkConnectionVPN = New-AzureRmResource -ResourceGroupName $resourceGroupNam ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : CloseError: (:) [New-AzureRmResource], ErrorResponseMessageException
    + FullyQualifiedErrorId : InternalServerError,Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.NewAzureResourceCmdlet

So I can't tell what's actually gone wrong. Running this command doesn't allow my app to connect either; I have to manually remove the connection and re-add it in the portal.

What am I missing?

1
try running the cmdlet with -debug or -verbose to see detailed exceptionitaysk
If I remove the network connection in the portal and then reconnect it in the portal, my app can connect to the VM. - this leads me to believe you are missing something in your powershell.4c74356b41
Take a look at this question. It might help.Jack Zeng
@itaysk thanks for the suggestion; unfortunately, all it shows is that the command gets back a 500 Internal Server Error with a body of { "message": "An error has occurred." }James Williams

1 Answers

1
votes

I've done something very similar (except the VM was on Windows). I was following the MSDN article here and ended up in a similar situation. The portal said it the App was connected to the VPN, but there was no connection between the App and the VM. Connecting the App manually from the Portal fixed the problem.

You need to re-sync the network (from the App Service Plan / Networking tile).

There is an open issue on this here. They appear to not want to fix this, they are awaiting new features regarding the App Service - VPN integration.