Skip to content

Instantly share code, notes, and snippets.

@celloza
Last active December 14, 2023 15:18
Show Gist options
  • Save celloza/2e862b8291e0b2ea1182fa106d36afdc to your computer and use it in GitHub Desktop.
Save celloza/2e862b8291e0b2ea1182fa106d36afdc to your computer and use it in GitHub Desktop.
Populate the custom routes element in an Azure VPN P2S client config with Public IP addresses from Azure Subscriptions
<#
.Synopsis
Modifies an Azure VWAN P2S config file from existing Azure PublicIP resources.
.DESCRIPTION
This script modifies an existing Azure VWAN P2S config file that was downloaded from the Azure Portal, by:
1. Adding a customroute element for each Public IP address found in the supplied list of subscriptions
2. (optional) Change the name displayed in the Azure VPN client
3. (optional) Add custom DNS servers
4. (optional) Add a custom route to a test service (icanhazip.com) to test split-tunneling
5. (optional) Excludes some specified IP addresses
.EXAMPLE
.\Modify-DevVPNConfigFile.ps1 -FileNameToModify "C:\Users\me\Desktop\originalazurevpnconfig.xml"
-OverrideVpnName "My new VPN" -OutputFileName "C:\modifiedazurevpnconfig.xml" -IncludeTestRoute
-SubscriptionIds "abc","def" -ExcludeIpAddresses "192.168.0.1","192.168.0.2"
Modifies the supplied file by adding all Public IP addresses from the Subscriptions with Ids "abc" and
"def" (except for 192.168.0.1 and .2), overrides the VPN name to "My new VPN", adds custom routes for
icanhazip.com, and saves the file to modifiedazurevpnconfig.xml.
#>
[CmdletBinding()]
param (
[Parameter(HelpMessage = "Supply the full path to the Azure VPN configuration file downloaded from Azure Portal.")]
[string]
$FileNameToModify,
[Parameter(HelpMessage = "Supply an output path where the new configuration file will be saved.")]
[string]
$OutputFileName,
[Parameter(HelpMessage = "If you supply a value for OverrideVpnName, the Display Name of the VPN will be changed to this.")]
[string]
$OverrideVpnName,
[Parameter(HelpMessage = "A list of SubscriptionIds from which Public IP addresses will be injected into the new configuration file.")]
[string[]]
$SubscriptionIds,
[Parameter(HelpMessage = "A list of IP addresses that will be ignored during the injection.")]
[string[]]
$ExcludeIpAddresses = @(),
[Parameter(HelpMessage = "Force clients to use the supplied list of DNS servers. If none are supplied, defaults will be used.")]
[string[]]
$CustomDnsServers,
[Parameter(HelpMessage = "Include routes for the icanhazip.com service to test split-tunneling.")]
[switch]
$IncludeTestRoute
)
$context = Get-AzContext
if (!$context)
{
Connect-AzAccount
}
else
{
Write-Host "Using existing Azure context."
}
$allSubscriptions = Get-AzSubscription
$hasAccessToAllSubscriptions = $true
foreach($subscriptionId in $SubscriptionIds)
{
if($allSubscriptions.SubscriptionId -notcontains $subscriptionId)
{
$hasAccessToAllSubscriptions = $false
Write-Error "Could not find subscription with Id $subscriptionId."
}
}
if(-not $hasAccessToAllSubscriptions)
{
return
}
$allIpAddresses = @()
# Get all the PublicIPs from each of the supplied Subscriptions
foreach($subscriptionId in $subscriptionIds)
{
Write-Host "Switching context to SubscriptionId: $subscriptionId"
$context = Set-AzContext -SubscriptionId $subscriptionId
if(!$context)
{
Write-Error "Couldn't access subscription $subscriptionId"
return
}
$currentIpCount = $allIpAddresses.Count
$allIpAddresses += Get-AzPublicIpAddress | Select-Object IpAddress | Where-Object {$_.IpAddress -ne "Not Assigned"}
$newIpCount = $allIpAddresses.Count
Write-Host "Successfully added $($newIpCount - $currentIpCount) addresses from $($context.Subscription.Name)"
}
if($IncludeTestRoute)
{
$icanhazIps = Resolve-DnsName -Name icanhazip.com | Where-Object {$_.Type -eq 'A'} | Select-Object IpAddress
foreach($icanhazIp in $icanhazIps)
{
$newIpAddress = New-Object -TypeName System.Object
Add-Member -InputObject $newIpAddress -NotePropertyName "IpAddress" -NotePropertyValue $icanhazIp.IpAddress
$allIpAddresses += $newIpAddress
}
}
[xml]$xmlDoc = Get-Content $FileNameToModify
if($xmlDoc.azvpnprofile.clientconfig.IsEmpty)
{
$includeRoutesNode = $xmlDoc.CreateElement("includeroutes")
($xmlDoc.AzVpnProfile.clientconfig.AppendChild($includeRoutesNode)) | Out-Null
}
[xml]$xmlDoc = [xml]$xmlDoc.OuterXml
if($OverrideVpnName)
{
$xmlDoc.AzVpnProfile.name = $OverrideVpnName
}
foreach($ipAddress in $allIpAddresses)
{
if(-not $ExcludeIpAddresses.Contains($ipAddress.IpAddress))
{
($routeNode = $xmlDoc.CreateElement("route")) | Out-Null
($destinationNode = $xmlDoc.CreateElement("destination")) | Out-Null
($destinationNode.InnerText = $ipAddress.IpAddress) | Out-Null
($maskNode = $xmlDoc.CreateElement("mask")) | Out-Null
($maskNode.InnerText = "32") | Out-Null
($routeNode.AppendChild($destinationNode)) | Out-Null
($routeNode.AppendChild($masknode)) | Out-Null
$xmlDoc.AzVpnProfile.clientconfig.includeroutes.AppendChild($routeNode) | Out-Null
Write-Debug "Added $($ipAddress.IpAddress)/32 to the list of custom routes."
}
else
{
Write-Warning "Excluding $($ipAddress.IpAddress)/32."
}
}
if($CustomDnsServers.Count -ne 0)
{
($dnsServersNode = $xmlDoc.CreateElement("dnsservers")) | Out-Null
$dnsCount = 0
foreach($customDnsServer in $CustomDnsServers)
{
$dnsCount++
($dnsServerNode = $xmlDoc.CreateElement("dnsserver")) | Out-Null
($dnsServerNode.InnerText = $customDnsServer) | Out-Null
($dnsServersNode.AppendChild($dnsServerNode)) | Out-Null
}
($xmlDoc.AzVpnProfile.clientconfig.AppendChild($dnsServersNode)) | Out-Null
Write-Host "Added $dnsCount DNS servers."
}
$xmlDoc.Save($OutputFileName)
Write-Host "Saved new VPN configuration file to '$OutputFileName' with $($allIpAddresses.Count) custom routes."
@celloza
Copy link
Author

celloza commented Dec 14, 2023

Overview

I needed to regularly add custom routes to my Azure VWAN P2S VPN client configuration file while I was busy testing a split-tunnel VPN, so made this script, and generalised it.

You supply it with a list of Azure SubscriptionIds, and it injects a <customroute> element for each valid Public IP resource it finds in those subscriptions, so that all those IP addresses are routed via the VPN tunnel.

Added options for excluding some of these addresses, injecting custom DNS servers, and changing the display name of the VPN in the Azure VPN client.

It also includes a switch that adds routes for the public IP addresses for the icanhazip.com service, which returns your public IP address when called (particularly useful when testing split-tunneling).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment