From 4f885580e5befa27c57c7bd3ed2b0c901f44d650 Mon Sep 17 00:00:00 2001 From: Paul Harper Date: Fri, 30 Oct 2026 11:24:11 -0700 Subject: [PATCH 1/3] feat(windows): honor HTTPS_PROXY, HTTP_PROXY and NO_PROXY in PowerShell scripts Windows PowerShell 5.1 ignores the proxy environment variables that curl reads on Linux, so install.ps1 and the manager's upgrade command could not reach GitHub, the image registry or the Fivetran API from a proxied host unless the user set a .NET proxy in the same session. Both scripts now build a System.Net.WebProxy from HTTPS_PROXY (or HTTP_PROXY), including URL credentials, and translate NO_PROXY entries into bypass rules with curl's host-and-subdomains semantics. Co-Authored-By: Claude Fable 5.1 --- README.md | 14 +++++++++++++ install.ps1 | 45 +++++++++++++++++++++++++++++++++++++++++ proxy-agent-manager.ps1 | 39 +++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+) diff --git a/README.md b/README.md index 288f73f..d05a301 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,20 @@ Windows Server hosts use an exact LTSC mapping: Server 2019 selects `ltsc2019`, Linux releases continue to use `proxy-agent:` for backward compatibility and are also published as `proxy-agent:-ubuntu-26.04`. Both tags reference the same Linux multi-architecture image. Windows releases use the explicit `proxy-agent:-windows-ltsc2019`, `proxy-agent:-windows-ltsc2022`, or `proxy-agent:-windows-ltsc2025` tags. +### Installing behind a network proxy + +Both installers and the `upgrade` command honor `HTTPS_PROXY` (or `HTTP_PROXY`) and `NO_PROXY` for the web requests they make: downloading the management script, listing image tags, and fetching the agent configuration from Fivetran. On Linux, `curl` reads these variables natively. On Windows, Windows PowerShell 5.1 does not, so `install.ps1` and `proxy-agent-manager.ps1` apply them themselves. The URL may include credentials: `http://user:pass@proxy.example.com:3128`. + +```powershell +$env:HTTPS_PROXY = 'http://proxy.example.com:3128' +$env:NO_PROXY = 'localhost,127.0.0.1,.example.com' +$env:RUNTIME = 'docker'; $env:TOKEN = 'YOUR_AGENT_TOKEN'; & .\install.ps1 +``` + +On Windows, download `install.ps1` first, as shown above, rather than fetching it inline with `Invoke-RestMethod`: the download itself runs before the script can apply the proxy settings. + +These variables do not configure Docker. The Docker daemon needs its own proxy settings to pull the agent image, and the agent needs `proxy_host` and `proxy_port` in `config.json` to reach Fivetran through a proxy at runtime. See the [Proxy Agent documentation](https://fivetran.com/docs/destinations/connection-options/proxy-agent) for both. + The installer will: - Check prerequisites - Create the installation directory diff --git a/install.ps1 b/install.ps1 index 554bd30..5e5d8aa 100644 --- a/install.ps1 +++ b/install.ps1 @@ -47,10 +47,53 @@ Usage: Options: -InstallDir Installation directory (default: %USERPROFILE%\fivetran-proxy-agent) -WindowsVersion Windows LTSC image variant for Windows containers; detected from the host when omitted + +Environment: + HTTPS_PROXY / HTTP_PROXY Proxy URL for the installer's web requests (http://[user:pass@]host:port) + NO_PROXY Comma-separated hosts that bypass the proxy '@ exit 1 } +# Windows PowerShell 5.1 ignores HTTP_PROXY/HTTPS_PROXY/NO_PROXY, so apply them to this +# script's web requests (GitHub, the image registry, the Fivetran API). curl honors the +# same variables, so install.sh and install.ps1 behave alike behind a proxy. Docker itself +# reads its own proxy settings; see the README. +function Set-WebProxyFromEnvironment { + $proxyUrl = if ($env:HTTPS_PROXY) { $env:HTTPS_PROXY } elseif ($env:HTTP_PROXY) { $env:HTTP_PROXY } else { $null } + if (-not $proxyUrl) { return } + + try { + $proxyUri = [Uri]$proxyUrl + } catch { + Write-Host "ERROR: Invalid proxy URL in HTTPS_PROXY/HTTP_PROXY: $proxyUrl" -ForegroundColor Red + exit 1 + } + + # .NET bypass entries are regular expressions matched against the request URL. As with curl, + # each entry matches the host and its subdomains, with or without a leading dot. CIDR ranges aren't supported. + $bypassList = @() + if ($env:NO_PROXY) { + foreach ($entry in ($env:NO_PROXY -split ',')) { + $bypassHost = $entry.Trim().TrimStart('.') + if (-not $bypassHost) { continue } + $bypassList += '^https?://([^/]+\.)?' + [regex]::Escape($bypassHost) + '(:\d+)?(/|$)' + } + } + + # Keep credentials out of the proxy address; pass them separately. + $proxyAddress = [Uri]"$($proxyUri.Scheme)://$($proxyUri.Authority)" + $webProxy = New-Object System.Net.WebProxy($proxyAddress, $false, [string[]]$bypassList) + if ($proxyUri.UserInfo) { + $userInfo = $proxyUri.UserInfo.Split(':', 2) + $proxyUser = [Uri]::UnescapeDataString($userInfo[0]) + $proxyPassword = if ($userInfo.Length -gt 1) { [Uri]::UnescapeDataString($userInfo[1]) } else { '' } + $webProxy.Credentials = New-Object System.Net.NetworkCredential($proxyUser, $proxyPassword) + } + [System.Net.WebRequest]::DefaultWebProxy = $webProxy + Write-Host "Using proxy $proxyAddress from the environment" +} + # ── Validation ─────────────────────────────────────────────────────────────── function Test-DockerVersion { @@ -250,6 +293,8 @@ if ($env:RUNTIME -ne 'docker') { exit 1 } +Set-WebProxyFromEnvironment + $currentPrincipal = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() if ($currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Warning "Running as Administrator is not recommended. Consider running as a regular user." diff --git a/proxy-agent-manager.ps1 b/proxy-agent-manager.ps1 index d0c0445..9779b47 100644 --- a/proxy-agent-manager.ps1 +++ b/proxy-agent-manager.ps1 @@ -70,6 +70,44 @@ function Write-Log { Add-Content -Path $LOGFILE -Value "$timestamp UTC - $Message" -Encoding ascii } +# Windows PowerShell 5.1 ignores HTTP_PROXY/HTTPS_PROXY/NO_PROXY, so apply them to this +# script's web requests (the image registry lookup during upgrade). Kept in sync with +# install.ps1. Docker itself reads its own proxy settings; see the README. +function Set-WebProxyFromEnvironment { + $proxyUrl = if ($env:HTTPS_PROXY) { $env:HTTPS_PROXY } elseif ($env:HTTP_PROXY) { $env:HTTP_PROXY } else { $null } + if (-not $proxyUrl) { return } + + try { + $proxyUri = [Uri]$proxyUrl + } catch { + Write-Host "ERROR: Invalid proxy URL in HTTPS_PROXY/HTTP_PROXY: $proxyUrl" -ForegroundColor Red + exit 1 + } + + # .NET bypass entries are regular expressions matched against the request URL. As with curl, + # each entry matches the host and its subdomains, with or without a leading dot. CIDR ranges aren't supported. + $bypassList = @() + if ($env:NO_PROXY) { + foreach ($entry in ($env:NO_PROXY -split ',')) { + $bypassHost = $entry.Trim().TrimStart('.') + if (-not $bypassHost) { continue } + $bypassList += '^https?://([^/]+\.)?' + [regex]::Escape($bypassHost) + '(:\d+)?(/|$)' + } + } + + # Keep credentials out of the proxy address; pass them separately. + $proxyAddress = [Uri]"$($proxyUri.Scheme)://$($proxyUri.Authority)" + $webProxy = New-Object System.Net.WebProxy($proxyAddress, $false, [string[]]$bypassList) + if ($proxyUri.UserInfo) { + $userInfo = $proxyUri.UserInfo.Split(':', 2) + $proxyUser = [Uri]::UnescapeDataString($userInfo[0]) + $proxyPassword = if ($userInfo.Length -gt 1) { [Uri]::UnescapeDataString($userInfo[1]) } else { '' } + $webProxy.Credentials = New-Object System.Net.NetworkCredential($proxyUser, $proxyPassword) + } + [System.Net.WebRequest]::DefaultWebProxy = $webProxy + Write-Log "Using proxy $proxyAddress from the environment" +} + function Get-ContainerOsType { $osTypeOutput = docker info --format '{{.OSType}}' 2>$null if ($LASTEXITCODE -ne 0) { @@ -404,6 +442,7 @@ switch ($Command) { if (-not (Start-CurrentProxyAgent)) { exit 1 } } 'upgrade' { + Set-WebProxyFromEnvironment Invoke-Upgrade } 'status' { From 186cc62faaf8fcbe498ecb136e171e98cba6e162 Mon Sep 17 00:00:00 2001 From: Paul Harper Date: Fri, 30 Oct 2026 11:31:43 -0700 Subject: [PATCH 2/3] test(windows): cover proxy environment handling in the manager Exercises HTTPS_PROXY with credentials and NO_PROXY, the HTTP_PROXY fallback, the no-op path, and an invalid proxy URL through the upgrade command, and asserts that install.ps1 carries the same function body so the two copies cannot drift apart. Co-Authored-By: Claude Fable 5.1 --- tests/pwsh/Manager.Tests.ps1 | 73 ++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/tests/pwsh/Manager.Tests.ps1 b/tests/pwsh/Manager.Tests.ps1 index 07c8b6f..fc04729 100644 --- a/tests/pwsh/Manager.Tests.ps1 +++ b/tests/pwsh/Manager.Tests.ps1 @@ -114,8 +114,9 @@ AfterAll { Describe 'proxy-agent-manager.ps1' { BeforeEach { - # A developer's environment must not leak into the default-URL assertion. - Remove-Item Env:\PROXY_AGENT_REGISTRY_TAGS_URL -ErrorAction SilentlyContinue + # A developer's environment must not leak into the default-URL or proxy assertions. + Remove-Item Env:\PROXY_AGENT_REGISTRY_TAGS_URL, Env:\HTTPS_PROXY, Env:\HTTP_PROXY, Env:\NO_PROXY -ErrorAction SilentlyContinue + $script:OriginalWebProxy = [System.Net.WebRequest]::DefaultWebProxy $global:ProxyAgentStub = @{ OsType = 'linux' LastImage = $null @@ -128,7 +129,8 @@ Describe 'proxy-agent-manager.ps1' { } AfterEach { - Remove-Item Env:\PROXY_AGENT_REGISTRY_TAGS_URL -ErrorAction SilentlyContinue + Remove-Item Env:\PROXY_AGENT_REGISTRY_TAGS_URL, Env:\HTTPS_PROXY, Env:\HTTP_PROXY, Env:\NO_PROXY -ErrorAction SilentlyContinue + [System.Net.WebRequest]::DefaultWebProxy = $script:OriginalWebProxy if ($script:AgentDir -and (Test-Path -LiteralPath $script:AgentDir)) { Remove-Item -LiteralPath $script:AgentDir -Recurse -Force } @@ -271,4 +273,69 @@ Describe 'proxy-agent-manager.ps1' { $decoded | Should -Match 'HEARTBEAT_EXPIRE_AT' } } + + # The manager applies HTTPS_PROXY/HTTP_PROXY/NO_PROXY to the process-wide + # default proxy before the registry lookup, since Windows PowerShell 5.1 + # ignores those variables. The script runs in this process, so the result + # is observable here; AfterEach restores the original default proxy. + Context 'behind a network proxy' { + It 'applies HTTPS_PROXY with credentials and NO_PROXY on upgrade' { + $env:HTTPS_PROXY = 'http://us%40er:p%3Ass@proxy.example.com:3128' + $env:NO_PROXY = 'localhost, .corp.example.com' + $manager = New-AgentDir + $output = Invoke-Manager $manager upgrade + $LASTEXITCODE | Should -Be 0 + $output | Should -Match 'Using proxy http://proxy.example.com:3128/' + + $proxy = [System.Net.WebRequest]::DefaultWebProxy + $proxy.Address | Should -Be 'http://proxy.example.com:3128/' + $proxy.Credentials.UserName | Should -Be 'us@er' + $proxy.Credentials.Password | Should -Be 'p:ss' + $proxy.IsBypassed([Uri]'https://us-docker.pkg.dev/v2/tags/list') | Should -BeFalse + $proxy.IsBypassed([Uri]'http://localhost:8080/') | Should -BeTrue + $proxy.IsBypassed([Uri]'https://corp.example.com/') | Should -BeTrue + $proxy.IsBypassed([Uri]'https://db.corp.example.com:5432/') | Should -BeTrue + $proxy.IsBypassed([Uri]'https://notcorp.example.com/') | Should -BeFalse + } + + It 'falls back to HTTP_PROXY without credentials' { + $env:HTTP_PROXY = 'http://proxy.example.com:8080' + $manager = New-AgentDir + $null = Invoke-Manager $manager upgrade + $LASTEXITCODE | Should -Be 0 + $proxy = [System.Net.WebRequest]::DefaultWebProxy + $proxy.Address | Should -Be 'http://proxy.example.com:8080/' + $proxy.Credentials | Should -BeNullOrEmpty + } + + It 'leaves the default proxy alone when no proxy variables are set' { + $manager = New-AgentDir + $null = Invoke-Manager $manager upgrade + [object]::ReferenceEquals($script:OriginalWebProxy, [System.Net.WebRequest]::DefaultWebProxy) | Should -BeTrue + } + + It 'rejects an invalid proxy URL before contacting the registry' { + $env:HTTPS_PROXY = 'http://bad host:' + $manager = New-AgentDir + $output = Invoke-Manager $manager upgrade + $LASTEXITCODE | Should -Be 1 + $output | Should -Match 'Invalid proxy URL' + $global:ProxyAgentStub.RegistryUrl | Should -BeNullOrEmpty + } + + It 'shares its proxy handling with install.ps1' { + # The two scripts are downloaded separately, so the function is duplicated + # rather than shared. This keeps the copies from drifting apart; the only + # allowed difference is that the manager logs through Write-Log. + function Get-ProxyFunctionBody { + param([string]$ScriptName) + $tokens = $null; $errors = $null + $ast = [System.Management.Automation.Language.Parser]::ParseFile((Join-Path $script:RepoRoot $ScriptName), [ref]$tokens, [ref]$errors) + $function = $ast.Find({ param($node) $node -is [System.Management.Automation.Language.FunctionDefinitionAst] -and $node.Name -eq 'Set-WebProxyFromEnvironment' }, $false) + $function | Should -Not -BeNullOrEmpty -Because "$ScriptName must define Set-WebProxyFromEnvironment" + return $function.Body.Extent.Text -replace 'Write-Log ', 'Write-Host ' + } + Get-ProxyFunctionBody 'proxy-agent-manager.ps1' | Should -Be (Get-ProxyFunctionBody 'install.ps1') + } + } } From 775f839579d580a469ac83994fed34af75654d50 Mon Sep 17 00:00:00 2001 From: Paul Harper Date: Fri, 30 Oct 2026 11:47:19 -0700 Subject: [PATCH 3/3] feat(windows): use Windows credentials for the proxy and report the system proxy Corporate proxies on Windows commonly require NTLM or Kerberos, which .NET only answers when told to. Both the proxy built from the environment (when its URL carries no credentials) and the Internet Options default now use the current Windows identity. When no proxy variables are set, the scripts also print the system proxy in effect for the registry host, so a stalled install shows what it is going through. Co-Authored-By: Claude Fable 5.1 --- README.md | 2 ++ install.ps1 | 27 ++++++++++++++++++++++----- proxy-agent-manager.ps1 | 26 ++++++++++++++++++++++---- tests/pwsh/Manager.Tests.ps1 | 24 +++++++++++++++++++----- 4 files changed, 65 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index d05a301..0f2e962 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,8 @@ $env:NO_PROXY = 'localhost,127.0.0.1,.example.com' $env:RUNTIME = 'docker'; $env:TOKEN = 'YOUR_AGENT_TOKEN'; & .\install.ps1 ``` +On Windows, the variables take precedence over the proxy in Internet Options; without them, the scripts use the Internet Options proxy as any PowerShell web request would, and print it when one is in effect. In both cases a proxy that asks for NTLM or Kerberos authentication is answered with the current Windows identity, unless the URL carries its own credentials. + On Windows, download `install.ps1` first, as shown above, rather than fetching it inline with `Invoke-RestMethod`: the download itself runs before the script can apply the proxy settings. These variables do not configure Docker. The Docker daemon needs its own proxy settings to pull the agent image, and the agent needs `proxy_host` and `proxy_port` in `config.json` to reach Fivetran through a proxy at runtime. See the [Proxy Agent documentation](https://fivetran.com/docs/destinations/connection-options/proxy-agent) for both. diff --git a/install.ps1 b/install.ps1 index 5e5d8aa..4bb6db8 100644 --- a/install.ps1 +++ b/install.ps1 @@ -56,12 +56,27 @@ Environment: } # Windows PowerShell 5.1 ignores HTTP_PROXY/HTTPS_PROXY/NO_PROXY, so apply them to this -# script's web requests (GitHub, the image registry, the Fivetran API). curl honors the -# same variables, so install.sh and install.ps1 behave alike behind a proxy. Docker itself -# reads its own proxy settings; see the README. +# script's web requests. curl honors the same variables, so install.sh and install.ps1 +# behave alike behind a proxy. Without them, .NET's default proxy is the user's Internet +# Options proxy, which is kept. Either way the proxy may answer NTLM/Kerberos challenges +# with the current Windows identity, which .NET doesn't do on its own. Docker itself reads +# its own proxy settings; see the README. function Set-WebProxyFromEnvironment { + param([Parameter(Mandatory)][Uri]$ProbeUri) + $proxyUrl = if ($env:HTTPS_PROXY) { $env:HTTPS_PROXY } elseif ($env:HTTP_PROXY) { $env:HTTP_PROXY } else { $null } - if (-not $proxyUrl) { return } + if (-not $proxyUrl) { + $systemProxy = [System.Net.WebRequest]::DefaultWebProxy + if ($systemProxy) { + $systemProxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials + $effectiveProxy = $systemProxy.GetProxy($ProbeUri) + # Direct connections come back as the probe URI on .NET Framework and as null on .NET Core. + if ($effectiveProxy -and $effectiveProxy -ne $ProbeUri) { + Write-Host "Using system proxy $effectiveProxy" + } + } + return + } try { $proxyUri = [Uri]$proxyUrl @@ -89,6 +104,8 @@ function Set-WebProxyFromEnvironment { $proxyUser = [Uri]::UnescapeDataString($userInfo[0]) $proxyPassword = if ($userInfo.Length -gt 1) { [Uri]::UnescapeDataString($userInfo[1]) } else { '' } $webProxy.Credentials = New-Object System.Net.NetworkCredential($proxyUser, $proxyPassword) + } else { + $webProxy.UseDefaultCredentials = $true } [System.Net.WebRequest]::DefaultWebProxy = $webProxy Write-Host "Using proxy $proxyAddress from the environment" @@ -293,7 +310,7 @@ if ($env:RUNTIME -ne 'docker') { exit 1 } -Set-WebProxyFromEnvironment +Set-WebProxyFromEnvironment -ProbeUri ([Uri]$REGISTRY_TAGS_URL) $currentPrincipal = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() if ($currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { diff --git a/proxy-agent-manager.ps1 b/proxy-agent-manager.ps1 index 9779b47..24dcdb2 100644 --- a/proxy-agent-manager.ps1 +++ b/proxy-agent-manager.ps1 @@ -71,11 +71,27 @@ function Write-Log { } # Windows PowerShell 5.1 ignores HTTP_PROXY/HTTPS_PROXY/NO_PROXY, so apply them to this -# script's web requests (the image registry lookup during upgrade). Kept in sync with -# install.ps1. Docker itself reads its own proxy settings; see the README. +# script's web requests during upgrade. curl honors the same variables, so install.sh and install.ps1 +# behave alike behind a proxy. Without them, .NET's default proxy is the user's Internet +# Options proxy, which is kept. Either way the proxy may answer NTLM/Kerberos challenges +# with the current Windows identity, which .NET doesn't do on its own. Docker itself reads +# its own proxy settings; see the README. function Set-WebProxyFromEnvironment { + param([Parameter(Mandatory)][Uri]$ProbeUri) + $proxyUrl = if ($env:HTTPS_PROXY) { $env:HTTPS_PROXY } elseif ($env:HTTP_PROXY) { $env:HTTP_PROXY } else { $null } - if (-not $proxyUrl) { return } + if (-not $proxyUrl) { + $systemProxy = [System.Net.WebRequest]::DefaultWebProxy + if ($systemProxy) { + $systemProxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials + $effectiveProxy = $systemProxy.GetProxy($ProbeUri) + # Direct connections come back as the probe URI on .NET Framework and as null on .NET Core. + if ($effectiveProxy -and $effectiveProxy -ne $ProbeUri) { + Write-Log "Using system proxy $effectiveProxy" + } + } + return + } try { $proxyUri = [Uri]$proxyUrl @@ -103,6 +119,8 @@ function Set-WebProxyFromEnvironment { $proxyUser = [Uri]::UnescapeDataString($userInfo[0]) $proxyPassword = if ($userInfo.Length -gt 1) { [Uri]::UnescapeDataString($userInfo[1]) } else { '' } $webProxy.Credentials = New-Object System.Net.NetworkCredential($proxyUser, $proxyPassword) + } else { + $webProxy.UseDefaultCredentials = $true } [System.Net.WebRequest]::DefaultWebProxy = $webProxy Write-Log "Using proxy $proxyAddress from the environment" @@ -442,7 +460,7 @@ switch ($Command) { if (-not (Start-CurrentProxyAgent)) { exit 1 } } 'upgrade' { - Set-WebProxyFromEnvironment + Set-WebProxyFromEnvironment -ProbeUri ([Uri]"https://$($IMAGE.Split('/')[0])/") Invoke-Upgrade } 'status' { diff --git a/tests/pwsh/Manager.Tests.ps1 b/tests/pwsh/Manager.Tests.ps1 index fc04729..41f2ec8 100644 --- a/tests/pwsh/Manager.Tests.ps1 +++ b/tests/pwsh/Manager.Tests.ps1 @@ -298,20 +298,34 @@ Describe 'proxy-agent-manager.ps1' { $proxy.IsBypassed([Uri]'https://notcorp.example.com/') | Should -BeFalse } - It 'falls back to HTTP_PROXY without credentials' { + It 'falls back to HTTP_PROXY and uses Windows credentials when the URL has none' { $env:HTTP_PROXY = 'http://proxy.example.com:8080' $manager = New-AgentDir $null = Invoke-Manager $manager upgrade $LASTEXITCODE | Should -Be 0 $proxy = [System.Net.WebRequest]::DefaultWebProxy $proxy.Address | Should -Be 'http://proxy.example.com:8080/' - $proxy.Credentials | Should -BeNullOrEmpty + $proxy.UseDefaultCredentials | Should -BeTrue } - It 'leaves the default proxy alone when no proxy variables are set' { + It 'keeps the system proxy, reports it and lets it use Windows credentials when no variables are set' { + # Stands in for an Internet Options proxy, which .NET exposes as the default. + $systemProxy = New-Object System.Net.WebProxy('http://system-proxy.example.com:8080') + [System.Net.WebRequest]::DefaultWebProxy = $systemProxy $manager = New-AgentDir - $null = Invoke-Manager $manager upgrade - [object]::ReferenceEquals($script:OriginalWebProxy, [System.Net.WebRequest]::DefaultWebProxy) | Should -BeTrue + $output = Invoke-Manager $manager upgrade + $LASTEXITCODE | Should -Be 0 + [object]::ReferenceEquals($systemProxy, [System.Net.WebRequest]::DefaultWebProxy) | Should -BeTrue + $systemProxy.Credentials | Should -Be ([System.Net.CredentialCache]::DefaultCredentials) + $output | Should -Match 'Using system proxy http://system-proxy.example.com:8080/' + } + + It 'reports nothing when the system proxy is a direct connection' { + [System.Net.WebRequest]::DefaultWebProxy = New-Object System.Net.WebProxy + $manager = New-AgentDir + $output = Invoke-Manager $manager upgrade + $LASTEXITCODE | Should -Be 0 + $output | Should -Not -Match 'Using (system )?proxy' } It 'rejects an invalid proxy URL before contacting the registry' {