12

Lots of PowerShell cmdlets (such as the AWS cmdlets) refer to the Internet Explorer proxy settings to find which proxy and bypass lists to use.

I'm trying to configure a proxy server and bypass lists to be used as a part of the userdata scripts that execute on new AWS instances.

I've tried multiple methods of getting this working but can't seem to get it working reliably.

Firstly let's set variables for the proxy server and bypass lists, and note that these addresses are different depending on which VPC the instance is in:

$proxy = "security-elb-1111111111.us-east-2.elb.amazonaws.com:3128"
$bypassList = "169.254.169.254;octopus-769734587.us-east-2.elb.amazonaws.com;s3.dualstack.us-east-1.amazonaws.com"

Secondly, netsh:

netsh winhttp set proxy $proxy bypass-list=$bypassList

This works well, but sadly lots of cmdlets don't refer to it.

Also this looked potentially useful:

& C:\windows\System32\bitsadmin.exe /Util /SetIEProxy LOCALSYSTEM Manual_proxy $proxy $bypassList

But that only sets the proxy server for the accounts LOCALSYSTEM,NETWORKSERVICE or LOCALSERVICE. I think the userdata script runs as Administrator, so this doesn't seem as useful as it first appeared.

So I tried hacking the registry, like this:

$reg = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings"
Set-ItemProperty -Path $reg -Name ProxyServer -Value $proxy
Set-ItemProperty -Path $reg -Name ProxyEnable -Value 1
Set-ItemProperty -Path $reg -Name ProxyOverride -Value $bypassList

This turned the proxy on and set up the basic details. But it looked like there was a vital piece missing as that wasn't working. Then I found

[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections]

which contained the keys "DefaultConnectionSettings" and "SavedLegacySettings". These seemed to contain hex versions of the proxy settings. So I created this:

$proxyString = ""
    for ($i = 0;$i -lt (([System.Text.Encoding]::Unicode.GetBytes($proxy)).length); $i++) {
        if ($i % 2 -eq 0) {
            $byte = (([System.Text.Encoding]::Unicode.GetBytes($proxy))[$i])
            $convertedByte=%{[System.Convert]::ToString($byte,16)}
            $proxyString = $proxystring + $convertedByte  + ","
        }
    }
    $bypassString = ""
    for ($i = 0;$i -lt (([System.Text.Encoding]::Unicode.GetBytes($bypassList)).length); $i++) {
        if ($i % 2 -eq 0) {
            $byte = (([System.Text.Encoding]::Unicode.GetBytes($bypassList))[$i])
            $convertedByte=%{[System.Convert]::ToString($byte,16)}
            $bypassString = $bypassString + $convertedByte  + ","
        }
    }

That converted the $proxy and $bypass into hexadecimal strings, which I combined into the long string to use in the registry like this:

$regString="46,00,00,00,00,00,00,00,0b,00,00,00,3c,00,00,00," + $proxystring + (%{[System.Convert]::ToString($bypassList.length,16)}) + ",00,00,00," + $bypassString +  "00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00"

This put in some (seemingly) arbitrary lines of hex (this could be where my problem lies), followed by the proxy string, followed by a hex indicator of the length of the bypass list, followed by the bypass list, followed by another seemingly arbitrary series of hex values.

It worked for instances on some networks, but not on others. The proxy address was one character shorter on the failing networks than the working ones.

But the strange thing is, if I logged onto the machine, opened up internet explorer and closed it again, suddenly the cmdlets would be able to use the proxy successfully.

So what is exactly going on when I open internet explorer? There must be registry keys that are changing which aids this process? I tried comparing the registry keys before and after but couldn't see anything.

Another thing I tried was programatically creating a PAC file to configure the settings. I did that like this:

'[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings]
"AutoConfigURL"="C:\\scripts\\proxyConfig.pac"'| out-file c:\proxyReg.reg
$proxy = "internal-sec-elb-937750220.us-east-1.elb.amazonaws.com:3128"
$bypassList = "169.254.169.254;internal-mgt-priv-adr-prod-man-lb-769734587.us-east-1.elb.amazonaws.com;" + "s3.dualstack.us-east-1.amazonaws.com"

$pacFileLocation = "c:\scripts\proxyConfig.pac"
if (!(test-path (split-path $pacFileLocation))) {New-Item -ItemType Directory -Path (Split-Path $pacFileLocation)}

$pacText = 'function FindProxyForURL(url, host) {
return "PROXY ' + $proxy + ';
DIRECT";
}
'
foreach ($localAddress in ($bypassList.Split(";"))) {
$pacText = $pacText + 'if (isPlainHostName(' + $localAddress+ '))
{
return "DIRECT";
}
'

}

$pacText = $pacText + "
if (isInNet(hostIP, '0.0.0.0', '255.0.0.0') ||
isInNet(hostIP, '10.0.0.0', '255.0.0.0') ||
isInNet(hostIP, '127.0.0.0', '255.0.0.0') ||
isInNet(hostIP, '169.254.0.0', '255.255.0.0') ||
isInNet(hostIP, '172.16.0.0', '255.240.0.0') ||
isInNet(hostIP, '192.0.2.0', '255.255.255.0') ||
isInNet(hostIP, '192.88.99.0', '255.255.255.0') ||
isInNet(hostIP, '192.168.0.0', '255.255.0.0') ||
isInNet(hostIP, '198.18.0.0', '255.254.0.0') ||
isInNet(hostIP, '224.0.0.0', '240.0.0.0') ||
isInNet(hostIP, '240.0.0.0', '240.0.0.0'))
{
return 'DIRECT';
}"


$pacText | out-file $pacFileLocation -Force

However, it seems to be the case that again, these only settings take effect when Internet Explorer is opened.

Unfortunately I can't open internet explorer because the proxy needs to work before a user logs onto the machine, and running iexplore.exe from within the script doesn't do it.

This all seems really over-complicated. I can configure the proxy within a linux server within two lines. I surely have missed the simple solution somewhere. What is it?

2 Answers 2

8

I came to the same result as Richard's (slightly shorter) whilst setting proxy for Server Core 2016. Horrific that this is necessary!

function Set-Proxy($proxy, $bypassUrls){
    $proxyBytes = [system.Text.Encoding]::ASCII.GetBytes($proxy)
    $bypassBytes = [system.Text.Encoding]::ASCII.GetBytes($bypassUrls)
    $defaultConnectionSettings = [byte[]]@(@(70,0,0,0,0,0,0,0,11,0,0,0,$proxyBytes.Length,0,0,0)+$proxyBytes+@($bypassBytes.Length,0,0,0)+$bypassBytes+ @(1..36 | % {0}))
    $registryPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings"
    Set-ItemProperty -Path $registryPath -Name ProxyServer -Value $proxy
    Set-ItemProperty -Path $registryPath -Name ProxyEnable -Value 1
    Set-ItemProperty -Path "$registryPath\Connections" -Name DefaultConnectionSettings -Value $defaultConnectionSettings
    netsh winhttp set proxy $proxy bypass-list=$bypassUrls
}
Set-Proxy "someproxy:1234" "*.example.com;<local>"
Sign up to request clarification or add additional context in comments.

3 Comments

Completely agree that this should be much easier, shouldn't it.
Thanks for the much more concise solution!
Thank you for posting this! I've been banging my head off a wall trying to get windows update to work on a system being built in a secure network segment. This does the trick.
3

I figured it out.

The key part missing was a hex byte to indicate the length of the proxy address.

So I changed the line that concatinates the hex string to this:

$regString="46,00,00,00,00,00,00,00,0b,00,00,00,"+(%{[System.Convert]::ToString($proxy.length,16)})+",00,00,00," + $proxystring + (%{[System.Convert]::ToString($bypassList.length,16)}) + ",00,00,00," + $bypassString +  "00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00"

And that works successfully.

So for a belt and braces approach: netsh

netsh winhttp set proxy $proxy bypass-list=$bypassList

Internet explorer for system accounts:

foreach ($account in "LOCALSYSTEM","NETWORKSERVICE","LOCALSERVICE") {
    & C:\windows\System32\bitsadmin.exe /Util /SetIEProxy $account Manual_proxy $proxy $bypassList
}

Internet explorer for current accounts:

$proxyString = ""
for ($i = 0;$i -lt (([System.Text.Encoding]::Unicode.GetBytes($proxy)).length); $i++) {
    if ($i % 2 -eq 0) {
        $byte = (([System.Text.Encoding]::Unicode.GetBytes($proxy))[$i])
        $convertedByte=%{[System.Convert]::ToString($byte,16)}
        $proxyString = $proxystring + $convertedByte  + ","
    }
}
$bypassString = ""
for ($i = 0;$i -lt (([System.Text.Encoding]::Unicode.GetBytes($bypassList)).length); $i++) {
    if ($i % 2 -eq 0) {
        $byte = (([System.Text.Encoding]::Unicode.GetBytes($bypassList))[$i])
        $convertedByte=%{[System.Convert]::ToString($byte,16)}
        $bypassString = $bypassString + $convertedByte  + ","
    }
}
$regString="46,00,00,00,00,00,00,00,0b,00,00,00,"+(%{[System.Convert]::ToString($proxy.length,16)})+",00,00,00," + $proxystring + (%{[System.Convert]::ToString($bypassList.length,16)}) + ",00,00,00," + $bypassString +  "00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00"
$regstringAsArray = ("0x"+$regString.replace(",",",0x")).Split(",")
$reg = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings"
Set-ItemProperty -Path $reg -Name ProxyServer -Value $proxy
Set-ItemProperty -Path $reg -Name ProxyEnable -Value 1
Set-ItemProperty -Path $reg -Name ProxyOverride -Value $bypassList
$reg = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections"
Set-ItemProperty -Path $reg -Name DefaultConnectionSettings -Type Binary -Value $regstringAsArray
Set-ItemProperty -Path $reg -Name SavedLegacySettings -Type Binary -Value $regstringAsArray

1 Comment

This is kind of madness, but it work... This is very useful for installing stuff in a container when behind a proxy.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.