AzVpnConnect.psm1
|
$script:DefaultProfile = "MSFT-AzVPN-Manual" $script:AzureVpnAppId = 'shell:AppsFolder\Microsoft.AzureVpn_8wekyb3d8bbwe!App' function Get-AzVpnStatus { <# .SYNOPSIS Gets the connection status of an Azure VPN profile. .DESCRIPTION Queries the Windows VPN subsystem for the current connection status of the specified Azure VPN profile. .PARAMETER ProfileName The Windows VPN profile name. Defaults to MSFT-AzVPN-Manual. .EXAMPLE Get-AzVpnStatus .EXAMPLE Get-AzVpnStatus -ProfileName "MyVpn" #> [CmdletBinding()] param( [Parameter(Position = 0)] [string]$ProfileName = $script:DefaultProfile ) $vpn = Get-VpnConnection -Name $ProfileName -ErrorAction Stop [PSCustomObject]@{ ProfileName = $vpn.Name Status = $vpn.ConnectionStatus Server = $vpn.ServerAddress SplitTunnel = $vpn.SplitTunneling } } function Connect-AzVpn { <# .SYNOPSIS Ensures the Azure VPN is connected, authenticating if needed. .DESCRIPTION Checks the VPN status and connects if disconnected. Uses rasdial for headless connection when credentials are cached. If credentials have expired, launches the Azure VPN Client app for interactive authentication and polls until the connection comes up or the timeout is reached. .PARAMETER ProfileName The Windows VPN profile name. Defaults to MSFT-AzVPN-Manual. .PARAMETER TimeoutSeconds How long to wait for interactive authentication before giving up. Defaults to 60 seconds. .PARAMETER Force Connect even if already connected (reconnect). .EXAMPLE Connect-AzVpn .EXAMPLE Connect-AzVpn -TimeoutSeconds 120 .EXAMPLE Connect-AzVpn -Force #> [CmdletBinding()] param( [Parameter(Position = 0)] [string]$ProfileName = $script:DefaultProfile, [Parameter()] [int]$TimeoutSeconds = 60, [Parameter()] [switch]$Force ) $status = (Get-VpnConnection -Name $ProfileName -ErrorAction Stop).ConnectionStatus if (($status -eq 'Connected') -and (-not $Force)) { Write-Verbose "$ProfileName is already connected." return [PSCustomObject]@{ ProfileName = $ProfileName; Connected = $true; Method = 'AlreadyConnected' } } if (($status -eq 'Connected') -and $Force) { Write-Verbose "Force reconnect -- disconnecting first." $null = rasdial $ProfileName /disconnect 2>&1 Start-Sleep -Seconds 2 } if ($status -eq 'Connecting') { Write-Verbose "$ProfileName is mid-connect, waiting..." if (Wait-AzVpnConnected -ProfileName $ProfileName -TimeoutSeconds $TimeoutSeconds) { return [PSCustomObject]@{ ProfileName = $ProfileName; Connected = $true; Method = 'WaitedForExisting' } } throw "${ProfileName}: timed out waiting for in-progress connection." } # Try rasdial -- headless, works when credentials are cached Write-Verbose "Attempting headless connect via rasdial..." $null = rasdial $ProfileName 2>&1 if ($LASTEXITCODE -eq 0) { Write-Verbose "rasdial succeeded." return [PSCustomObject]@{ ProfileName = $ProfileName; Connected = $true; Method = 'Rasdial' } } # Credentials expired -- open the Azure VPN Client for interactive auth $rasExit = $LASTEXITCODE Write-Warning "rasdial failed (exit $rasExit). Opening Azure VPN Client for authentication..." explorer.exe $script:AzureVpnAppId Write-Host "Please connect in the Azure VPN Client. Waiting up to ${TimeoutSeconds}s..." -ForegroundColor Cyan if (Wait-AzVpnConnected -ProfileName $ProfileName -TimeoutSeconds $TimeoutSeconds) { return [PSCustomObject]@{ ProfileName = $ProfileName; Connected = $true; Method = 'Interactive' } } throw "${ProfileName}: failed to connect within ${TimeoutSeconds}s." } function Disconnect-AzVpn { <# .SYNOPSIS Disconnects the Azure VPN. .PARAMETER ProfileName The Windows VPN profile name. Defaults to MSFT-AzVPN-Manual. .EXAMPLE Disconnect-AzVpn #> [CmdletBinding()] param( [Parameter(Position = 0)] [string]$ProfileName = $script:DefaultProfile ) $status = (Get-VpnConnection -Name $ProfileName -ErrorAction Stop).ConnectionStatus if ($status -eq 'Disconnected') { Write-Verbose "$ProfileName is already disconnected." return } $null = rasdial $ProfileName /disconnect 2>&1 if ($LASTEXITCODE -ne 0) { throw "Failed to disconnect $ProfileName (exit $LASTEXITCODE)." } Write-Verbose "$ProfileName disconnected." } function Wait-AzVpnConnected { <# .SYNOPSIS Polls until the Azure VPN connects or the timeout expires. .PARAMETER ProfileName The Windows VPN profile name. .PARAMETER TimeoutSeconds Maximum seconds to wait. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$ProfileName, [Parameter()] [int]$TimeoutSeconds = 60 ) $deadline = (Get-Date).AddSeconds($TimeoutSeconds) while ((Get-Date) -lt $deadline) { $s = (Get-VpnConnection -Name $ProfileName -ErrorAction SilentlyContinue).ConnectionStatus if ($s -eq 'Connected') { return $true } Start-Sleep -Seconds 2 } return $false } Export-ModuleMember -Function Connect-AzVpn, Disconnect-AzVpn, Get-AzVpnStatus |