Azure VPN Gateway Basic Sku.. The poor man’s P2S VPN solution. Or is it?
If you check the notes on the Azure VPN Basic Sku P2S solution:
This table is taken from https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-about-vpngateways and it shows the limitations of the Basic Sku. The table goes on and on, but I just wanted to show the Basic Sku’s properties. Specifically the P2S options. SSTP only, 128 (simultaneous) connections. More than enough for small businesses. Or home labs. BGP not supported, which means you need to take care of routing tables yourself. A maximum of 10 S2S tunnels, which means you can connect 10 sites to the Virtual Network holding the VPN Gateway deployment. Again, more than enough for small businesses. Or home Labs.
Looking at pricing:
This table is taken from https://azure.microsoft.com/en-us/pricing/details/vpn-gateway/ and it shows pricing in Euros for West Europe. Basic Sku is by far the cheapest solution.
In short, Basic Sku seems like the perfect solution for small businesses and especially for home Labs.
The only thing I don’t like about the Basic Sku is the P2S Client options you get out of the box when you deploy the solution. The out of the box solution and how to get there is not in scope of this post. You can find more info and instructions in some of my other posts:
https://msfreaks.wordpress.com/2015/12/07/building-an-azure-lab-implementing-p2s-point-to-site-vpn/ (Classic, but shows the basic implementation).
https://msfreaks.wordpress.com/2015/12/11/building-an-azure-lab-customizing-the-p2s-point-to-site-vpn-client/
https://msfreaks.wordpress.com/2015/12/03/building-an-azure-lab-implementing-s2s-site-to-site-vpn/
https://msfreaks.wordpress.com/2017/11/28/building-an-azure-lab-implementing-s2s-site-to-site-vpn-using-azure-portal-or-powershell/
https://msfreaks.wordpress.com/2017/12/02/building-an-azure-lab-implementing-s2s-site-to-site-vpn-part-2-multi-virtual-networks/
So I started some research on how to effectively deploy and manage the VPN client (on Windows 10) when using the Azure VPN Gateway Basic Sku.
For reference, this is the setup for my VPN infrastructure, showing the managed Windows 10 client on which I want to deploy the P2S VPN connection:
First, let’s have a look at why I don’t like the out of the box VPN client options.
You can download your VPN client package in the Point-to-site configuration blade for your VPN Gateway. I specifically say “your” because this package is tailored for this VPN Gateway, and this VPN Gateway only. That is why it takes a little while before your download starts.
The download is a zip file, which once extracted, shows 3 client options:
A “Generic” “Client”, a x64 client package, and a x86 client package. The Generic client simply shows an xml file and the Gateway certificate. Examine the xml file, it will tell you all sort of configuration stuff for the VPN Client:
From this we can get the actual VPN Gateway address, the type, which is SSTP, the routes that are needed, and the authentication method that is needed.
Creating a PKI, and getting the right certificates installed is out of scope of this post.
Let’s check the obvious choice, the x64 client package.
Installing the package in the user context:
Oops. Unsigned, meaning crappy user experience. Ignoring that for now, click “More info” and click “Run anyway”.
Oops. Need elevated administrator access to install this.
Let’s see if it installs machine-wide using an administrator account. Logging on with an administrator and running the same setup again:
So no pesky SmartScreen popup for administrators.
VPN Connection available.
Now logging back in with the normal user, without administrative permissions:
No VPN Connection. So the installation of the VPN Client package is not a machine-wide installer, when using an administrative account. It shows up for the administrator I am logged on with, so it will also not show up for a normal user. Spoiler-alert: even running it as System, using psexec, it will not install machine-wide.
So how to install the connection in the user context, or how to install the connection machine wide, and of course, I want it to be unattended.
Looking around my favorite wiki (google.com) to look for solutions other people have come up with, I found this excellent article which got me started: https://www.risual.com/2018/07/05/manually-configure-azure-point-to-site-on-windows-10/.
I have two issues with this solution though.
First one is a pesky (one-time) certificate warning.
For which a workaround could probably be found. It’s a one-time thing, it has to be registered somewhere right? But the second problem with this solution, and one that prevented me from trying to figure out how to get rid of the first one, is that you need to add routes to tell your machine where to route packets if they are meant to go to the Azure Virtual Network using the VPN Gateway.
A normal user does not have enough rights to do this, and besides, I don’t want them to be static. Like a proper VPN Connection, routes should be added as needed when the connection is established and they need to be removed when the connection is ended or disconnected.
Thank you Risual, close, but no cigar.
So what other options are left? Well, Windows 10 has a native VPN provider (the same one that is used in the article from Risual), and it can be configured using xml VPNv2 profiles.
More information on VPNv2 xml profiles can be found here: https://docs.microsoft.com/en-us/windows/client-management/mdm/vpnv2-csp.
Cool thing about these profiles is that they can be used for deployment using Intune.
Let’s dig into configuring that. I will show you how to capture a VPNv2 xml for deployment in Intune.
First make sure that the Root certificate from the Point-to-site configuration on the VPN Gateway is also a Trusted Root certificate on the machine on which you configure the next steps. Also make sure you have a client certificate that matches that Root certificate in the certificate path. Again, doing so, and configuring those certificates, is outside of the scope of this post.
Those certificates not required to capture the VPN profile which I am about to show you, but they are required if you also want to test the profile before capturing it.
In the Start Menu, type “VPN”.
Click “Add a VPN connection”.
Click “Add a VPN connection”.
Choose “Windows (built-in)” as VPN provider. I entered my Azure VPN Gateway name as the Connection Name. For Server name or address provide the address that you can get from the xml in the “Generic Client” folder. VPN type should be Automatic, because SSTP type is not (yet) supported, Type of sign-in info should be Certificate. I also left the Remember my sign-in info check enabled.
Click Save.
Now click Change adapter options and open the properties for the newly created adapter.
On the Security tab, switch Data encryption to “Require encryption” and then click Properties.
Uncheck Verify the server’s identity (gets rid of the certificate warning which remained in the Risual article).
Click OK three times and test the connection (Remembering that testing only works if you have the Root certificate and a valid user certificate installed on the machine you are using to capture the VPN profile):
Success! At least for the first part. The routes are still not there. And it’s a manual nightmare. Fret not, we will get there. We will use this VPN connection to build our own VPNv2 xml profile.
Time to grab the xml from the profile we just created.
Run the following PowerShell code (after replacing my values with your own values for the variables $Name, $Server, $DnsSuffix, $OutputFile, and $RouteConfig before running it):
$Name = "ITW-01" $Server = "azuregateway-e4850069-xxxx-xxxx-xxxx-ae72007d9e79-585c3b92babd.vpn.azure.com" $DnsSuffix = "it-worxx.local" $OutputFile = "$($env:TEMP)\VPNv2_Profile.xml" $RouteConfig = @("<Route><Address>192.168.2.0</Address><PrefixSize>24</PrefixSize></Route> <Route><Address>10.161.253.0</Address><PrefixSize>24</PrefixSize></Route> <Route><Address>10.162.253.0</Address><PrefixSize>24</PrefixSize></Route> <Route><Address>10.163.253.0</Address><PrefixSize>24</PrefixSize></Route> <Route><Address>10.164.253.0</Address><PrefixSize>24</PrefixSize></Route>") @("<VPNProfile> <ProfileName>$($Name.ToLower())profile</ProfileName> <!-- the dns suffix to use for the vpn adapter --> <DnsSuffix>$($DnsSuffix)</DnsSuffix> <NativeProfile> <!-- the azure vpn gateway address goes here --> <Servers>$Server</Servers> <!-- for azure vpn gateway basic sku this must be Automatic --> <NativeProtocolType>Automatic</NativeProtocolType> <Authentication> <!-- this must be eap and a valid eap configuration must be supplied --> <UserMethod>Eap</UserMethod> <Eap> <Configuration> <!-- sabm eap configuration taken from template profile --> $((Get-VpnConnection -Name $Name).EapConfigXmlStream.InnerXml) </Configuration> </Eap> </Authentication> <!-- activate splittunnel configuration --> <RoutingPolicyType>SplitTunnel</RoutingPolicyType> <!-- disable class based default route --> <DisableClassBasedDefaultRoute>true</DisableClassBasedDefaultRoute> </NativeProfile> <RememberCredentials>true</RememberCredentials> <!-- add route config for this connection --> $RouteConfig </VPNProfile>") | Out-File -FilePath $OutputFile
The result will be an xml file in your output destination, which, you guessed right, is the VPNv2 xml profile. Mine looks like this when I run the script:
<VPNProfile> <ProfileName>it-worxxprofile</ProfileName> <!-- the dns suffix to use for the vpn adapter --> <DnsSuffix>it-worxx.local</DnsSuffix> <NativeProfile> <!-- the azure vpn gateway address goes here --> <Servers>azuregateway-e4850069-xxxx-xxxx-xxxx-ae72007d9e79-585c3b92babd.vpn.azure.com</Servers> <!-- for azurre vpn gateway basic sku this must be Automatic --> <NativeProtocolType>Automatic</NativeProtocolType> <Authentication> <!-- this must be eap and a valid eap configuration must be supplied --> <UserMethod>Eap</UserMethod> <Eap> <Configuration> <!-- sabm eap configuration taken from template profile --> <EapHostConfig xmlns="http://www.microsoft.com/provisioning/EapHostConfig"><EapMethod><Type xmlns="http://www.microsoft.com/provisioning/EapCommon">13</Type><VendorId xmlns="http://www.microsoft.com/provisioning/EapCommon">0</VendorId><VendorType xmlns="http://www.microsoft.com/provisioning/EapCommon">0</VendorType><AuthorId xmlns="http://www.microsoft.com/provisioning/EapCommon">0</AuthorId></EapMethod><Config xmlns="http://www.microsoft.com/provisioning/EapHostConfig"><Eap xmlns="http://www.microsoft.com/provisioning/BaseEapConnectionPropertiesV1"><Type>13</Type><EapType xmlns="http://www.microsoft.com/provisioning/EapTlsConnectionPropertiesV1"><CredentialsSource><CertificateStore><SimpleCertSelection>true</SimpleCertSelection></CertificateStore></CredentialsSource><ServerValidation><DisableUserPromptForServerValidation>false</DisableUserPromptForServerValidation><ServerNames></ServerNames></ServerValidation><DifferentUsername>false</DifferentUsername><PerformServerValidation xmlns="http://www.microsoft.com/provisioning/EapTlsConnectionPropertiesV2">false</PerformServerValidation><AcceptServerName xmlns="http://www.microsoft.com/provisioning/EapTlsConnectionPropertiesV2">false</AcceptServerName><TLSExtensions xmlns="http://www.microsoft.com/provisioning/EapTlsConnectionPropertiesV2"><FilteringInfo xmlns="http://www.microsoft.com/provisioning/EapTlsConnectionPropertiesV3"><CAHashList><IssuerHash>8c ff ff ff ff b3 c9 20 3f c1 b1 da 3f 02 77 b5 93 fd 3b 3e </IssuerHash></CAHashList></FilteringInfo></TLSExtensions></EapType></Eap></Config></EapHostConfig> </Configuration> </Eap> </Authentication> <!-- activate splittunnel configuration --> <RoutingPolicyType>SplitTunnel</RoutingPolicyType> <!-- disable class based default route --> <DisableClassBasedDefaultRoute>true</DisableClassBasedDefaultRoute> </NativeProfile> <RememberCredentials>true</RememberCredentials> <!-- add route config for this connection --> <Route><Address>192.168.2.0</Address><PrefixSize>24</PrefixSize></Route> <Route><Address>10.161.253.0</Address><PrefixSize>24</PrefixSize></Route> <Route><Address>10.162.253.0</Address><PrefixSize>24</PrefixSize></Route> <Route><Address>10.163.253.0</Address><PrefixSize>24</PrefixSize></Route> <Route><Address>10.164.253.0</Address><PrefixSize>24</PrefixSize></Route> </VPNProfile>
And this is the profile data we can use to publish the profile using Intune.
In Intune, create a new Configuration Profile.
Give it a name, select Windows 10 and later for Platform, and most importantly, select Custom Profile Type.
In the Custom OMA-URI settings tab click Add.
Name and Description really don’t matter. What’s important here is the OMA-URI. This should be like “./user/vendor/MSFT/VPNv2/<profile name>/ProfileXML” where “<profile name>” will be the name for the VPN connection once it’s deployed and visible in Windows 10. In my case I entered “./user/vendor/MSFT/VPNv2/IT-WorXX/ProfileXML” as the OMA-URI. Also note here that this profile name setting does not have to match the profile name that you used to capture the VPNv2 profile.
The Data type must be string, and in the Value field you paste the captured profile xml.
When you’re done don’t forget to assign the profile. I chose to assign it to All users.
Time to test the setup.
When I log on to a managed device with a user that has the VPN profile assigned, it shows up in the available connections.
In the above screenshot you can see the initial IP Config output, and the IP Config output with the “IT-WorXX” VPN connection in a connected state.
The initial IPv4 Route Table, which clearly shows there’re no routes to the address spaces in Azure, and no routes to my on-premises networks.
And after connecting the “IT-WorXX” VPN Connection, the IPv4 Routing Table show routes to the Azure address spaces, and the on-premises network.
There’s one more “gotcha” with this setup.
I have configured custom DNS servers on the Azure Virtual Network. What happens here is when I connect the “IT-WorXX” VPN profile, I automatically receive these DNS servers on the VPN client connection:
If for some reason your configuration prevents you from entering custom DNS servers your client will receive no DNS server settings when it is connected.
You can solve this issue by modifying the XML file for the VPNv2 profile between the DnsSuffix and NativeProfile tags:
<VPNProfile> <ProfileName>it-worxxprofile</ProfileName> <!-- the dns suffix to use for the vpn adapter --> <DnsSuffix>it-worxx.local</DnsSuffix> <DomainNameInformation> <DomainName>.it-worxx.local</DomainName> <DnsServers>10.161.253.4,192.168.2.4</DnsServers> </DomainNameInformation> <NativeProfile>
And that’s it. A fully managed VPNv2 profile deployment using Intune, which allows you to connect to an Azure VPN Gateway Basic Sku P2S configuration.
Until next time,
Arjan
I don’t get an .xml as in your example when I run the script. Any guidance on what I missed?
Hi Avaron,
Did you change the output? $OutputFile = “$($env:TEMP)\VPNv2_Profile.xml”
If not, it should be in the TEMP folder for the useraccount you used to run the script.
We did update the file path and we do generate an output file. However, all it contains is the VPN Name we specify and the DNS we specify
Can you send me the PS script you executed?
Trying a different text editor we were able to get the .xml output as described in your example. Followed the other steps and we are able to get the VPN deployed. Next road block… when we try to connect using the profile that was pushed by Intune, first error we face is the server address isn’t populated and it needs to be manually added. When that is done we get a second error where we are challenged for a user name and password. To our understanding this shouldn’t be because we’re authenticating using certificates.
Cert auth only works if the cert authority cert is on the gateway’s p2s settings and on the endpoints as well. Was your gateway redeployed? It will get a different name then. Orher than that, check your script for weird quotes, which sometimes happens when copying code from wordpress sites.
Thanks again for the assistance. On the device the vpn profile was deployed to, we found that the issue appears to be that the profile that is set by Intune is missing necessary settings. Once the profile is deployed, and we must manually supply the server address. We must then manually change authentication to EAP and select certificate auth, and then manually remove the option to validate the certificate. We must also manually add our domain suffix. Once all these manual changes are made, the profile does work. We are just trying to understand why the profile is lacking these settings initially. They were configured on the profile that was sampled by the original powershell script.
Hello, Thank you for your post it is amazingly well documented. I managed to go throughts all the steps. Unfortunatelly the profile in INTUNE is stuck to ‘PENDING’. I have tried to apply it to other computers/user : same it does not work.
If I take a INTUNE template then it works…
Any thoughts ?
Mat
Thanks for this helpful article. In the Powershell to create the XML file you seem to define a variable $DnsSuffix but then don’t use it in the creation of the file. (you have a hardcoded Suffix). I can have a guess at the syntax but you might want to correct it on your post?
Hi Jon,
Thanks! Good catch, fixed that now.
Posted already about this several times, but post won’t seem to stick! Maybe I need to strip out the Powershell and XML – will try that. Can send that detail if you tell me how.
I created an XML file using your post, but the one I created doesn’t appear to have included any certificate information within it. (Detail appended)
The XML file included in the Azure Portal VPN Client download does have a tag but this does not seem to have been included in the output XML.
This was the exact Powershell I ran (all information sanitised):-
XML File downloaded from Azure Portal – VPNSettings.xml:-
XML File created by the script – VPNv2_Profile.xml:-
OK – WordPress has been stripping out and blocking Powershell / XML detail. I can send you something if you tell me how!
Not sure if it will let me post but have put XML / Powershell in Pastebin – https://pastebin.com/FaY2pXpA
Hi Jon,
Were you able to successfully connect manually on the machine where you created the VPN manually?
Yes – it worked fine when I tested manually – didn’t require sign-in and just worked with the certificate.
I recreated it again too and did a dump and the XML file was identical.
Any thoughts?
Hey Jon,
Lots of thoughts, but none that will help you, sorry. I have successfully deployed this several times now, never without any problems in the xml. The fact that you are able to connect manually indicates that everything should be fine. I’m afraid that you will have to (once) enter the hash there manually before using the profile in Intune. It’s the thumbprint for the CA certificate that you uploaded to the VPN gateway.
OK – I’ll see if I can use the same syntax you used above.