Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App Service VNET integration not working #2254

Closed
mat-mcloughlin opened this issue Nov 6, 2018 · 10 comments
Closed

App Service VNET integration not working #2254

mat-mcloughlin opened this issue Nov 6, 2018 · 10 comments
Labels
bug service/app-service upstream/microsoft Indicates that there's an upstream issue blocking this issue/PR

Comments

@mat-mcloughlin
Copy link

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}"
    }
  }
@mat-mcloughlin
Copy link
Author

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

@grayzu grayzu self-assigned this Nov 6, 2018
@test-in-prod
Copy link

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)

@grayzu grayzu removed their assignment Nov 6, 2018
@tombuildsstuff tombuildsstuff self-assigned this Nov 8, 2018
@tombuildsstuff
Copy link
Member

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!

@tombuildsstuff tombuildsstuff added the upstream/microsoft Indicates that there's an upstream issue blocking this issue/PR label Nov 8, 2018
@tombuildsstuff tombuildsstuff added this to the Blocked milestone Nov 8, 2018
@tombuildsstuff tombuildsstuff removed their assignment Nov 8, 2018
@ravulachetan
Copy link

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

@sensei-hacker
Copy link

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.

@siobam
Copy link

siobam commented Apr 23, 2019

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 = <<EOF
        ${data.template_file.New-AppServiceVnetIntegration.rendered}
    EOF
    interpreter = ["PowerShell", "-Command"]
  }
}

@tombuildsstuff
Copy link
Member

👋

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!

@robck
Copy link

robck commented Oct 2, 2019

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?

@tombuildsstuff
Copy link
Member

@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!

@tombuildsstuff tombuildsstuff removed this from the Blocked milestone Oct 3, 2019
@ghost
Copy link

ghost commented Oct 3, 2019

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 👉 hashibot-feedback@hashicorp.com. Thanks!

@ghost ghost locked and limited conversation to collaborators Oct 3, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug service/app-service upstream/microsoft Indicates that there's an upstream issue blocking this issue/PR
Projects
None yet
Development

No branches or pull requests

8 participants