Azure Virtual Networks with ARM and PowerShell

Now that I do all of the things in ARM (classic is so old and smelly you see), I am starting to get the hang of my PowerShell foo. Recently, I began working on the networking track and needed to create a Point to Site VPN gateway into my secured VMs. There's an excellent article on how to do this located here.

My challenge is that I'm a visual learner and sometimes step by step articles have a level of assumption on actual knowledge and skills. So while some may be able to follow the above instructions with ease, it took me a while because of certificate hell. In the old portal (smelly, remember?), one could just upload the root certificate that all the client certs were based off. Now, PowerShell all the things! So here we go:

First, I've abandoned the Azure PowerShell MSI in favor of the new PowerShell Gallery. Just issue the commands:

Install-Module AzureRM
Install-AzureRM
Import-AzureRM

(Update: 02/16/2016 with info on which way is which located here).

This will install all the PowerShells and makes it even easier to keep them updated. One thing to note though - in subsequent PowerShell sessions, don't ipmo azure - that will barf things up, always use Import-AzureRM

Then, login with the standard:
Add-AzureRmAccount (with the optional -SubscriptionID if you are in lots of subs land. Remember, always use AAD if you can - MSA gets funny with multiple subscriptions!)

The next section is to set up lots of variables. I've simplified the network setup here instead of the rather awesomely complex network from the blog article.

$VNetName = "VNet"
$SubName = "FrontEnd"
$GWSubName = "GatewaySubnet"
$VNetPrefix = "192.168.0.0/16"
$SubPrefix = "192.168.1.0/24"
$GWSubPrefix = "192.168.200.0/26"
$VPNClientAddressPool = "172.16.201.0/24"
$RG = "TestRG"
$Location = "West US"
$DNS = "208.67.222.222"
$GWName = "GW"
$GWIPName = "GWIP"
$GWIPconfName = "gwipconf"
$P2SRootCertName = "RootCertificateName"

A few notes: First, $DNS -> you'll want this to be a DNS server that will resolve your stuff. You can update it later but you need it to set up. I've used OpenDNS cause Google's DNS is poop. Next, the $P2SRootCertName we haven't generated yet, that's coming up in a sec. For now, just know you have to have a cert.

Next:

1. Create the Resource Group:
New-AzureRmResourceGroup -Name $RG -Location $Location

2. Create the Subnets:
$sub = New-AzureRmVirtualNetworkSubnetConfig -Name $SubName -AddressPrefix $SubPrefix
$gwsub = New-AzureRmVirtualNetworkSubnetConfig -Name $GWSubName -AddressPrefix $GWSubPrefix

3. Create the Virtual Network based on those Subnets:
$vnet = New-AzureRmVirtualNetwork -Name $VNetName -ResourceGroupName $RG -Location $Location -AddressPrefix $VNetPrefix -Subnet $sub, $gwsub -DnsServer $DNS

4. Add a public IP address to the Gateway subnet:
$subnet = Get-AzureRmVirtualNetworkSubnetConfig -Name "GatewaySubnet" -VirtualNetwork $vnet
$pip = New-AzureRmPublicIpAddress -Name $GWIPName -ResourceGroupName $RG -Location $Location -AllocationMethod Dynamic
$ipconf = New-AzureRmVirtualNetworkGatewayIpConfig -Name $GWIPconfName -Subnet $subnet -PublicIpAddress $pip

Now at this point, you have a decent looking setup:

Next up is the part that threw me off...way way off. I need some certs. To do that, fire up a Visual Studio command prompt and run these two commands.

First - This command creates the Root Certificate that all your client certificates will be based on. This allows certificate based auth because the clients will be trusted if they are signed by the root. The OUTPUT of this command is nothing special, but it's important to know that you now have a cert in your PERSONAL cert store.

makecert -sky exchange -r -n "CN=RootCertificateName" -pe -a sha1 -len 2048 -ss My "RootCertificateName.cer"

Like so:

Now, before anything else, you need to EXPORT that certificate to a file (named RootCertificateName.cer in my example) with the following options:
*Do NOT export the private key
*File format should be BASE 64 ENCODED (the second option)

*Then name it RootCertificateName.cer

Next, run this second command:

makecert.exe -n "CN=ClientCertificateName" -pe -sky exchange -m 96 -ss My -in "RootCertificateName" -is my -a sha1

This command creates a certificate that is signed by the RootCertificate and places it in the cert store also. You need to EXPORT THIS TO FILE too with these options
*DO export the private key
*Leave the second page options as they are:


*Name the file whatever - you will INSTALL this certificate on a client machine that you want to use your VPN. More on that later.
*Make sure to remember the password you set here, you'll need it to import!

Okay, now we can move back to configuring our stuff in PowerShell. We need to get a Base64 string of the root certificate contents to upload to Azure. This part took some time because the instructions on the referenced blog post are not clear on how best to accomplish that.

Create the Virtual Network Gateway:
New-AzureRmVirtualNetworkGateway -Name $GWName -ResourceGroupName $RG -Location $Location -IpConfigurations $ipconf -GatewayType Vpn -VpnType RouteBased -EnableBgp $false -GatewaySku Standard -VpnClientAddressPool $VPNClientAddressPool
(This takes a while, sometimes a LONG while)

Next, get the cert contents minus the first and last line (which are the headers, we don't care about that):

$Text = Get-Content -Path "C:\RootCertificateName.cer"
$CertificateText = for ($i=1; $i -lt $Text.Length -1 ; $i++){$Text[$i]}

Now, add the root certificate to the Virtual Gateway:
Add-AzureRmVpnClientRootCertificate -PublicCertData ($CertificateText | out-string) -ResourceGroupName $RG -VirtualNetworkGatewayName $GWName -VpnClientRootCertificateName $P2SRootCertName

BIG note here - this took me forever - the $CertificateText is of type Object, not String, so in the Add-AzureRmVpnClientRootCertificate command, I had to pipe it to out-string to get the command to go otherwise you get a type fail.

Now that you have everything setup, you'll need to get the VPN client. This client gets generated for every VNet Gateway and is stored in a special BLOB account that MSFT controls. In order to get it, issue this command:
Get-AzureRmVpnClientPackage -ResourceGroupName $RG -VirtualNetworkGatewayName $GWName -ProcessorArchitecture Amd64

Copy the string that it returns and download it.

The next step, now that you have all the goodness setup is to move over to a client machine you want to be on the Azure network. Bring the VPN client and the client certificate PFX file with you.

First, install the certificate by double clicking on it. Install to Current User:

Enter your password that you used when you exported the cert and leave all other options as is on this page:

Most importantly, place the cert in the PERSONAL store:

Finally, just install the VPN client you downloaded, you don't need to configure anything. You may get Windows Untrusted Source warnings - they are okay to ignore in this instance as the install is generated by Microsoft. After you install, just hit connect, it will do cert auth and you're up and going!

Note, the first time you connect, you'll need to elevate to let the VPN tool do admin'y things:

#Huzzah!
derekmartin.org