使用 Windows VPN 用戶端優化遠端工作者的 Microsoft 365 流量

本文說明如何設定適用於 Windows VPN 用戶端的 Microsoft 365 VPN 分割通道 一文中的建議。 本指引可讓 VPN 系統管理員優化 Microsoft 365 使用量,同時確保所有其他流量通過 VPN 連線,以及透過現有的安全性網關或工具。

您可以使用 具有排除 範圍的強制通道方法,為內建 Windows VPN 用戶端實作建議,定義 IP 型排除專案,即使使用 強制通道也一樣。 某些流量可以 分割 成使用實體介面,同時仍然透過 VPN 介面強制所有其他流量。 與 Microsoft 365 優化類別中所列的目的地 (一樣,) 會遵循更直接且更有效率的路徑,而 不需要透過 VPN 通道往返組織網路。 對於 Microsoft 365 之類的雲端服務,這對遠端使用者的效能和可用性有很大的差異。

注意

其他廠商和某些在線檔有時會混淆「強制通道」一詞與排除範圍。 針對 Windows VPN, 「分割通道 」一詞的定義方式不同,如 VPN 路由決策一文所述。

解決方案概觀

此解決方案是以 VPN 設定服務提供者參考設定檔 (VPNv2 CSP) 和內嵌 ProfileXML 為基礎。 這些是用來在裝置上設定 VPN 配置檔。 您可以使用各種佈建方法來建立和部署 VPN 配置檔,如步驟 6 一文所述。設定 Windows 10用戶端 Always On VPN 連線

一般而言,這些 VPN 配置檔是使用行動裝置 裝置管理 解決方案散發,例如 Intune,如 VPN 配置檔選項使用 Intune 設定 VPN 用戶端中所述。

若要在 Windows 10 或 Windows 11 VPN 中啟用強制通道的使用,<RoutingPolicyType>設定通常會在您現有的設定檔 XML (中設定為 ForceTunnel 值,或透過下列專案,在區段下 <NativeProfile></NativeProfile>) 腳本:

<RoutingPolicyType>ForceTunnel</RoutingPolicyType>

若要定義特定的強制通道排除專案,您接著必須將下列幾行新增至現有的配置檔 XML (或腳本) ,以供每個必要排除專案使用,並將它們放在 區段之外 <NativeProfile></NativeProfile> ,如下所示:

<Route>
 <Address>[IP addresses or subnet]</Address>
 <PrefixSize>[IP Prefix]</PrefixSize>
 <ExclusionRoute>true</ExclusionRoute>
</Route>

因此, [IP Addresses or Subnet][IP Prefix] 參考所定義的專案會新增到路由表中,作為將使用因特網連線介面做為預設網關的 更特定路由專案 ,而不是使用 VPN 介面。 您必須為每個必要排除項目定義唯一和個別 <Route></Route> 的區段。

針對具有排除範圍的強制通道,正確格式化的配置檔 XML 設定範例如下:

<VPNProfile>
 <NativeProfile>
  <RoutingPolicyType>ForceTunnel</RoutingPolicyType>
 </NativeProfile>
 <Route>
  <Address>203.0.113.0</Address>
  <PrefixSize>24</PrefixSize>
  <ExclusionRoute>true</ExclusionRoute>
 </Route>
 <Route>
  <Address>198.51.100.0</Address>
  <PrefixSize>22</PrefixSize>
  <ExclusionRoute>true</ExclusionRoute>
 </Route>
</VPNProfile>

注意

此範例中的IP位址和前置詞大小值僅做為範例使用,不應使用。

解決方案部署

因此,針對 Microsoft 365,必須針對 Office 365 URL 和 IP 位址範圍中所述的優化類別內所述的所有 IP 位址新增排除專案,以確保從 VPN 強制通道中排除這些位址。

您可以手動將 優化 類別專案內定義的IP位址新增至現有的配置檔 XML (或腳本) 檔案,或者,也可以使用下列腳本,以根據直接查詢 REST 型 Web 服務,將必要的專案動態新增至現有的 PowerShell 腳本或 XML 檔案,以確保一律使用正確的 IP 位址範圍。

以下提供可用來更新使用 Microsoft 365 排除專案之強制通道 VPN 連線的 PowerShell 腳本範例。

# Copyright (c) Microsoft Corporation.  All rights reserved.
#  
# THIS SAMPLE CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
# WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
# IF THIS CODE AND INFORMATION IS MODIFIED, THE ENTIRE RISK OF USE OR RESULTS IN
# CONNECTION WITH THE USE OF THIS CODE AND INFORMATION REMAINS WITH THE USER.

<#
.SYNOPSIS
    Applies or updates recommended Microsoft 365 optimize IP address exclusions to an existing force tunnel Windows 10 and Windows 11 VPN profile
.DESCRIPTION
    Connects to the Microsoft 365 worldwide commercial service instance endpoints to obtain the latest published IP address ranges
    Compares the optimized IP addresses with those contained in the supplied VPN Profile (PowerShell or XML file)
    Adds or updates IP addresses as necessary and saves the resultant file with "-NEW" appended to the file name
.PARAMETERS
    Filename and path for a supplied Windows 10 or Windows 11 VPN profile file in either PowerShell or XML format
.NOTES
    Requires at least Windows 10 Version 1803 with KB4493437, 1809 with KB4490481, or later
.VERSION
    1.0
#>

param (
    [string]$VPNprofilefile
)

$usage=@"

This script uses the following parameters:

VPNprofilefile - The full path and name of the VPN profile PowerShell script or XML file

EXAMPLES

To check a VPN profile PowerShell script file:

Update-VPN-Profile-Office365-Exclusion-Routes.ps1 -VPNprofilefile [FULLPATH AND NAME OF POWERSHELL SCRIPT FILE]

To check a VPN profile XML file:

Update-VPN-Profile-Office365-Exclusion-Routes.ps1 -VPNprofilefile [FULLPATH AND NAME OF XML FILE]

"@
  
# Check if filename has been provided #
if ($VPNprofilefile -eq "")
{
   Write-Host "`nWARNING: You must specify either a PowerShell script or XML filename!" -ForegroundColor Red

    $usage
    exit
}

$FileExtension = [System.IO.Path]::GetExtension($VPNprofilefile)

# Check if XML file exists and is a valid XML file #
if ( $VPNprofilefile -ne "" -and $FileExtension -eq ".xml")
{
    if ( Test-Path $VPNprofilefile )
    {
        $xml = New-Object System.Xml.XmlDocument
        try
        {
            $xml.Load((Get-ChildItem -Path $VPNprofilefile).FullName)

        }
        catch [System.Xml.XmlException]
        {
            Write-Verbose "$VPNprofilefile : $($_.toString())"
            Write-Host "`nWARNING: The VPN profile XML file is not a valid xml file or incorrectly formatted!" -ForegroundColor Red
            $usage
            exit
        }
    }else
    {
        Write-Host "`nWARNING: VPN profile XML file does not exist or cannot be found!" -ForegroundColor Red
        $usage
        exit
    }
}

# Check if VPN profile PowerShell script file exists and contains a VPNPROFILE XML section #
if ( $VPNprofilefile -ne "" -and $FileExtension -eq ".ps1")
{
    if ( (Test-Path $VPNprofilefile) )
    {
        if (-Not $(Select-String -Path $VPNprofilefile -Pattern "<VPNPROFILE>") )
        {
            Write-Host "`nWARNING: PowerShell script file does not contain a valid VPN profile XML section or is incorrectly formatted!" -ForegroundColor Red
            $usage
            exit
        }
    }else
    {
        Write-Host "`nWARNING: PowerShell script file does not exist or cannot be found!"-ForegroundColor Red
        $usage
        exit
    }
}

# Define Microsoft 365 endpoints and service URLs #
$ws = "https://endpoints.office.com"
$baseServiceUrl = "https://endpoints.office.com"

# Path where client ID and latest version number will be stored #
$datapath = $Env:TEMP + "\endpoints_clientid_latestversion.txt"

# Fetch client ID and version if data file exists; otherwise create new file #
if (Test-Path $datapath)
{
    $content = Get-Content $datapath
    $clientRequestId = $content[0]
    $lastVersion = $content[1]

}else
{
    $clientRequestId = [GUID]::NewGuid().Guid
    $lastVersion = "0000000000"
    @($clientRequestId, $lastVersion) | Out-File $datapath
}

# Call version method to check the latest version, and pull new data if version number is different #
$version = Invoke-RestMethod -Uri ($ws + "/version?clientRequestId=" + $clientRequestId)

if ($version[0].latest -gt $lastVersion)
{

    Write-Host
    Write-Host "A new version of Microsoft 365 worldwide commercial service instance endpoints has been detected!" -ForegroundColor Cyan

    # Write the new version number to the data file #
    @($clientRequestId, $version[0].latest) | Out-File $datapath
}

# Invoke endpoints method to get the new data #
$uri = "$baseServiceUrl" + "/endpoints/worldwide?clientRequestId=$clientRequestId"

# Invoke endpoints method to get the data for the VPN profile comparison #
$endpointSets = Invoke-RestMethod -Uri ($uri)
$Optimize = $endpointSets | Where-Object { $_.category -eq "Optimize" }
$optimizeIpsv4 = $Optimize.ips | Where-Object { ($_).contains(".") } | Sort-Object -Unique

# Temporarily include additional IP address until Teams client update is released
$optimizeIpsv4 += "13.107.60.1/32"

# Process PowerShell script file start #
if ($VPNprofilefile -ne "" -and $FileExtension -eq ".ps1")
{
    Write-host "`nStarting PowerShell script exclusion route check...`n" -ForegroundColor Cyan

    # Clear Variables to allow re-run testing #

    $ARRVPN=$null              # Array to hold VPN addresses from VPN profile PowerShell file #
    $In_Opt_Only=$null         # Variable to hold IP addresses that only appear in the optimize list #
    $In_VPN_Only=$null         # Variable to hold IP addresses that only appear in the VPN profile PowerShell file #

    # Extract the Profile XML from the ps1 file #

    $regex = '(?sm).*^*.<VPNProfile>\r?\n(.*?)\r?\n</VPNProfile>.*'

    # Create xml format variable to compare with the optimize list #

    $xmlbody=(Get-Content -Raw $VPNprofilefile) -replace $regex, '$1'
    [xml]$VPNprofilexml="<VPNProfile>"+$xmlbody+"</VPNProfile>"

        # Loop through each address found in VPNPROFILE XML section #
        foreach ($Route in $VPNprofilexml.VPNProfile.Route)
        {
        $VPNIP=$Route.Address+"/"+$Route.PrefixSize
        [array]$ARRVPN=$ARRVPN+$VPNIP
        }

    # In optimize address list only #
    $In_Opt_Only= $optimizeIpsv4 | Where {$ARRVPN -NotContains $_}

    # In VPN list only #
    $In_VPN_only =$ARRVPN | Where {$optimizeIpsv4 -NotContains $_}
    [array]$Inpfile = get-content $VPNprofilefile

    if ($In_Opt_Only.Count -gt 0 )
    {
        Write-Host "Exclusion route IP addresses are unknown, missing, or need to be updated in the VPN profile`n" -ForegroundColor Red

         [int32]$insline=0

            for ($i=0; $i -lt $Inpfile.count; $i++)
            {
                if ($Inpfile[$i] -match "</NativeProfile>")
                {
                $insline += $i # Record the position of the line after the NativeProfile section ends #
                }
            }
            $OFS = "`r`n"
                foreach ($NewIP in $In_Opt_Only)
                {
                    # Add the missing IP address(es) #
                    $IPInfo=$NewIP.Split("/")
                    $InpFile[$insline] += $OFS+"    <Route>"
                    $InpFile[$insline] += $OFS+"      <Address>"+$IPInfo[0].Trim()+"</Address>"
                    $InpFile[$insline] += $OFS+"      <PrefixSize>"+$IPInfo[1].Trim()+"</PrefixSize>"
                    $InpFile[$insline] += $OFS+"      <ExclusionRoute>true</ExclusionRoute>"
                    $InpFile[$insline] += $OFS+"    </Route>"
                }
             # Update fileName and write new PowerShell file #
             $NewFileName=(Get-Item $VPNprofilefile).Basename + "-NEW.ps1"
             $OutFile=$(Split-Path $VPNprofilefile -Parent)+"\"+$NewFileName
             $InpFile | Set-Content $OutFile
             Write-Host "Exclusion routes have been added to VPN profile and output to a separate PowerShell script file; the original file has not been modified`n" -ForegroundColor Green
    }else
    {
        Write-Host "Exclusion route IP addresses are correct and up to date in the VPN profile`n" -ForegroundColor Green
        $OutFile=$VPNprofilefile
    }

if ( $In_VPN_Only.Count -gt 0 )
{
    Write-Host "Unknown exclusion route IP addresses have been found in the VPN profile`n" -ForegroundColor Yellow

        foreach ($OldIP in $In_VPN_Only)
        {
            [array]$Inpfile = get-content $Outfile
            $IPInfo=$OldIP.Split("/")
            Write-Host "Unknown exclusion route IP address"$IPInfo[0]"has been found in the VPN profile - Do you wish to remove it? (Y/N)`n" -ForegroundColor Yellow
            $matchstr="<Address>"+$IPInfo[0].Trim()+"</Address>"
            $DelAns=Read-host
                if ($DelAns.ToUpper() -eq "Y")
                {
                    [int32]$insline=0
                        for ($i=0; $i -lt $Inpfile.count; $i++)
                        {
                            if ($Inpfile[$i] -match $matchstr)
                            {
                                $insline += $i # Record the position of the line for the string match #
                            }
                        }
                        # Remove entries from XML #
                        $InpFile[$insline-1]="REMOVETHISLINE"
                        $InpFile[$insline]="REMOVETHISLINE"
                        $InpFile[$insline+1]="REMOVETHISLINE"
                        $InpFile[$insline+2]="REMOVETHISLINE"
                        $InpFile[$insline+3]="REMOVETHISLINE"
                        $InpFile=$InpFile | Where-Object {$_ -ne "REMOVETHISLINE"}

                        # Update filename and write new PowerShell file #
                        $NewFileName=(Get-Item $VPNprofilefile).Basename + "-NEW.xml"
                        $OutFile=$(Split-Path $VPNprofilefile -Parent)+"\"+$NewFileName
                        $Inpfile | Set-content $OutFile
                        Write-Host "`nAddress"$IPInfo[0]"exclusion route has been removed from the VPN profile and output to a separate PowerShell script file; the original file has not been modified`n" -ForegroundColor Green

                }else
                {
                    Write-Host "`nExclusion route IP address has *NOT* been removed from the VPN profile`n" -ForegroundColor Green
                }
        }
 }
}

# Process XML file start #
if ($VPNprofilefile -ne "" -and $FileExtension -eq ".xml")
{
    Write-host "`nStarting XML file exclusion route check...`n" -ForegroundColor Cyan

    # Clear variables to allow re-run testing #
    $ARRVPN=$null              # Array to hold VPN addresses from the XML file #
    $In_Opt_Only=$null         # Variable to hold IP Addresses that only appear in optimize list #
    $In_VPN_Only=$null         # Variable to hold IP Addresses that only appear in the VPN profile XML file #  

    # Extract the Profile XML from the XML file #
    $regex = '(?sm).*^*.<VPNProfile>\r?\n(.*?)\r?\n</VPNProfile>.*'

    # Create xml format variable to compare with optimize list #
    $xmlbody=(Get-Content -Raw $VPNprofilefile) -replace $regex, '$1'
    [xml]$VPNRulesxml="$xmlbody"

        # Loop through each address found in VPNPROFILE file #
        foreach ($Route in $VPNRulesxml.VPNProfile.Route)
        {
            $VPNIP=$Route.Address+"/"+$Route.PrefixSize
            [array]$ARRVPN=$ARRVPN+$VPNIP
        }

    # In optimize address list only #
    $In_Opt_Only= $optimizeIpsv4 | Where {$ARRVPN -NotContains $_}

    # In VPN list only #
    $In_VPN_only =$ARRVPN | Where {$optimizeIpsv4 -NotContains $_}
    [System.Collections.ArrayList]$Inpfile = get-content $VPNprofilefile

        if ($In_Opt_Only.Count -gt 0 )
        {
            Write-Host "Exclusion route IP addresses are unknown, missing, or need to be updated in the VPN profile`n" -ForegroundColor Red

                foreach ($NewIP in $In_Opt_Only)
                {
                    # Add the missing IP address(es) #
                    $IPInfo=$NewIP.Split("/")
                    $routes += "<Route>`n"+"`t<Address>"+$IPInfo[0].Trim()+"</Address>`n"+"`t<PrefixSize>"+$IPInfo[1].Trim()+"</PrefixSize>`n"+"`t<ExclusionRoute>true</ExclusionRoute>`n"+"</Route>`n"
                }
            $inspoint = $Inpfile.IndexOf("</VPNProfile>")
            $Inpfile.Insert($inspoint,$routes)

            # Update filename and write new XML file #
            $NewFileName=(Get-Item $VPNprofilefile).Basename + "-NEW.xml"
            $OutFile=$(Split-Path $VPNprofilefile -Parent)+"\"+$NewFileName
            $InpFile | Set-Content $OutFile
            Write-Host "Exclusion routes have been added to VPN profile and output to a separate XML file; the original file has not been modified`n`n" -ForegroundColor Green

        }else
        {
            Write-Host "Exclusion route IP addresses are correct and up to date in the VPN profile`n" -ForegroundColor Green
            $OutFile=$VPNprofilefile
        }

        if ( $In_VPN_Only.Count -gt 0 )
        {
            Write-Host "Unknown exclusion route IP addresses found in the VPN profile`n" -ForegroundColor Yellow

            foreach ($OldIP in $In_VPN_Only)
            {
                [array]$Inpfile = get-content $OutFile
                $IPInfo=$OldIP.Split("/")
                Write-Host "Unknown exclusion route IP address"$IPInfo[0]"has been found in the VPN profile - Do you wish to remove it? (Y/N)`n" -ForegroundColor Yellow
                $matchstr="<Route>"+"<Address>"+$IPInfo[0].Trim()+"</Address>"+"<PrefixSize>"+$IPInfo[1].Trim()+"</PrefixSize>"+"<ExclusionRoute>true</ExclusionRoute>"+"</Route>"
                $DelAns=Read-host
                    if ($DelAns.ToUpper() -eq "Y")
                    {
                        # Remove unknown IP address(es) #
                        $inspoint = $Inpfile[0].IndexOf($matchstr)
                        $Inpfile[0] = $Inpfile[0].Replace($matchstr,"")

                        # Update filename and write new XML file #
                        $NewFileName=(Get-Item $VPNprofilefile).Basename + "-NEW.xml"
                        $OutFile=$(Split-Path $VPNprofilefile -Parent)+"\"+$NewFileName
                        $Inpfile | Set-content $OutFile
                        Write-Host "`nAddress"$IPInfo[0]"exclusion route has been removed from the VPN profile and output to a separate XML file; the original file has not been modified`n" -ForegroundColor Green

                    }else
                    {
                        Write-Host "`nExclusion route IP address has *NOT* been removed from the VPN profile`n" -ForegroundColor Green
                    }
            }
        }
}

其他考慮

您也應該能夠調整此方法,以包含其他可由已知/靜態IP位址定義之雲端服務的必要排除專案; Cisco WebExZoom 所需的排除專案是不錯的範例。

範例

以下提供可用來建立與 Microsoft 365 排除專案之強制通道 VPN 連線的 PowerShell 腳本範例,或參閱 建立 ProfileXML 組態檔 以建立初始 PowerShell 腳本中的指引:

# Copyright (c) Microsoft Corporation.  All rights reserved.
#
# THIS SAMPLE CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
# WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
# IF THIS CODE AND INFORMATION IS MODIFIED, THE ENTIRE RISK OF USE OR RESULTS IN
# CONNECTION WITH THE USE OF THIS CODE AND INFORMATION REMAINS WITH THE USER.

<#
.SYNOPSIS
    Configures an AlwaysOn IKEv2 VPN Connection using a basic script
.DESCRIPTION
    Configures an AlwaysOn IKEv2 VPN Connection with proxy PAC information and force tunneling
.PARAMETERS
    Parameters are defined in a ProfileXML object within the script itself
.NOTES
    Requires at least Windows 10 Version 1803 with KB4493437, 1809 with KB4490481, or later
.VERSION
    1.0
#>

<#-- Define Key VPN Profile Parameters --#>
$ProfileName = 'Contoso VPN with Microsoft 365 Exclusions'
$ProfileNameEscaped = $ProfileName -replace ' ', '%20'

<#-- Define VPN ProfileXML --#>
$ProfileXML = '<VPNProfile>
      <RememberCredentials>true</RememberCredentials>
    <DnsSuffix>corp.contoso.com</DnsSuffix>
    <AlwaysOn>true</AlwaysOn>
    <TrustedNetworkDetection>corp.contoso.com</TrustedNetworkDetection>
<NativeProfile>
        <Servers>edge1.contoso.com</Servers>
        <RoutingPolicyType>ForceTunnel</RoutingPolicyType>
        <NativeProtocolType>IKEv2</NativeProtocolType>
        <Authentication>
            <MachineMethod>Certificate</MachineMethod>
        </Authentication>
    </NativeProfile>
    <Route>
      <Address>13.107.6.152</Address>
      <PrefixSize>31</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>13.107.18.10</Address>
      <PrefixSize>31</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>13.107.128.0</Address>
      <PrefixSize>22</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>23.103.160.0</Address>
      <PrefixSize>20</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>40.96.0.0</Address>
      <PrefixSize>13</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>40.104.0.0</Address>
      <PrefixSize>15</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>52.96.0.0</Address>
      <PrefixSize>14</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>131.253.33.215</Address>
      <PrefixSize>32</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>132.245.0.0</Address>
      <PrefixSize>16</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>150.171.32.0</Address>
      <PrefixSize>22</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>191.234.140.0</Address>
      <PrefixSize>22</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>204.79.197.215</Address>
      <PrefixSize>32</PrefixSize>
    <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>13.107.136.0</Address>
      <PrefixSize>22</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>40.108.128.0</Address>
      <PrefixSize>17</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>52.104.0.0</Address>
      <PrefixSize>14</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>104.146.128.0</Address>
      <PrefixSize>17</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>  
    <Route>
      <Address>150.171.40.0</Address>
      <PrefixSize>22</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>  
    <Route>
      <Address>13.107.60.1</Address>
      <PrefixSize>32</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>13.107.64.0</Address>
     <PrefixSize>18</PrefixSize>
    <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>52.112.0.0</Address>
      <PrefixSize>14</PrefixSize>
      <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Route>
      <Address>52.120.0.0</Address>
      <PrefixSize>14</PrefixSize>
    <ExclusionRoute>true</ExclusionRoute>
    </Route>
    <Proxy>  
            <AutoConfigUrl>http://webproxy.corp.contoso.com/proxy.pac</AutoConfigUrl>  
      </Proxy>  
</VPNProfile>'

<#-- Convert ProfileXML to Escaped Format --#>
$ProfileXML = $ProfileXML -replace '<', '&lt;'
$ProfileXML = $ProfileXML -replace '>', '&gt;'
$ProfileXML = $ProfileXML -replace '"', '&quot;'

<#-- Define WMI-to-CSP Bridge Properties --#>
$nodeCSPURI = './Vendor/MSFT/VPNv2'
$namespaceName = "root\cimv2\mdm\dmmap"
$className = "MDM_VPNv2_01"

<#-- Define WMI Session --#>
$session = New-CimSession

<#-- Detect and Delete Previous VPN Profile --#>
try
{
    $deleteInstances = $session.EnumerateInstances($namespaceName, $className, $options)
    foreach ($deleteInstance in $deleteInstances)
    {
        $InstanceId = $deleteInstance.InstanceID
        if ("$InstanceId" -eq "$ProfileNameEscaped")
        {
            $session.DeleteInstance($namespaceName, $deleteInstance, $options)
            $Message = "Removed $ProfileName profile $InstanceId"
            Write-Host "$Message"
        } else {
            $Message = "Ignoring existing VPN profile $InstanceId"
            Write-Host "$Message"
        }
    }
}
catch [Exception]
{
    $Message = "Unable to remove existing outdated instance(s) of $ProfileName profile: $_"
    Write-Host "$Message"
    exit
}

<#-- Create VPN Profile --#>
try
{
    $newInstance = New-Object Microsoft.Management.Infrastructure.CimInstance $className, $namespaceName
    $property = [Microsoft.Management.Infrastructure.CimProperty]::Create("ParentID", "$nodeCSPURI", 'String', 'Key')
    $newInstance.CimInstanceProperties.Add($property)
    $property = [Microsoft.Management.Infrastructure.CimProperty]::Create("InstanceID", "$ProfileNameEscaped", 'String', 'Key')
    $newInstance.CimInstanceProperties.Add($property)
    $property = [Microsoft.Management.Infrastructure.CimProperty]::Create("ProfileXML", "$ProfileXML", 'String', 'Property')
    $newInstance.CimInstanceProperties.Add($property)

    $session.CreateInstance($namespaceName, $newInstance, $options)
    $Message = "Created $ProfileName profile."
    Write-Host "$Message"
    Write-Host "$ProfileName profile summary:"  
    $session.EnumerateInstances($namespaceName, $className, $options)
}
catch [Exception]
{
    $Message = "Unable to create $ProfileName profile: $_"
    Write-Host "$Message"
    exit
}

$Message = "Script Complete"
Write-Host "$Message"

以下提供可用來建立與 Microsoft 365 排除專案之強制通道 VPN 連線的 Intune 就緒 XML 檔案範例,或參閱建立 ProfileXML 組態檔以建立初始 XML 檔案中的指引。

注意

這個 XML 會格式化為與 Intune 搭配使用,不能包含任何歸位字元或空格符。

<VPNProfile><RememberCredentials>true</RememberCredentials><DnsSuffix>corp.contoso.com</DnsSuffix><AlwaysOn>true</AlwaysOn><TrustedNetworkDetection>corp.contoso.com</TrustedNetworkDetection><NativeProfile><Servers>edge1.contoso.com</Servers><RoutingPolicyType>ForceTunnel</RoutingPolicyType><NativeProtocolType>IKEv2</NativeProtocolType><Authentication><MachineMethod>Certificate</MachineMethod></Authentication></NativeProfile><Route><Address>13.107.6.152</Address><PrefixSize>31</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>13.107.18.10</Address><PrefixSize>31</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>13.107.128.0</Address><PrefixSize>22</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>23.103.160.0</Address><PrefixSize>20</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>40.96.0.0</Address><PrefixSize>13</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>40.104.0.0</Address><PrefixSize>15</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>52.96.0.0</Address><PrefixSize>14</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>131.253.33.215</Address><PrefixSize>32</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>132.245.0.0</Address><PrefixSize>16</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>150.171.32.0</Address><PrefixSize>22</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>191.234.140.0</Address><PrefixSize>22</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>204.79.197.215</Address><PrefixSize>32</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>13.107.136.0</Address><PrefixSize>22</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>40.108.128.0</Address><PrefixSize>17</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>52.104.0.0</Address><PrefixSize>14</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>104.146.128.0</Address><PrefixSize>17</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>150.171.40.0</Address><PrefixSize>22</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>13.107.60.1</Address><PrefixSize>32</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>13.107.64.0</Address><PrefixSize>18</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>52.112.0.0</Address><PrefixSize>14</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Route><Address>52.120.0.0</Address><PrefixSize>14</PrefixSize><ExclusionRoute>true</ExclusionRoute></Route><Proxy><AutoConfigUrl>http://webproxy.corp.contoso.com/proxy.pac</AutoConfigUrl></Proxy></VPNProfile>