教程:部署 Always On VPN - 为 Windows 10+ 客户端配置 Always On VPN 配置文件

适用于:Windows Server 2022、Windows Server 2019、Windows Server 2016、Windows Server 2012 R2、Windows 10 和 Windows 11

本教程的最后一部分(本文)介绍如何使用 ProfileXML PowerShell 配置脚本来配置 Always On VPN 设置,并为客户端连接创建用户隧道。

有关配置服务提供程序 (CSP) 的 Always on VPN 配置选项的更详细信息,请参阅 VPNv2 配置服务提供程序

先决条件

完成教程:部署 Always On VPN - 配置证书颁发机构模板

创建 Always On VPN 客户端配置文件

在本部分,我们将创建一个 VPN 客户端连接,以验证 VPN 测试客户端是否可以成功建立 VPN 连接。 此连接还使我们能够创建要在下一部分导出的 EAP 设置。

有关 EAP 设置的详细信息,请参阅 EAP 配置

  1. 以在创建 Active Directory 测试用户部分中创建的 VPN 用户身份登录到已加入域的 VPN 客户端计算机。

  2. 在“开始”菜单中键入 VPN,以选择 VPN 设置。 按 Enter。

  3. 在详细信息窗格中,选择添加 VPN 连接

  4. 对于VPN 提供程序,请选择 Windows (内置)

  5. 对于连接名称,请输入 Contoso VPN

  6. 对于服务器名称或地址,请输入 VPN 服务器的外部 FQDN(例如 vpn.contoso.com)。

  7. 对于 VPN 类型,请选择 IKEv2

  8. 对于“登录信息的类型”,请选择“证书”。

  9. 选择保存

  10. 在“相关设置”下,选择更改适配器选项

  11. 右键单击 Contoso VPN 并选择属性

  12. 安全性选项卡上,对于数据加密,请选择最大强度加密

  13. 选择使用可扩展的身份验证协议(EAP)。 然后,对于使用可扩展的身份验证协议(EAP),请选择 Microsoft: 受保护的 EAP (PEAP) (启用加密)

  14. 选择属性打开“受保护的 EAP 属性”,然后完成以下步骤:

    1. 对于连接到这些服务器,请输入 NPS 服务器的名称。

    2. 对于受信任的根证书颁发机构,请选择颁发 NPS 服务器证书的 CA(例如 contoso-CA)。

    3. 对于连接之前进行通知,请选择不要求用户授权新服务器或受信任的 CA

    4. 对于选择身份验证方法,请选择智能卡或其他证书

    5. 选择配置

      1. 选择在此计算机上使用证书

      2. 对于连接到这些服务器,请输入 NPS 服务器的名称。

      3. 对于受信任的根证书颁发机构,请选择颁发 NPS 服务器证书的 CA。

      4. 选择不提示用户授权新服务器或受信任的证书颁发机构

      5. 选择确定关闭“智能卡或其他证书属性”。

      6. 选择确定关闭“受保护的 EAP 属性”。

    6. 选择确定关闭“Contoso VPN 属性”。

  15. 关闭“网络连接”窗口。

  16. 在“设置”中选择 Contoso VPN,然后选择连接

重要

确保模板 VPN 成功连接到 VPN 服务器。 这样可以确保在下一步骤中使用 EAP 设置之前这些设置正确。 在继续操作之前,必须至少进行一次连接;否则,配置文件不会包含连接到 VPN 所需的所有信息。

配置 Windows VPN 客户端

在本部分,你将使用 PowerShell 脚本手动配置 Windows VPN 客户端。

  1. 以 VPN 用户身份登录到 VPN 客户端计算机。

  2. 以管理员身份打开 Windows PowerShell 集成脚本环境 (ISE)。

  3. 复制并粘贴以下脚本:

    
    # Define key VPN profile parameters
    # Replace with your own values
    
    $Domain = 'corp' # Name of the domain.
    
    $TemplateName = 'Contoso VPN' # Name of the test VPN connection you created in the tutorial. 
    
    $ProfileName = 'Contoso AlwaysOn VPN' # Name of the profile we are going to create.
    
    $Servers = 'aov-vpn.contoso.com' #Public or routable IP address or DNS name for the VPN gateway.
    
    $DnsSuffix = 'corp.contoso.com' # Specifies one or more commas separated DNS suffixes. 
    
    $DomainName = '.corp.contoso.com' #Used to indicate the namespace to which the policy applies. Contains `.` prefix.
    
    $DNSServers = '10.10.0.6' #List of comma-separated DNS Server IP addresses to use for the namespace.
    
    $TrustedNetwork = 'corp.contoso.com' #Comma-separated string to identify the trusted network.
    
    
    #Get the EAP settings for the current profile called $TemplateName
    
    $Connection = Get-VpnConnection -Name $TemplateName
    
    if(!$Connection)
    {
        $Message = "Unable to get $TemplateName connection profile: $_"
        Write-Host "$Message"
        exit
    }
    
    $EAPSettings= $Connection.EapConfigXmlStream.InnerXml
    
    $ProfileNameEscaped = $ProfileName -replace ' ', '%20'
    
    # Define ProfileXML
    $ProfileXML = @("
    <VPNProfile>
      <DnsSuffix>$DnsSuffix</DnsSuffix>
      <NativeProfile>
    <Servers>$Servers</Servers>
    <NativeProtocolType>IKEv2</NativeProtocolType>
    <Authentication>
      <UserMethod>Eap</UserMethod>
      <Eap>
        <Configuration>
        $EAPSettings
        </Configuration>
      </Eap>
    </Authentication>
    <RoutingPolicyType>SplitTunnel</RoutingPolicyType>
      </NativeProfile>
    <AlwaysOn>true</AlwaysOn>
    <RememberCredentials>true</RememberCredentials>
    <TrustedNetworkDetection>$TrustedNetwork</TrustedNetworkDetection>
      <DomainNameInformation>
    <DomainName>$DomainName</DomainName>
    <DnsServers>$DNSServers</DnsServers>
    </DomainNameInformation>
    </VPNProfile>
    ")
    
    #Output the XML for possible use in Intune
    $ProfileXML | Out-File -FilePath ($env:USERPROFILE + '\desktop\VPN_Profile.xml')
    
    # Escape special characters in the profile (<,>,")
    $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"
    
    try
    {
    
        # Determine user SID for VPN profile.
        $WmiLoggedOnUsers = (Get-WmiObject Win32_LoggedOnUser).Antecedent
        If($WmiLoggedOnUsers.Count -gt 1) { 
            $WmiLoggedOnUsers = $WmiLoggedOnUsers -match "Domain=""$Domain"""
        }
    
        $WmiUserValid = ($WmiLoggedOnUsers | Select-Object -Unique -First 1) -match 'Domain="([^"]+)",Name="([^"]+)"'
    
        If(-not $WmiUserValid){
            Throw "Returned object is not a valid WMI string"
        }
    
    
        $UserName = "$($Matches[1])\$($Matches[2])"
    
        $ObjUser = New-Object System.Security.Principal.NTAccount($UserName)
        $Sid = $ObjUser.Translate([System.Security.Principal.SecurityIdentifier])
        $SidValue = $Sid.Value
        $Message = "User SID is $SidValue."
    
        Write-Host "$Message"
    
    }
    catch [Exception] 
    {
    
        $Message = "Unable to get user SID. $_"
        Write-Host "$Message" 
        exit
    }
    
    try 
    {
        # Define WMI session.
        $session = New-CimSession
        $options = New-Object Microsoft.Management.Infrastructure.Options.CimOperationOptions
        $options.SetCustomOption("PolicyPlatformContext_PrincipalContext_Type", "PolicyPlatform_UserContext", $false)
        $options.SetCustomOption("PolicyPlatformContext_PrincipalContext_Id", "$SidValue", $false)
    
    }
    catch {
    
        $Message = "Unable to create new session for $ProfileName profile: $_"
        Write-Host $Message
        exit
    }
    
    try
    {
        #Detect and delete previous VPN profile.
        $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
    
    }
    
    try
    {
        # Create the VPN profile.
        $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"
    
    }
    catch [Exception]
    {
    
        $Message = "Unable to create $ProfileName profile: $_"
        Write-Host "$Message"
        exit
    }
    
    $Message = "Script Complete"
    Write-Host "$Message"
    
    
  4. 在脚本的顶部设置以下变量的值:$Domain$TemplateName$ProfileName$Servers$DnsSuffix$DomainName$DNSServers。 有关如何设置这些变量的更详细信息,请参阅:VPNv2 CSP

  5. 按 Enter 运行该脚本。

  6. 在 Windows PowerShell ISE 中运行以下命令,验证脚本是否成功:

        Get-CimInstance -Namespace root\cimv2\mdm\dmmap -ClassName MDM_VPNv2_01
    
  7. 你应会看到以下输出(为便于阅读,ProfileXML 值已截断):

    
    AlwaysOn                : True
    ByPassForLocal          : 
    DeviceTunnel            : 
    DnsSuffix               : corp.contoso.com
    EdpModeId               : 
    InstanceID              : Contoso%20AlwaysOn%20VPN
    LockDown                : 
    ParentID                : ./Vendor/MSFT/VPNv2
    ProfileXML              : <VPNProfile>...</VPNProfile>
    RegisterDNS             : 
    RememberCredentials     : True
    TrustedNetworkDetection : corp.contoso.com
    PSComputerName          : 
    `
    

现在,你已经为 Always On VPN 配置了用户隧道。 若要了解如何配置设备隧道,请参阅在 Windows 客户端中配置 VPN 设备隧道

后续步骤