Add-VPNConnection

Creating VPNs

First, an apology. I usually try to be conscientious about adding new nuggets of PowerShell fun on a regular basis, but this winter, LIFE has intruded, and it simply hasn't happened. I won't promise it won't happen again, but I will try to do better.

Today's post looks at a problem we've been dealing with at work -- how to pre-configure new laptops with the VPN access they'll need for users to get logged in, even when they're not ever in an office to set themselves up. There are lots of different workarounds and solutions, but what  we came up with was a PowerShell script that would create one or more VPNs programmatically. We take advantage of the Invoke-WebRequest cmdlet I discussed earlier to pull down an updated set of parameters for the available VPNs, allowing us to separate the code from the data, useful for providing some protection against changes -- we only have to update one file.

The command to create a new VPN connection is: Add-VPNConnection and it comes with a plethora of parameters, most of which you'll never need. But as always, good to have them when you need them. For our purposes, we needed to specify the VPN type (-TunnelType), the authentication method, and an initial pre-shared key for L2TP. We also wanted the ability to use the same script for individual user profile VPNs, and to control whether the VPN used a split tunnel. (Normally, we configure for split tunneling.) The basic command is:

Add-VpnConnection -Name <ConnectionName> `
                  -ServerAddress <IP Address of VPN Server> `
                  -TunnelType L2TP `
                  -L2tpPsk <somereallylongandoddstring> `
                  -AuthenticationMethod MSChapv2 `
                  -AllUserConnection `
                  -SplitTunneling `
                  -PassThru

So, we know we're going to need a name for the connection, and IP address, and the L2TP PSK for each connection. The easy way is to stuff that into a CSV file and store it up in the cloud where all the IT staff can get at it from whatever location we're in. So we need to read the contents of a file, probably stored in the cloud, and, using a foreach statement, iterate the Add-VPNConnection command once for each line of the CSV file to create VPNs to all of the VPNs listed in the CSV file. Pretty simple, really. The annoying part is that we have to repeat ourselves doing this to handle the values of the AllUserConnection and SplitTunneling switches in the Add-VPNConnection command. If they were Booleans, it would be a bit less messy.

<#
.Synopsis
Creates one or more VPNs. Uses a CSV file stored in OneDrive for Business
.Description
New-myVPN reads a list of VPNs and their parameters from a CSV file stored 
in OneDrive for Business, and creates one or more VPNs based on that list.

The created VPNs can be configured as AllUser VPNs, or only for the current
user (the default). The VPNs are created as Split-Tunnel VPNs unless the 
NoSplitVPN parameter is specified. 
.Example
New-myVPN 

Reads the default VPN.csv file and creates vpns using the details in that file
to create VPNs as split-Tunnel VPNs available to all users.
.Example
New-myVPN -NoSplitVPN -AllUserConnection $False

Reads the default VPN.csv file and creates new VPNs using the details in that file. 
The VPNs are created in the current user's profile, and are not created as split VPNs
.Parameter Path
Path to CSV file. The CSV file is in the format: Name,ServerAddress,L2tpPsk. The 
default path is to a file called VPN.csv, stored in a folder called Private, in 
the current user's OneDrive for Business. 
.Parameter AllUserConnection
Boolean -- default is $True. When True, VPNs are created as an AllUserConnection and
are available to all users on the computer. When False, VPNs are created in the 
current user's profile and are only available to the user after logon. 
.Parameter NoSplitTunnel
Switch -- VPNs are created as SplitTunnel VPNs unless this switch is enabled. A 
SplitTunnel VPN sends all regular traffic to the main network interface, but 
sends traffic to hosts connected via the VPN to the VPN. 

When this switch is set, all traffic outside of the local subnet is sent over the 
VPN connection. 

.Inputs
[string]
[Boolean]
[Switch]
.Notes
    Author: Charlie Russel
 Copyright: 2018 by Charlie Russel
          : Permission to use is granted but attribution is appreciated
   Initial: 13 March, 2018 (cpr)
   ModHist:
          :
#>
[CmdletBinding()]
Param(
     [Parameter(Mandatory=$False,Position=0)]
     [string]
     $Path = (Get-ItemProperty 'HKCU:\Software\Microsoft\OneDrive\Accounts\Business1').UserFolder + "\private\vpn.csv",
     [Parameter(Mandatory=$true)]
     [Boolean]
     $AllUserConnection,
     [Parameter(Mandatory=$False)]
     [Switch]
     $NoSplitTunnel
     )


if ($Path -match "http") { # We're connecting to the web to get the parameters
   Write-Verbose "Found a match against $Matches[0], so using WebRequest"
   $vpnParams = ConvertFrom-CSV (Invoke-WebRequest -Uri $Path ).ToString() 
} else { # We're going against a local path
   Write-Verbose "Reading from a local file $path"
   $vpnParams = ConvertFrom-CSV (Get-Content $Path)
}

$vpncount = $vpnParams.count
if (($AllUserConnection) -AND (! $NoSplitTunnel)) {
   Write-Verbose "Creating $vpn.count AllUser VPNs using parameters in $path and split-tunneling"
   ForEach ($param in $vpnParams) {
      Add-VpnConnection -Name $param.Name `
                        -ServerAddress $param.ServerAddress `
                        -TunnelType L2TP `
                        -L2tpPsk $param.L2tpPsk `
                        -AuthenticationMethod MSChapv2 `
                        -EncryptionLevel Optional `
                        -AllUserConnection `
                        -SplitTunneling `
                        -Force `
                        -PassThru
   }
} elseif (($AllUserConnection) -AND ($NoSplitTunnel)) {
   Write-Verbose "Creating $vpncount AllUser VPNs using parameters in $path and no split-tunneling"
   ForEach ($param in $vpnParams) {
      Add-VpnConnection -Name $param.Name `
                        -ServerAddress $param.ServerAddress `
                        -TunnelType L2TP `
                        -L2tpPsk $param.L2tpPsk `
                        -AuthenticationMethod MSChapv2 `
                        -EncryptionLevel Optional `
                        -AllUserConnection `
                        -Force `
                        -PassThru
   }
} elseif ($NoSplitTunnel) {
   Write-Verbose "Creating $vpncount current user VPNs using parameters in $path and no split-tunneling"
   ForEach ($param in $vpnParams) {
      Add-VpnConnection -Name $param.Name `
                        -ServerAddress $param.ServerAddress `
                        -TunnelType L2TP `
                        -L2tpPsk $param.L2tpPsk `
                        -AuthenticationMethod MSChapv2 `
                        -EncryptionLevel Optional `
                        -Force `
                        -PassThru
   }
} else { 
   Write-Verbose "Creating $vpncount current user VPNs using parameters in $path and split-tunneling"
   ForEach ($param in $vpnParams) {
      Add-VpnConnection -Name $param.Name `
                        -ServerAddress $param.ServerAddress `
                        -TunnelType L2TP `
                        -L2tpPsk $param.L2tpPsk `
                        -AuthenticationMethod MSChapv2 `
                        -EncryptionLevel Optional `
                        -SplitTunneling `
                        -Force `
                        -PassThru
   }
}