L2TP VPN via ARM template in Azure

Virtual Network Gateway in Azure (native Azure VPN solution) have numerous drawbacks vs hosted VPN solution. I would say following are main drawbacks of native VPN solution:

  • Long time to provision (per documentation you expect up to 45 minute provisioning time)
  • Expensive if you need higher bandwidth or bigger number of simultaneous clients
  • Takes at least /29 IP address space
  • VPN client can not be used remote gateway mode (split tunnel disabled). As a result:
    • Can not use Azure provided DNS service (
    • Can not use directly private endpoints by their names
    • Security risk since VPN client becomes a switch connecting external Internet with your VNET
  • Can not limit incoming connections based on IP addresses (NSGs are not allowed on VPN gateway subnets)
  • Can not use simple username/password authentication (requires either certificate based authentication or Azure AD based)
  • Requires to download and install file from Azure or Microsoft store

Solution outlined below deploys VPN server based on Windows 2019 Server core image with RRAS service installed with L2TP VPN. I tested it on smallest VM compute size (Standard_B1s) and had no issues reaching 200MBps. Server Core install runs surprisingly good on 1CPU/1GB RAM. Total monthly cost of config is $12 which is half the price of the cheapest tier of Azure VPN gateway with no licenses limitation for either bandwidth or number of concurrent sessions.

Windows 10 client requires following AssumeUDPEncapsulationContextOnSendRule with value of 2 and reboot. You can set this key with powershell below

Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\PolicyAgent AssumeUDPEncapsulationContextOnSendRule -Type DWord -Value 2 -Force

To force VPN connection to use VPN provided DNS service interface metric for Ethernet adapter of client shall be set to higher number then VPN client one. Substitute name of your adapter which you can get by executing Get-NetIPInterface | Select-Object -Property InterfaceAlias, InterfaceMetric

Set-NetIPInterface -InterfaceAlias "Ethernet" -InterfaceMetric 30

Complete ARM template is available at https://github.com/artisticcheese/Azure/tree/main/ARM-L2TP

Template consists of following resources:

  • Network security group which allows only L2TP VPN protocol ports
  • VM with Windows2019-ServerCore-smalldisk SKU
  • Custom script extension which configures server for VPN use
  • Template outputs FQDN for public IP of server for configuration of VPN client

Steps to deploy custom VPN server:

  1. Download template and parameters file from repo
  2. Modify parameters file which matches your network and with your desired password for preshared key and password for user account
  3. Execute powershell to start deployment
 New-AzResourceGroupDeployment -ResourceGroupName RRAS-RG -TemplateFile .\ARM-L2TP\template.json -TemplateParameterFile .\ARM-L2TP\template.parameters.json -Verbose
  1. As part of deployment server is rebooted and bearing in mind server is of smallest size you shall expect total execution time of about 10-20 minutes
  2. Once execution completes you need to configure Windows 10 VPN client:
  • Navigate start menu and type VPN, click Add VPN Connection button
  • Fill in the menu name, for server address use FQDN output of ARM template. For VPN type L2TP/IpSec with preshared key and username/password from parameters file

You shall be able now to connect to VPN and test functionality.

  1. Check that you route through VPN by checking what your connection appear as coming from when reaching Internet
PS C:\Users\artis> Invoke-RestMethod http://ipinfo.io | select Region

  1. Check if you can resolve private endpoint IP addresses to internal addresses. I created storage account and created private endpoint for it which shall resolve to IP address on my VNET.
PS C:\Users\artis> resolve-dnsname rrasstorageaccount.blob.core.windows.net

Name                           Type   TTL   Section    NameHost
----                           ----   ---   -------    --------
rrasstorageaccount.blob.core.w CNAME  60    Answer     rrasstorageaccount.privatelink.blob.core.windows.net

Name       : rrasstorageaccount.privatelink.blob.core.windows.net
QueryType  : A
TTL        : 1800
Section    : Answer
IP4Address :

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s