I recently upgraded my workstation to Windows 8.1 and as such, am now using Internet Explorer 11. While there are some welcomed improvements, there are several changes that have made day-to-day administration activities a bit challenging. For instance, all of the Dell hardware we use has a Remote Access Controller installed that allows us to perform various remote administration tasks. Unfortunately, the current version of firmware for these DRACs is not compatible with IE 11. However, running in IE 7 compatibility mode allows the UI of the DRACs to function properly.
The problem is, we access all of these directly by private IP and adding hundreds of IP addresses to the IE compatibility view settings on multiple workstations is a bit of a pain. Thankfully, these compatibility view exceptions can be set with Group Policy, but the workstations I use are in a Workgroup and do not belong to a domain. I set out to find a way to programmatically add these exceptions using powershell.
First, it’s important to note that the registry keys that control this behavior changed in IE 11. In previous versions of IE, the setting was exclusively maintained under HKCU(HKLM)\Software\[Wow6432Node]\Policies\Microsoft\Internet Explorer\BrowserEmulation\PolicyList. Starting with IE 11, there’s an additional key under HKCU\Software\Microsoft\Internet Explorer\BrowserEmulation\ClearableListData\UserFilter that controls compatibility view settings. Unfortunately, this new key is stored in binary format and there’s not much information regarding it. I was able to find a stackoverflow post where a user attempted to decipher the data, but I found that some of the assumptions they made did not hold true. Via a process of trial-and-error, I was able to come up with a script that can set this value. However, because IE 11 still supports the previous registry key, I HIGHLY recommend using the other method described later in this post. While this seems to work, there are several values I was not able to decode.
The script will pipe the values in the $domains array into the UserFilter registry key. It accepts either top-level domains, IP addresses or Subnets in CIDR notation.
$key = "HKCU:\Software\Microsoft\Internet Explorer\BrowserEmulation\ClearableListData" $item = "UserFilter" . .\Get-IPrange.ps1 $cidr = "^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)/(3[0-2]|[1-2]?[0-9])$" [byte[]] $regbinary = @() #This seems constant [byte[]] $header = 0x41,0x1F,0x00,0x00,0x53,0x08,0xAD,0xBA #This appears to be some internal value delimeter [byte[]] $delim_a = 0x01,0x00,0x00,0x00 #This appears to separate entries [byte[]] $delim_b = 0x0C,0x00,0x00,0x00 #This is some sort of checksum, but this value seems to work [byte[]] $checksum = 0xFF,0xFF,0xFF,0xFF #This could be some sort of timestamp for each entry ending with 0x01, but setting to this value seems to work [byte[]] $filler = 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 #Examples: mydomain.com, 192.168.1.0/24 $domains = @("google.com","192.168.1.0/24") function Get-DomainEntry($domain) { [byte[]] $tmpbinary = @() [byte[]] $length = [BitConverter]::GetBytes([int16]$domain.Length) [byte[]] $data = [System.Text.Encoding]::Unicode.GetBytes($domain) $tmpbinary += $delim_b $tmpbinary += $filler $tmpbinary += $delim_a $tmpbinary += $length $tmpbinary += $data return $tmpbinary } if($domains.Length -gt 0) { [int32] $count = $domains.Length [byte[]] $entries = @() foreach($domain in $domains) { if($domain -match $cidr) { $network = $domain.Split("/")[0] $subnet = $domain.Split("/")[1] $ips = Get-IPrange -ip $network -cidr $subnet $ips | %{$entries += Get-DomainEntry $_} $count = $count - 1 + $ips.Length } else { $entries += Get-DomainEntry $domain } } $regbinary = $header $regbinary += [byte[]] [BitConverter]::GetBytes($count) $regbinary += $checksum $regbinary += $delim_a $regbinary += [byte[]] [BitConverter]::GetBytes($count) $regbinary += $entries } Set-ItemProperty -Path $key -Name $item -Value $regbinary |
You’ll need the Get-IPrange.ps1 script from the technet gallery and you can download the above script here: IE11_CV
IE 11 still supports the older registry key, therefore it is the preferred method not only because the above is a hack, but also because the data is stored in the registry as strings and it supports specific hosts instead of only top-level domains. Again, this script supports hosts, domains, IP Addresses and Subnets in CIDR notation.
$key = "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft" if(-Not (Test-Path "$key\Internet Explorer")) { New-Item -Path $key -Name "Internet Explorer" | Out-Null } if(-Not (Test-Path "$key\Internet Explorer\BrowserEmulation")) { New-Item -Path "$key\Internet Explorer" -Name "BrowserEmulation" | Out-Null } if(-Not (Test-Path "$key\Internet Explorer\BrowserEmulation\PolicyList")) { New-Item -Path "$key\Internet Explorer\BrowserEmulation" -Name "PolicyList" | Out-Null } . .\Get-IPrange.ps1 $cidr = "^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)/(3[0-2]|[1-2]?[0-9])$" #Examples: mydomain.com, 192.168.1.0/24 $domains = @("google.com","192.168.1.0/24") $regkey = "$key\Internet Explorer\BrowserEmulation\PolicyList" foreach($domain in $domains) { if($domain -match $cidr) { $network = $domain.Split("/")[0] $subnet = $domain.Split("/")[1] $ips = Get-IPrange -ip $network -cidr $subnet $ips | %{$val = New-ItemProperty -Path $regkey -Name $_ -Value $_ -PropertyType String | Out-Null} $count = $count - 1 + $ips.Length } else { New-ItemProperty -Path $regkey -Name $domain -Value $domain -PropertyType String | Out-Null } } |
Again, you’ll need the Get-IPrange.ps1 script from the technet gallery and you can download the above script here: IE_CV