Modifying IE Compatibility View Settings with Powershell

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

$domains = @("","")

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])$"

$domains = @("","")

$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

Roaming Profiles in a mixed OS environment

**UPDATE: Microsoft recently released hotfixes for Windows 8/2012 and Windows 8.1/2012 R2 to address the roaming profile compatibility issue. The hotfix and regkey is outlined in Step 1 this technet article: Note that there is no hotfix for Windows 7/2008 R2, so you’ll still need the procedure outlined below if you are trying to roam from Vista/2008 to newer versions.

Windows 8/Server 2012 (KB 2887239)
Windows 8.1/Server 2012 R2 (KB 2887595)

I love mandatory roaming profiles. With the hundreds of servers I manage daily, it’s important I have to same settings on each system to maximize efficiency. Anything I do on one system is available on all systems – for the most part. Each version of Windows comes with a slightly different user profile and they’re not always compatible. For instance, the profile in use with Windows XP/2003 was vastly different from Windows Vista/2008 which was also different from Windows 7/2008 R2. This becomes an issue with roaming profiles since the path to the profile is set on the AD user account with no regard to the actual operating system you’ll be logging into. We support systems ranging from Windows Server 2003 to Windows Server 2012 R2, so I need the ability to roam on any platform.

Microsoft addressed this in the jump from XP to Vista by automatically appending a .V2 to profile names, so you actually had different profiles for XP and Vista. However, that’s as far as they went. Every version of Windows since Vista uses that .V2 profile, but they are not fully compatible. Make a change to the profile in Windows 8 and you’ll lose your Windows Vista desktop. Similarly, modify the start menu in Windows 8.1 and you won’t see the same layout on your Windows 8 systems.

The directory services team provided a workaround by creating multiple GPO’s with WMI filters that apply only to a specific operating system thereby allowing you to set an environment variable on every system to use the in the profile path of users. To me, that was too much clutter and too many GPO’s applying to all domain servers slowing down startup and login. A better solution is a single GPO with item level targeting. Item level targeting basically allows you to apply a preference only if certain criteria are met – in this case, the Operating System version.

To accomplish this, we’ll use a GPO to set an environment variable named PROFILEVER, and then use that variable in the user profile settings in AD. We’ll use the following values for each OS:

v1 = Windows XP/2003/2003 R2
v2 = Windows Vista/2008
v3 = Windows 7/2008 R2
v3.1 = Windows 8/2012
v3.2 = Windows 8.1/2012 R2

  1. Create a new Group Policy Object.
  2. Under Computer Configuration > Preferences > Windows Settings > Environment, create a new Environment Variable named PROFILEVER.
  3. Set the Action to Update, the name to PROFILEVER, the Value to v1.
  4. On the Common tab, select the check box for Item-Level targeting and click the Targeting button.
  5. We want this to apply to multiple Operating Systems, so first add a collection.
  6. Next, select New Item > Operating System, and then select Windows XP from the Product drop-down.*
  7. Drag the Operating System object under the collection.
  8. Repeat step 6 adding entries for Windows Server 2003 and Windows Server 2003 R2.
  9. Repeat steps 2 through 8 for each profile version you wish to support using the proper value and Operating Systems.
    PROFILEVER Environment Variable
  10. As a fail-safe, I’ve added a v0 with no item-level targeting set. Because items are applied sequentially, PROFILEVER should be overwritten by one of the item-level targeted preferences.
  11. Be sure to update AD Profile path to use the new environment variable:
  12. You’ll need to logoff any existing sessions and run a gpupdate /force on systems (or reboot) for the setting to take effect.

*Note: In order to properly select Operating System versions, you need to perform this from the highest OS you wish to support. For instance, you’ll only be able to select Windows 8.1/Server 2012 R2 if you are editing the GPO on a Windows 8.1/Server 2012 R2 system.


Resolving error 0x8007007e Cannot cannect to wmi provider

Recently, I had to troubleshoot a problem with SQL backups via Microsoft Data Protection Manager 2012 SP1 for a SQL Server 2008 system. DPM was alerting us that database auto-protection failed with error code ID 32511. The detailed errors showed that DPM could not enumerate SQL Server instances using Windows Management Instrumentation on the protected computer. This error was detailed in the DPMRACurr.errlog on the production server:

WARNING Failed: Hr: = [0x8007007e] : unable to execute the WQL query: SELECT * FROM ServerSettings

This pointed to a problem with the underlying WMI configuration for SQL, so I used wbemtest.exe from the remote DPM server to test WMI connectivity. If you are unsure of exactly what WMI namespaces are in use or what queries are being run, you can use WMI Tracing to see what’s happening under the hood.

Log Name: Microsoft-Windows-WMI-Activity/Trace
Source: Microsoft-Windows-WMI-Activity
Date: 10/22/2013 3:59:39 PM
Event ID: 1
Task Category: None
Level: Information
GroupOperationId = 9283379; OperationId = 9300341; Operation = Start IWbemServices::ExecQuery – SELECT * FROM ServerSettings; ClientMachine = DPMSERVER; User = jeff; ClientProcessId = 2540; NamespaceName = \\.\root\Microsoft\SqlServer\ComputerManagement10

Once wbemtest is open, connect to the appropriate namespace:

SQL 2005

SQL 2008 & 2008 R2

SQL 2012

Once connected, try executing the WQL query that your application is using – in my case, it was SELECT * FROM ServerSettings. Doing this resulted in the error:

Number: 0x8007007e
Facility: Win32
Description: The specified module could not be found.

Some quick research shows this can most often be resolved by recompiling the WMI template for SQL with mofcomp:

On 64-bit Windows with SQL 2008, the command is:

mofcomp “C:\Program Files (x86)\Microsoft SQL Server\100\Shared\sqlmgmproviderxpsp2up.mof”

You may need to adjust the command for bitness and version of SQL and then restart the WMI service for the changes to take effect. However, this did not resolve the issue on the specific system where I was encountering the problem. The same error was returned when trying to run a query in wbemtest after recompiling and restarting the service, the DPM console also displayed the same error when attempting to enumerate SQL instances. The 0x8007007e error typically means a DLL or registration is missing. Time to break out procmon and see what’s happening under the covers. Using filters to include only the wmiprvse.exe process and excluding entries with a SUCCESS result, I could see that there was a file it seemed to be looking for, but could not find:

Procmon WMI SQL


It seemed to be scouring the path looking for sqlmgmprovider.dll and svrenumapi100.dll. I checked on disk, and sure enough, neither of those files existed under the path C:\Program Files\Microsoft SQL Server\100\Shared, however, their 32-bit counterparts were located under C:\Program Files\Microsoft SQL Server\100\Shared. Checking another  64-bit SQL 2008 server, I was able to find those files under that first path. After copying them from a known working system, the error was resolved. Also, the second file was only listed in procmon once I copied the first to the server and retested, so it make take several passes to completely resolve.

Note that this resolved this specific error for me, though it may not be the best solution. The reason those files were not on the server is because there was only a 32-bit instance of SQL Server on the system. By adding those two files and re-running wbemtest, an error was no longer returned, but the query also did not show any instances of SQL Server because it was querying for 64-bit instances.

Powershell Remoting

One of the most powerful features of Powershell is the ability to issue commands to remote systems, better known as Powershell remoting. This further lessens the need for console or remote desktop access to administer systems. Powershell remoting was introduced in v2 and relies on the Windows Remote Management service (WinRM) to issue commands to remote systems.

Enabling PowerShell remoting is fairly simple. For a single system on a domain, you can run the Enable-PSRemoting -Force cmdlet which will perform necessary configuration steps. You can also ensure WinRM is configured properly using the quickconfig command:

winrm quickconfig

You can use Group Policy if you need to enable remoting on multiple systems. Do this by automatically configuring the WinRM service:

Computer Configuration\Policies\Administrative Templates\Windows Components\Windows Remote Management (WinRM)\WinRM Service\Allow automatic configuration of listeners

Enable this GPO setting and use * for IPv4 and IPv6 filters (unless you wish to limit WinRM requests to specific source IP ranges). In a non-domain environment, you will need to add the remote system to the TrustedHosts list on the client system:

winrm s winrm/config/client ‘@{TrustedHosts=”RemoteSystem”}’

Alternatively, for non-domain environments, you can configure WinRM to allow SSL connections. You will need a “Server Authentication” certificate installed in the local computer certificate store, with a CN matching the hostname that is not expired, revoked, or self-signed. WinRM then needs to be configured for HTTPS using the certificate:

winrm quickconfig -transport:https

To check the current WinRM configuration, use the get command:

winrm get winrm/config

Once WinRM is configured properly, ensure there is a firewall rule in the local Windows Firewall allowing inbound traffic on port 5985 (and port 5986 for SSL). If WinRM is not properly configured, or there is a firewall blocking traffic, you will likely see an error similar to this:

[RemoteSystem] Connecting to remote server RemoteSystem failed with the following error message : WinRM cannot complete the operation. Verify that the specified computer name is valid, that the computer is accessible over the network, and that a firewall exception for the WinRM service is enabled and allows access from this computer. By default, the WinRM firewall exception for public profiles limits access to remote computers within the same local subnet. For more information, see the about_Remote_Troubleshooting Help topic.
+ CategoryInfo : OpenError: (RemoteSystem:String) [], PSRemotingTransportException
+ FullyQualifiedErrorId : WinRMOperationTimeout,PSSessionStateBroken

Test Powershell remoting with the Test-WSMan cmdlet:

Test-WSMan -Computer RemoteSystem

Which will output something similar to this:

wsmid :
ProtocolVersion :
ProductVendor : Microsoft Corporation
ProductVersion : OS: 0.0.0 SP: 0.0 Stack: 2.0

Now that WinRM is configured properly, we can open a remote powershell session from the client system:

Enter-PSSession -ComputerName RemoteSystem -Credential username

The credential parameter is optional on domain-joined systems but can be used to open a remote Powershell session as a specific user. If the remote system cannot authenticate the client because it is not domain joined and you have not added it to the TrustedHosts list, you will receive an error similar to this:

Enter-PSSession : Connecting to remote server RemoteSystem failed with the following error message : The WinRM client cannot process the request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting. Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be authenticated. You can get more information about that by running the following command: winrm help config. For more information, see the about_Remote_Troubleshooting Help topic.
At line:1 char:1
+ Enter-PSSession -ComputerName RemoteSystem -Credential jeff
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (RemoteSystem:String) [Enter-PSSession], PSRemotingTransportException
+ FullyQualifiedErrorId : CreateRemoteRunspaceFailed

You can also run commands on several remote systems simultaneously (this example will force a checkin with the WSUS server):

Invoke-Command -ComputerName RemoteSystem1, RemoteSystem2 -ScriptBlock {wuauclt /reportnow}

Happy Remoting!

Using CheckSUR to repair Windows file corruption

Microsoft has developed a System Update Readiness tool which can identify and repair Windows system file corruption that could prevent other updates from being installed. The tool is available for all editions of Windows since Vista/Server 2008 and is published under KB947821. The utility installs as a MSU package on older platforms, and is baked in for Windows 8 and Server 2012 with the DISM utility. This utility is often referred to as CheckSUR – short for Check System Update Readiness.

Once installed, a log is generated under %windir%\Logs\CBS\CheckSUR.log. If CheckSUR is able to automatically repair files, it will do so and report this in the log file. For any corruption which cannot be automatically repaired, we can still use this utility to manually fix these items. You will find the KB article number of files that cannot be repaired in the CheckSUR log file:

Checking System Update Readiness.
Binary Version 6.1.7601.21645
Package Version 15.0
2012-07-06 13:57

Checking Windows Servicing Packages

Checking Package Manifests and Catalogs
(f)    CBS MUM Corrupt    0x00000000    servicing\Packages\Package_2_for_KB2685939~31bf3856ad364e35~amd64~~        Expected file name Microsoft-Windows-Foundation-Package~31bf3856ad364e35~amd64~~6.1.7600.16385.mum does not match the actual file name

Checking Package Watchlist
Checking Component Watchlist
Checking Packages
Checking Component Store

Seconds executed: 109
Found 1 errors
CBS MUM Corrupt Total count: 1

Unavailable repair files:    servicing\packages\Package_2_for_KB2685939~31bf3856ad364e35~amd64~~   servicing\packages\

From this log, we can see the corrupt files are part of KB2685939. To repair, you can follow these instructions.

1. Download the appropriate update package for KB2685939 for the target system from the Microsoft Download Center.

2. Expand the package using the expand command (this assumes the package was downloaded to C:\temp and that we’re expanding to C:\temp\KB2685939):

expand C:\temp\Windows6.1-KB2685939-x64.msu /f:* C:\temp\KB2685939

3. Expand the cab files to the same directory:

expand C:\temp\ /f:* C:\temp\KB2685939

4. Copy the expanded *.mum and *.cat files to %windir%\Temp\CheckSUR\servicing\packages:

copy C:\temp\KB2685939\*.mum %windir%\Temp\CheckSUR\servicing\packages\
copy C:\temp\KB2685939\*.cat %windir%\Temp\CheckSUR\servicing\packages\

5. Re-run the System Update Readiness tool which will use the files in the %windir%\Temp\CheckSUR\servicing\packages folder to repair the corrupt or missing files.

Windows Server 2008 Firewall Block rule prevents RPC communication

Recently opened a PSS case regarding on issue we discovered with the Windows Firewall with Advanced Security on Server 2008 SP2. As a web host, we have many customer web servers with various ports open to the Internet. From time to time, nefarious users will test the server’s security. Part of the standard response is to block all access to the server from the offending IP. This is realized by creating a Windows Firewall with Advanced Security rule that blocks traffic on all ports, for all services with the remote IP scope set to the IP in question.

The problem was uncovered when we noticed backups were failing. The backup program in use leverages a dynamic RPC endpoint for communication, and with the block rule in place, the communication between the customer’s server and the backup server was failing – even though the scope of the block rule was configured to use only the attacker’s IP address. Furthermore, there was a rule specifically allowing communication from the backup server’s on the dynamic RPC endpoint.

Block Rule:

Rule Name: Block Hacker
Enabled: Yes
Direction: In
Profiles: Domain,Private,Public
LocalIP: Any
Protocol: Any
Edge traversal: No
Action: Block

Backup server rule:

Rule Name: Allow Backups
Enabled: Yes
Direction: In
Profiles: Domain,Private,Public
LocalIP: Any
Protocol: TCP
LocalPort: RPC
RemotePort: Any
Edge traversal: No
Action: Allow

According to PSS, this is a known issue with Server 2008 SP2. It was fixed in 2008 R2 but this apparently will not be fixed in Server 2008. Luckily, there is a workaround. By creating a rule with action of Secure and allowing it to override block rules and selecting the computer account of the server in question, we can ensure proper communication:

Rule Name: Fix Server 2008 firewall bug
Enabled: Yes
Direction: In
Profiles: Domain,Private,Public
LocalIP: Any
RemoteIP: Any
Protocol: TCP
LocalPort: RPC
RemotePort: Any
Edge traversal: No
InterfaceTypes: Any
RemoteComputerGroup: D:(A;;CC;;;S-1-5-21-2041841331-1236329097-1724550537-522200)
Security: Authenticate
Action: Bypass

The reason this works has to do with the order in which Windows Firewall applies rules – that process is described in detail here: This also seems to be the reason the communication is blocked – block rules are processed before allow rules and rules with broader scope before those with a more narrow scope.

Compare Two Directories with Powershell

We use DFS to keep webfarm nodes’ content in sync and ran across a problem where a directory with thousands of files and folders had one missing file on a replica. Here’s a quick script we used to find out what files were missing:

$dir1 = “\\server1\c$\folder1″
$dir2 = “\\server2\c$\folder1″
$d1 = get-childitem -path $dir1 -recurse
$d2 = get-childitem -path $dir2 -recurse
$results = @(compare-object $d1 $d2)

foreach($result in $results)


Directory: \\server1\c$\folder1\subfolder

Mode LastWriteTime Length Name
—- ————- —— —-
-a— 2/16/2012 4:08 PM 162 ~$README.txt

This will output all of the files and directories that exist in only one location.

Server-side workaround for BEAST SSL vulnerability on IIS

Recently, a vulnerability in the CBC cipher suite used in the SSL and TLS protocols was discovered that could allow an attacker to gain access to encrypted information. While the attack is not easily implemented, it will show up on compliance audits and auditors don’t like that. Fortunately, there is a server-side fix for Server 2008 and above that can be easily implemented without breaking compatibility with clients.

More information about the attack and workarounds can be found here:

The workaround is to enable TLS 1.1 and/or 1.2 on servers that support it, and prioritize cipher suites so RC4 takes precedence over CBC. Server 2008 R2 and above supports TLS 1.1 and 1.2 – you can enable those protocols by following the instructions in KB 2588513. You’ll also want to change the priority of cipher suites on all Server 2008 and above systems using group policy (either a local group policy object for a single server, or by modifying domain policy in an AD environment).

1. Open Group Policy Editor (locally, Start>Run>gpedit.msc).
2. Browse to Computer Configuration>Administrative Templates>Network>SSL Configuration Settings.
3. Modify SSL Cipher Suite Order: set it as enabled, and enter a comma delimited list of cipher suites. I recommend the following:


4. Reboot the server for the setting to take effect.

Troubleshooting Error 0xC004F042 after installing new KMS key

We recently installed KB 2757817 on our Server 2008 SP2 KMS host to support Server 2012 installations. Following the instructions, we used the slmgr script to install the new Server 2012 product key and activate the KMS host. However, after doing so, attempting to activate a Server 2012 client using slmgr /ato gave us error 0xC004F074 suggesting that it could not contact the KMS server. After ensuring that the KMS server was accessible from the client and that the DNS records were registered properly, I checked Event Viewer on the client and found this entry:

Log Name: Application
Source: Microsoft-Windows-Security-SPP
Date: 10/14/2012 11:24:07 AM
Event ID: 12288
Task Category: None
Level: Information
Keywords: Classic
User: N/A
The client has sent an activation request to the key management service machine.
0xC004F042, 0×00000000,

According to KB 938450, this error means a mismatch between the client and server KMS keys, however the KMS host was properly reporting the key as VOLUME_KMS_2012_C. I found a thread that suggested removing the key, rebooting, installing the key, activating and rebooting again to resolve a similar issue with 2008 R2. Given that this is a production system, rebooting outside of a maintenance window isn’t possible, however, that gave me the idea that the KMS service may cache the key. Sure enough, after restarting the Software Licensing Service on the KMS host, we could successfully activate Server 2012 client systems.

Must have addons for Windows 8

I recently upgraded both my desktop and MacBook Pro to Windows 8. I was a bit reluctant since I rely heavily on the desktop experience of Windows 7 for work. While the Metro UI is great for tablets, I’ve found it cumbersome to use as a desktop. I started pinning items to the taskbar with Windows 7, so using that same functionality in Windows 8 has made things a lot easier. Here are a few recommendations to make your Windows 8 system more desktop friendly:

1. Windows 7 Style start menu. My preference is Start8 from Stardock. It’s a simple install and works flawlessly. Performance is excellent and with the exception of right-clicking on Computer to get to the System applet in Control Panel, it works exactly as the Windows 7 start menu.

2. Gadgets. I was apparently one of the few users who liked and relied upon gadgets. There’s so many things vying for my attention on my desktop, Gadgets were a great way for me to watch system resources, server response times, twitter updates, and access music quickly. There’s a few solutions available over at, but my favorite is the 8Gadget Pack. It includes a bunch of the Gadgets I already used and it’s a clean install rather than dumping pre-RTM bits on a RTM system. One drawback is that it’s a Vista style sidebar instead of Windows 7 style, but nearly all of my gadgets (All CPU Meter, Drives MeterRemote Desktop Gadget, Server PingTweetz, and my own WAAF Gadget) worked without issue. I couldn’t get the in-box RSS reader to work, but that could be that I’m using Office 2013 preview.

3. Media Center. Again, I must have been one of the few users who loves this feature. While it’s not completely going away in Windows 8, it’s no longer inbox. Furthermore, it’s not available until October 26th – which makes no sense to me. Since I’ve already upgraded my laptop to Windows 8 and I rely on Media Center to connect up my OTA antenna so I can get out of market NFL games from distant stations, I needed a work around. Luckily, Tom Keating has a great blog post describing his attempts Installing Media Center in Windows 8 Pro RTM. While I don’t recommend using a pirated key, this *should* get you by until the 26th when you can revert back to your existing key. I should mention that initially, I had installed using the Windows 8 Pro VL media from my MSDN subscription and that “Add features to Windows 8″ is ONLY available in the Retail version. So I had to re-install the OS on both my laptop and desktop using the proper media to get that option to even show up.

4. Acrobat Reader. I thought having an in-box PDF reader would be an awesome addition to Windows 8 – until I found out that it’s Metro only. So that means when I need to open up invoices so I can enter them into an Excel spreadsheet, I have to keep switching between Metro and Desktop. Highly annoying and extremely unproductive. You’ll still need Adobe’s Acrobat Reader if you plan on actually using PDF’s.

Have favorite tools/tweaks for your Windows 8 installation? Post them in the comments section.