Terraform-provider-azurerm: App Service VNET integration not working

Created on 6 Nov 2018  ·  10Comments  ·  Source: terraform-providers/terraform-provider-azurerm

When creating an app service I'm attempting to attach it to a virtual network using the following syntax

site_config {
        virtual_network_name = "${azurerm_virtual_network.default.name}"
    }

Terraform runs as expected with no errors. However it doesn't seem to be taking as you can see from the attached screenshot:

screenshot 2018-11-06 at 09 27 27

Full tf file is below:

  resource "azurerm_resource_group" "default" {
    name     = "vnet-test-terraform"
    location = "UK West"
  }
  resource "azurerm_virtual_network" "default" {
    name                = "vnet-test-terraform-vn"
    address_space       = ["10.1.0.0/16"]
    location            = "UK West"
    resource_group_name = "${azurerm_resource_group.default.name}"
  }

  resource "azurerm_subnet" "subnet" {
    name                 = "Default"
    resource_group_name  = "${azurerm_resource_group.default.name}"
    virtual_network_name = "${azurerm_virtual_network.default.name}"
    address_prefix       = "10.1.0.0/24"
  }

  resource "azurerm_subnet" "gateway_subnet" {
    name                 = "GatewaySubnet"
    resource_group_name  = "${azurerm_resource_group.default.name}"
    virtual_network_name = "${azurerm_virtual_network.default.name}"
    address_prefix       = "10.1.1.0/24"
  }

  resource "azurerm_public_ip" "default" {
    name                         = "vnet-test-terraform-ip"
    location                     = "UK West"
    resource_group_name  = "${azurerm_resource_group.default.name}"
    public_ip_address_allocation = "Dynamic"
  }

  resource "azurerm_virtual_network_gateway" "default" {
    name                = "vnet-test-terraform-vng"
    location            = "UK West"
    resource_group_name  = "${azurerm_resource_group.default.name}"
    type                = "Vpn"
    sku                 = "VpnGw1"

    ip_configuration {
      private_ip_address_allocation = "Dynamic"
      public_ip_address_id          = "${azurerm_public_ip.default.id}"
      subnet_id                     = "${azurerm_subnet.gateway_subnet.id}"
    }

    vpn_client_configuration {
      address_space        = ["10.0.1.0/24"]
      vpn_client_protocols = ["SSTP"]
    }
  }

  resource "azurerm_app_service_plan" "default" {
    name                = "vnet-test-terraform-asp"
    location            = "UK West"
    resource_group_name  = "${azurerm_resource_group.default.name}"

    sku {
      tier = "Standard"
      size = "S1"
    }
  }

  resource "azurerm_app_service" "default" {
    name                = "vnet-test-terraform-as"
    location            = "UK West"
    resource_group_name  = "${azurerm_resource_group.default.name}"
    app_service_plan_id = "${azurerm_app_service_plan.default.id}"

    site_config {
        virtual_network_name = "${azurerm_virtual_network.default.name}"
    }
  }
bug servicapp-service upstream-microsoft

All 10 comments

If they are of any worth here are the templates from when did it manually compared to how it looked with TF

Actual: https://gist.github.com/mat-mcloughlin/ea8b760452ffacf20d97f2e943634d9d
Expected: https://gist.github.com/mat-mcloughlin/1a618ca8eb1bf14b732492317d6bf745

I believe in order to host App Service on a vnet, you need to stand up an App Service Environment, attach plan(s) to that, then attach app services to those plans.

Unfortunately there's no official ASE resource in terraform for this, so you need to use azurerm_template_deployment (ARM template)

hey @mat-mcloughlin

Thanks for opening this issue :)

Digging into how the Portal achieves this - it appears the Azure Portal calls out to Kudu, which is the API hosted on the App Service to configure this Virtual Network Connection, rather than an API within Resource Manager. In our case that makes the following request:

URL: https://web1.appsvcux.ext.azure.com/api/Websites/AddExistingV2VirtualNetworkToSite

{
    "WebAppResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tom-devrg/providers/Microsoft.Web/sites/tomdev-appservice",
    "VnetResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tom-devrg/providers/Microsoft.Network/virtualNetworks/tom-devvn",
    "VnetGatewayResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tom-devrg/providers/Microsoft.Network/virtualNetworkGateways/tom-devvng",
    "VnetLocation": "westeurope"
}

Unfortunately at this time there's no Golang API Client for Kudu (and Kudu itself isn't overly well documented) - but I'd requested support in this issue - which I'd recommend subscribing to for updates.

Thanks!

I am seeing the same issue attached the app service to vnet...
Is this issues resolved?

Note the documentation indicates it should work.

Since it does not actually work, you end up with the App Service not in the Vnet you expect, which means it's not protected by the network security group that it is documented to have. Boom, security issue.

Hi,

I use the following workaround for this. My VPN gateway terraform module creates two resources:
VNET gateway module.

  1. vnet gateway.
  2. App Service (with app service plan)
    I use script to create app service, because additional i have a code that adding my newly created app service to vnet:
$virtualNetwork = New-AzureRmResource -Location $Location 
                                      -Properties $propertiesObject 
                                      -ResourceName "$($GatewayName)/$($VnetName)" 
                                      -ResourceType "Microsoft.Web/sites/virtualNetworkConnections" 
                                      -ApiVersion 2015-08-01 
                                      -ResourceGroupName $ResourceGroup -Force 

After this new certificate will be generated by azure.
Then i run this code to add app service certificate that will be used by all app service in future to vnet gateway:

    Add-AzureRmVpnClientRootCertificate -VpnClientRootCertificateName "AppServiceCertificate.cer" 
                                        -PublicCertData $virtualNetwork.Properties.CertBlob 
                                        -VirtualNetworkGatewayName $GatewayName 
                                        -ResourceGroupName $ResourceGroup

The certificate should be added exactly with this name - AppServiceCertificate.cer

App service module.

My app service terraform module for app service include terraform code for app service creation and null_resource block that run powershell script that adding app service to vnet.
This approach was tested before terraform adding support for this property:

site_config {
        virtual_network_name = "${azurerm_virtual_network.default.name}"
}
   ```
----------------------------------------------------------------------
Code for app service gateway certificate configuration

param (
$AADSecret = "",
$AADClientID = "",
$TenantID = "",
$ResourceGroup = "",
$Location = "",
$SubscriptionID = "",
$GatewayName = "",
$VnetName = ""
)
Import-Module AzureRM.Websites
Import-Module AzureRM.Profile

create sessiong for azure login

$securityString = $AADSecret | ConvertTo-SecureString -Force -AsPlainText
$credential = New-Object System.Management.Automation.PsCredential("$AADClientID",$securityString)
$session = Get-Credential -Credential $credential;

login to azure account

Connect-AzureRmAccount -Credential $session -TenantId $TenantID
-ServicePrincipal `
-Subscription $SubscriptionID *>$null

-----------------------------------------------------------[Execution]------------------------------------------------------------

$vnetObj = Get-AzureRmVirtualNetwork -Name $VnetName -ResourceGroupName $ResourceGroup
Write-Host "Creating App association to VNET"
$propertiesObject = @{
"vnetResourceId" = "$($vnetObj.Id)"
}
if (-not (Get-AzureRmWebApp -ResourceGroupName $ResourceGroup -Name $GatewayName -ErrorAction SilentlyContinue )) {
New-AzureRmAppServicePlan -ResourceGroupName $ResourceGroup -Location $Location
-Tier Standard `
-Name "$($GatewayName)" -ErrorAction SilentlyContinue

New-AzureRmWebApp -ResourceGroupName $ResourceGroup -Name "$($GatewayName)"
-Location $Location -AppServicePlan "$($GatewayName)" -ErrorAction SilentlyContinue } else { $virtualNetwork = New-AzureRmResource -Location $Location
-Properties $propertiesObject -ResourceName "$($GatewayName)/$($VnetName)"
-ResourceType "Microsoft.Web/sites/virtualNetworkConnections" -ApiVersion 2015-08-01
-ResourceGroupName $ResourceGroup -Force
}

$virtualNetwork = New-AzureRmResource -Location $Location -Properties $propertiesObject
-ResourceName "$($GatewayName)/$($VnetName)" -ResourceType "Microsoft.Web/sites/virtualNetworkConnections"
-ApiVersion 2015-08-01 `
-ResourceGroupName $ResourceGroup -Force

$oldCert = Get-AzureRmVpnClientRootCertificate -VpnClientRootCertificateName "AppServiceCertificate.cer" -VirtualNetworkGatewayName $GatewayName
-ResourceGroupName $ResourceGroup

if ($oldCert.PublicCertData -eq $virtualNetwork.Properties.CertBlob) {
Write-host "Certificate already present on gateway"
} else {
if ($oldCert.PublicCertData.Length -ne 0) {
write-host "Remove AppServiceCertificate.cer from gateway and create new. "
Remove-AzureRmVpnClientRootCertificate -VpnClientRootCertificateName "AppServiceCertificate.cer" -PublicCertData $oldCert.PublicCertData
-VirtualNetworkGatewayName $GatewayName -ResourceGroupName $ResourceGroup } Add-AzureRmVpnClientRootCertificate -VpnClientRootCertificateName "AppServiceCertificate.cer"
-PublicCertData $virtualNetwork.Properties.CertBlob -VirtualNetworkGatewayName $GatewayName
-ResourceGroupName $ResourceGroup
}

**Terraform** 

resource "null_resource" "gateway_cert_for_webapp" {
triggers {
id = "${azurerm_virtual_network_gateway.gateway.id}"
trigger_null_resource = "${var.trigger_null_resource}"
trigger_sync_webapp_cert = "${var.trigger_sync_webapp_cert}"
}

provisioner "local-exec" {
when = "create"

command = <<EOF
    powershell -file ${path.module}\Update-AppServiceGatewayCertificate.ps1 -AADSecret ${var.AADSecret}
                                                                            -AADClientID ${data.azurerm_client_config.current.client_id}
                                                                            -TenantID ${data.azurerm_client_config.current.tenant_id}
                                                                            -ResourceGroup ${var.resource_group_name}
                                                                            -Location ${var.location}
                                                                            -SubscriptionID ${data.azurerm_client_config.current.subscription_id}
                                                                            -GatewayName ${var.name}
                                                                            -VnetName ${var.vnet_name}

EOF

interpreter = ["PowerShell", "-Command"]

}

lifecycle {
create_before_destroy = false
}
}


**and code for app service vnet integration** 

param (
$VnetName = "${VnetName}",
$GatewayName = "${GatewayName}",
$ResourceGroup = "${ResourceGroup}",
$Location = "${Location}",
$WebApp = "${WebApp}",
$AADSecret = "${AADSecret}",
$AADClientID = "${AADClientID}",
$TenantID = "${TenantID}",
$SubscriptionId = "${SubscriptionId}"
)
$securityString = $AADSecret | ConvertTo-SecureString -Force -AsPlainText
$credential = New-Object System.Management.Automation.PsCredential("$AADClientID",$securityString)
$session = Get-Credential -Credential $credential;

Connect-AzureRmAccount -Credential $session -TenantId $TenantID -ServicePrincipal -Subscription $SubscriptionID *>$null

$vnetName = $VnetName
$vpnRecieved = $false
do {
$vpnClient = Get-AzureRmVpnClientPackage -ResourceGroupName $ResourceGroup -VirtualNetworkGatewayName $GatewayName -ProcessorArchitecture Amd64 -ErrorAction SilentlyContinue

    if ($vpnClient -like '"*.blob.core.windows.net*"') {
    $packageUri = $vpnClient.ToString(); 
    $packageUri = $vpnClient.Substring(1, $vpnClient.Length-2);
    $vpnPackageUri = $packageUri
    $vpnRecieved = $true
    Write-Host "Vpn package has been recieved"
    }

} while ($recieved -eq $false)

$PropertiesObject = @{
"vnetName" = $vnetName;
"vpnPackageUri" = $vpnPackageUri
}
New-AzureRmResource -Location $Location -Properties $PropertiesObject
-ResourceName "$($WebApp)/$($vnetName)/primary" -ResourceType "Microsoft.Web/sites/virtualNetworkConnections/gateways"
-ApiVersion 2015-08-01 -ResourceGroupName $ResourceGroup -Force`

**and terraform code** 

data "template_file" "New-AppServiceVnetIntegration" {
template = "${file("${local.script_path}/New-AppServiceVnetIntegration.ps1")}"
vars = {
VnetName = "${var.virtual_network_name}",
GatewayName = "${var.gateway_name}",
ResourceGroup = "${var.resource_group_name}",
Location = "${var.location}",
WebApp = "${var.name}",
AADSecret = "${var.AADSecret}",
AADClientID = "${data.azurerm_client_config.current.client_id}",
TenantID = "${data.azurerm_client_config.current.tenant_id}",
SubscriptionId = "${data.azurerm_client_config.current.subscription_id}"
}
}


resource "null_resource" "New-AppServiceVnetIntegration" {
depends_on = ["azurerm_app_service.app_service"]
triggers {
id = "${azurerm_app_service.app_service.id}"
vnet_integration = " ${data.template_file.New-AppServiceVnetIntegration.rendered}"
}
provisioner "local-exec" {
command = < ${data.template_file.New-AppServiceVnetIntegration.rendered}
EOF
interpreter = ["PowerShell", "-Command"]
}
}
```

👋

Taking a look into this - it appears the virtual_network_name field has now been repurposed to require the ResourceGUID and the Subnet name combined, such that I believe this should now be fixed via #2325 - as such I'm going to close this issue in favour of that one.

Thanks!

Using the format vnetResourceGuid_subnetName for the virtual_network_name doesn't work for me. No errors occurs but the azure portal shows no VNet integration configured (same as the OP screenshot)

Can anyone confirm this has worked previously?

@robck attaching App Services to a Virtual Network's gone through a few iterations on the Azure end unfortunately.

After chatting with the service team a while back it appears that the virtual_network_name field exposed in the API is a previous integration attempt which was really only available through the Kudu API's, which has since been superseded by a new integration method which is being added in #4250

Thanks!

I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 [email protected]. Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hashibot picture hashibot  ·  43Comments

hashibot picture hashibot  ·  48Comments

ben-lings-tessella picture ben-lings-tessella  ·  30Comments

shivamsriva31093 picture shivamsriva31093  ·  47Comments

TPPWC picture TPPWC  ·  55Comments