Azure VPN with Windows Server 2012 R2 – Routing & Remote Access

My other blog post talked about setting up an Azure Virtual Network along with a Local network gateway, Public IP address and Virtual Network Gateway in the same Resource Group.

The other end to this is setting up the on-prem side of the VPN tunnel.

As I am a Microsoft person, here is a PowerShell script below to setup the Azure site-to-site VPN tunnel on a Microsoft firewall – Windows Server 2012 R2 (Routing & Remote Access). Now all the network admins of this world will be cursing at me for using a Micosoft product as a firewall, but you know what? It works, easy to setup and works just like ISA/TMG did. Why not use it. Let’s face it, both ISA and TMG were good products and I think RAS is a cut down slim verson of those products.

You need a double holmed server with Windows Server 2012 R2, two NICs: one NIC in a public or internet acessible network, this NIC has a default gateway IP address set on it. The other NIC is to be plugged into the internal (trusted) network, this NIC doesn’t have a default gateway IP address set on it.

The server will sit in a workgroup and would be a good idea to also be a DHCP server and the internal IP address of the server to be used as the default gateway IP address for all servers on the internal network.  The Server can also be setup as a server to do NAT (Network Address Translation) inbound with the settings as per:


The screenshot below explains how to add in the NAT rules on the external NIC “Ethernet 2“.


Here is the PowerShell to setup the Azure VPN part, change the parts in the variables to suit.

$AzureGatewayIP = ''
$VpnS2SInterfaceName = 'VPN-2-Azure'
$IPv4Subnet = @("")
$SharedSecret = '4wer64erh0js35u4689'

Function Invoke-WindowsApi(
[string] $dllName,
[Type] $returnType,
[string] $methodName,
[Type[]] $parameterTypes,
[Object[]] $parameters
## Begin to build the dynamic assembly
$domain = [AppDomain]::CurrentDomain
$name = New-Object Reflection.AssemblyName 'PInvokeAssembly'
$assembly = $domain.DefineDynamicAssembly($name, 'Run')
$module = $assembly.DefineDynamicModule('PInvokeModule')
$type = $module.DefineType('PInvokeType', "Public,BeforeFieldInit")

$inputParameters = @()

for($counter = 1; $counter -le $parameterTypes.Length; $counter++)
$inputParameters += $parameters[$counter - 1]

$method = $type.DefineMethod($methodName, 'Public,HideBySig,Static,PinvokeImpl',$returnType, $parameterTypes)

## Apply the P/Invoke constructor
$ctor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([string])
$attr = New-Object Reflection.Emit.CustomAttributeBuilder $ctor, $dllName

## Create the temporary type, and invoke the method.
$realType = $type.CreateType()

$ret = $realType.InvokeMember($methodName, 'Public,Static,InvokeMethod', $null, $null, $inputParameters)

return $ret

Function Set-PrivateProfileString(
## Prepare the parameter types and parameter values for the Invoke-WindowsApi script
$parameterTypes = [string], [string], [string], [string]
$parameters = [string] $category, [string] $key, [string] $value, [string] $file

## Invoke the API
[void] (Invoke-WindowsApi "kernel32.dll" ([UInt32]) "WritePrivateProfileString" $parameterTypes $parameters)

### Install RRAS role
Import-Module ServerManager
Install-WindowsFeature RemoteAccess -IncludeManagementTools
Add-WindowsFeature -name Routing -IncludeManagementTools

### !!! NOTE: A reboot of the machine might be required here after which the script can be executed again.

### Install S2S VPN
Import-Module RemoteAccess
if ((Get-RemoteAccess).VpnS2SStatus -ne "Installed")
Install-RemoteAccess -VpnType VpnS2S

### Add and configure S2S VPN interface
Add-VpnS2SInterface -Protocol IKEv2 -AuthenticationMethod PSKOnly -NumberOfTries 3 -ResponderAuthenticationMethod PSKOnly -Name $VpnS2SInterfaceName -Destination $AzureGatewayIP -IPv4Subnet $IPv4Subnet -SharedSecret $SharedSecret
Set-VpnServerIPsecConfiguration -EncryptionType MaximumEncryption
Set-VpnS2Sinterface -Name $VpnS2SInterfaceName -InitiateConfigPayload $false -Force

### Set S2S VPN connection to be persistent by editing the router.pbk file (required admin priveleges)
Set-PrivateProfileString $env:windir\System32\ras\router.pbk $VpnS2SInterfaceName "IdleDisconnectSeconds" "0"
Set-PrivateProfileString $env:windir\System32\ras\router.pbk $VpnS2SInterfaceName "RedialOnLinkFailure" "1"

### Restart the RRAS service
Restart-Service RemoteAccess

### Dial-in to Azure gateway
Connect-VpnS2SInterface -Name $VpnS2SInterfaceName

For a complete listing of intructions in setting up the on-prem tunnel side with other VPN devices or firewalls, see

Leave a Reply

Please log in using one of these methods to post your comment: Logo

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

Facebook photo

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

Connecting to %s