Keeping Microsoft 365 gateway routes up to date¶
If you use Enclave gateways to route Microsoft 365 traffic through a known static IP address, you need to keep your gateway policy in sync with Microsoft's published IP ranges. Microsoft maintains a JSON endpoint listing the current IPv4 subnets for Exchange Online, SharePoint, Teams, and other Microsoft 365 services, but the list changes over time and contains duplicates that need to be cleaned up before use.
This script fetches the current Microsoft 365 IP ranges, de-duplicates them, and writes the result as subnet filtering rules on an Enclave gateway policy. Each rule is labelled with the Microsoft 365 service area it belongs to (e.g. "Exchange Online", "SharePoint Online and OneDrive for Business").
Prerequisites¶
- An Enclave API key
- Your Organisation ID
- A configured Enclave gateway with a gateway access policy that routes
0.0.0.0/0
How it works¶
The script pulls Microsoft's worldwide endpoint data from their published JSON feed, which lists every IP range and FQDN used by Microsoft 365 services globally. It filters for IPv4 entries only, de-duplicates them, and tags each with a description based on the service area (Exchange Online, SharePoint Online, Teams, etc.). The de-duplicated subnets are then patched onto the named gateway policy as allowed IP ranges.
A -test flag lets you preview the subnets that would be written to the policy without making changes.
The script¶
Save the following script as a .ps1 file.
-orgId, the organisation to run queries against.-apiKey, the Enclave API key. If you prefer not to put your API key on the command line, set theENCLAVE_API_KEYenvironment variable instead.-policyName, the name of the gateway policy to update.-test, prints what the script would do without making changes.
Param(
[Parameter(Mandatory=$true)]
[string]$orgId,
[Parameter()]
[string]$apiKey = "",
[Parameter(Mandatory=$true)]
[string]$policyName,
[Parameter()]
[switch]$test = $false
)
$ErrorActionPreference = "Stop"
if ($apiKey -eq "") {
$apiKey = $env:ENCLAVE_API_KEY
}
if ($apiKey -eq "") {
Write-Error "No API key provided; either specify the 'apiKey' argument, or set the ENCLAVE_API_KEY environment variable."
return;
}
$headers = @{Authorization = "Bearer $apiKey"}
$contentType = "application/json";
$jsonUrl = "https://endpoints.office.com/endpoints/worldwide?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7"
$jsonResponse = Invoke-RestMethod -Uri $jsonUrl -Method Get -Headers $headers
$subnets = @()
$uniqueSubnets = @{}
foreach ($item in $jsonResponse) {
if ($item.ips) {
foreach ($ip in $item.ips) {
if ($ip -match '\.') {
$description = if ($item.serviceAreaDisplayName) { "Office 365 ($($item.serviceAreaDisplayName))" } else { "Office 365" }
# Check if the IP is already in the hashtable
if (-not $uniqueSubnets.ContainsKey($ip)) {
$uniqueSubnets[$ip] = @{
ipRange = $ip
description = $description
}
}
}
}
}
}
# Convert the hashtable back to an array of objects
$subnets = $uniqueSubnets.Values
$response = Invoke-RestMethod -ContentType $contentType -Method Get -Uri "https://api.enclave.io/org/$orgId/policies?search=$policyName" -Headers $headers;
if ($response.total -eq 0) {
Write-Error "No policies found with name $policyName."
return;
}
elseif ($response.total -gt 1) {
Write-Error "Multiple policies found with name $policyName; please provide a more specific name."
return;
}
$policyId = $response.items[0].id;
Write-Host "Found policy $policyName with id $policyId."
$policyPatch = @{
"gatewayAllowedIpRanges" = $subnets
} | ConvertTo-Json
if ($test)
{
Write-Host "Test mode enabled; not updating policy."
Write-Host "Would have updated policy $policyId with the following patch:"
Write-Host $policyPatch
}
else
{
Invoke-RestMethod -ContentType $contentType -Method Patch -Uri "https://api.enclave.io/org/$orgId/policies/$policyId" -Headers $headers -Body $policyPatch
Write-Host "Updated policy with $($subnets.Count) IP addresses."
}
Usage¶
Preview the subnets that would be applied to the policy:
.\gateway-policy-o365-update.ps1 -orgId 7G2AS `
-apiKey $env:ENCLAVE_API_KEY `
-policyName "Microsoft 365 for Users" `
-test
Once you've reviewed the output, remove the -test flag to apply the changes:
.\gateway-policy-o365-update.ps1 -orgId 7G2AS `
-apiKey $env:ENCLAVE_API_KEY `
-policyName "Microsoft 365 for Users"
Scheduling¶
Microsoft periodically updates the IP ranges used by their services. You should run this script on a recurring schedule to keep your gateway policy current. Windows Task Scheduler is the simplest option for a single site, but if you manage multiple organisations, consider running the script from your RMM platform or as an Azure Function on a timer trigger. Store your API key using the platform's secrets management rather than embedding it in the script or command line.