#Requires -Version 5.1 #Requires -RunAsAdministrator [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)][string]$VpnProfilePackagePath, [Parameter(Mandatory)][string]$ClientCertificatePfxPath, [securestring]$ClientCertificatePfxPassword, [Parameter(Mandatory)][string]$ClientRootCertificatePath, [string]$ConnectionName = 'SGU Azure P2S', [string[]]$AzureNetworkPrefixes = @('10.77.0.0/16'), [ipaddress]$DomainControllerIPv4Address = '10.77.0.4', [string]$DomainName = 'lci.lasalle.mx', [switch]$Connect ) $ErrorActionPreference = 'Stop' foreach ($path in @($VpnProfilePackagePath,$ClientCertificatePfxPath,$ClientRootCertificatePath)) { if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { throw "Required P2S file not found: $path" } } if (-not $ClientCertificatePfxPassword) { $ClientCertificatePfxPassword = Read-Host 'Password protecting the P2S client PFX' -AsSecureString } $temporaryRoot = Join-Path $env:ProgramData ("SGU\AzureP2S\Import-" + [Guid]::NewGuid().ToString('N')) try { Expand-Archive -LiteralPath $VpnProfilePackagePath -DestinationPath $temporaryRoot -Force $vpnSettingsPath = Get-ChildItem -LiteralPath $temporaryRoot -Recurse -Filter VpnSettings.xml -File | Select-Object -First 1 -ExpandProperty FullName if (-not $vpnSettingsPath) { throw 'The Azure package does not contain Generic\VpnSettings.xml. Generate it with IKEv2 enabled.' } [xml]$vpnSettings = Get-Content -LiteralPath $vpnSettingsPath -Raw $vpnServerNode = $vpnSettings.SelectSingleNode('//*[local-name()="VpnServer"]') if (-not $vpnServerNode -or [string]::IsNullOrWhiteSpace($vpnServerNode.InnerText)) { throw 'VpnSettings.xml does not contain the Azure VPN gateway FQDN.' } $vpnServer = $vpnServerNode.InnerText.Trim() $serverRootPath = Get-ChildItem -LiteralPath (Split-Path $vpnSettingsPath -Parent) ` -Filter VpnServerRoot.cer -File | Select-Object -First 1 -ExpandProperty FullName if ($serverRootPath) { Import-Certificate -FilePath $serverRootPath -CertStoreLocation Cert:\LocalMachine\Root | Out-Null } $clientRoot = Import-Certificate -FilePath $ClientRootCertificatePath ` -CertStoreLocation Cert:\LocalMachine\Root | Select-Object -First 1 $clientCertificates = @(Import-PfxCertificate -FilePath $ClientCertificatePfxPath ` -Password $ClientCertificatePfxPassword -CertStoreLocation Cert:\LocalMachine\My) $clientCertificate = $clientCertificates | Where-Object { $_.HasPrivateKey -and $_.NotAfter -gt (Get-Date) -and @($_.EnhancedKeyUsageList | ForEach-Object ObjectId) -contains '1.3.6.1.5.5.7.3.2' } | Sort-Object NotAfter -Descending | Select-Object -First 1 if (-not $clientCertificate) { throw 'The imported PFX does not contain a valid Client Authentication certificate with a private key.' } if ($PSCmdlet.ShouldProcess($ConnectionName, 'Install an all-user IKEv2 Azure P2S connection using a machine certificate')) { $existingConnection = Get-VpnConnection -Name $ConnectionName -AllUserConnection ` -ErrorAction SilentlyContinue if ($existingConnection) { Remove-VpnConnection -Name $ConnectionName -AllUserConnection -Force } $dnsParameters = @{} if ($DomainName) { $dnsParameters.DnsSuffix = $DomainName } Add-VpnConnection @dnsParameters ` -Name $ConnectionName ` -ServerAddress $vpnServer ` -TunnelType Ikev2 ` -AuthenticationMethod MachineCertificate ` -MachineCertificateIssuerFilter $clientRoot ` -MachineCertificateEKUFilter '1.3.6.1.5.5.7.3.2' ` -EncryptionLevel Required ` -SplitTunneling ` -AllUserConnection ` -Force | Out-Null foreach ($prefix in $AzureNetworkPrefixes) { Add-VpnConnectionRoute -ConnectionName $ConnectionName ` -DestinationPrefix $prefix -AllUserConnection -PassThru | Out-Null } # The unified bootstrap can discover the domain after connecting. if ($DomainName) { $nrptDisplayName = "SGU Azure P2S DNS - $DomainName" Get-DnsClientNrptRule -ErrorAction SilentlyContinue | Where-Object DisplayName -eq $nrptDisplayName | Remove-DnsClientNrptRule -Force Add-DnsClientNrptRule ` -Namespace ".$DomainName" ` -NameServers $DomainControllerIPv4Address.IPAddressToString ` -DisplayName $nrptDisplayName ` -Comment 'Managed by SGU Azure P2S bootstrap; routes only the AD namespace to the domain controller.' | Out-Null } } if ($Connect) { & "$env:SystemRoot\System32\rasdial.exe" $ConnectionName if ($LASTEXITCODE -ne 0) { throw "Windows could not connect $ConnectionName. Verify UDP 500/4500 (IKEv2) or use the Azure-generated SSTP profile when the local network blocks IKEv2." } } $connection = Get-VpnConnection -Name $ConnectionName -AllUserConnection [pscustomobject]@{ ConnectionName = $connection.Name ServerAddress = $connection.ServerAddress TunnelType = $connection.TunnelType AllUserConnection = $true AuthenticationMethod = $connection.AuthenticationMethod ConnectionStatus = $connection.ConnectionStatus ClientCertificateThumbprint = $clientCertificate.Thumbprint DomainControllerIPv4Address = $DomainControllerIPv4Address.IPAddressToString DomainDnsNamespace = ".$DomainName" AzureNetworkPrefixes = $AzureNetworkPrefixes AvailableBeforeLogon = $true } } finally { $ClientCertificatePfxPassword = $null if (Test-Path -LiteralPath $temporaryRoot) { Remove-Item -LiteralPath $temporaryRoot -Recurse -Force -ErrorAction SilentlyContinue } }