Finding The CachedMode Setting In Outlook 2010 Using PowerShell

I had a requirement come in find the current CachedMode setting on the Outlook 2010 clients for all of the users in the domain. Seems like a simple request that can be solved with the Exchange PowerShell cmdlets, right? Wrong. What I was hoping to use to get this information is the following code:

Get-Mailbox -ResultSize Unlimited |  Get-LogonStatistics | Select Name, ClientName, ClientMode

The ClientMode property is supposed to show you whether the client is running in ExchangeServer (non-cached), or in CachedMode. Unfortunately, this wasn’t the case as every client came back with ‘ExchangeServer’, thus putting the results at risk. With this being an issue, I had to find another way to accomplish task. There was, in fact, 2 other ways that I found that I could pull this information even though it had the risk of not being quite as accurate as querying the server directly. The 2 ways are

  1. Parse all of the logs under %ExchangeInstallDir%Logging\RPC Client Access
    1. RCA_*.log
  2. Query all of the workstations in the domain for a specific registry value.
    1. HKEY_Users\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\<profile>\<stringOfCharacters>003601
      1. Enabled: 84 19 00 00
      2. Disabled: 04 16 00 00

In this case, I went with the workstation registry query because I wasn’t confident that the logs would have as many users as the workstations would. With that now decided, I had to figure out the best way to accomplish this goal.

Because this would be used to query some 4000+ systems, I decided to use runspaces again to handle several jobs at a given time to greatly speed up the query.

Because I am querying the registry remotely on the HKEY_Users key, I need something that can translate the SIDs to a friendly username that can be read.

Function ConvertSID-ToUserName {
    [cmdletbinding()]
    Param (
        [parameter()]
        [string[]]$SID
    )
    Process {
        ForEach ($S in $Sid) {
            Try {
                $s = [system.security.principal.securityidentifier]$s
                $user = $s.Translate([System.Security.Principal.NTAccount])
                New-Object PSObject -Property @{
                    Name = $user.value
                    SID = $s.value
                }                 
            } Catch {
                Write-Warning ("Unable to translate {0}.`n{1}" -f $UserName,$_.Exception.Message)
            }
        }
    }
} #End Function   

This function is simple and provides an easy way to translate the SID to username that will be helpful not only for the report generated later, but also to help determine if that registry key is owned by a currently logged on user.

I use the [microsoft.win32.registrykey] namespace along with the OpenRemoteBasekey static method to connect to the remote registry key to begin the query for CachedMode. I also use regular expressions to filter the list of users and create a hash table of SID to usernames for easier reference throughout the script.

            $rootkey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("Users",$computer)

            $SIDs = $rootkey.GetSubKeyNames() | Where {
                $_ -match "^S-1-5-\d{2}-\d{10}-\d{9}-\d{9}-\d{5}$"
            }

            #Match up the SID to a username
            $Hash = @{}
            $SIDs | ForEach {
                $Hash.$_ = (ConvertSID-ToUserName $_ | Select -Expand Name)
                $Hash[$hash.$_] = $_
            }

Now we start to iterate through the remaining collection of SIDs in the registry to find out which ones are using Outlook by checking for the Windows Messaging SubSystem key. Because a single user may have more than one Outlook profile, I also iterate through each profile to check the settings. While the ValueName is always going to be 00036601, the actual key that it resides in will not always be the same. Once I find the valuename, I can then use the GetValueNames() method to pull the value.

The value is a collection of bytes that we must use to determine if it is Enabled or Disabled. For this, I use the –BOR bitwise operator to validate if the bytes match an Enabled or Disabled setting.

$SIDs | ForEach {
    $SID = $_
    Try {
        If ($hash.$_ -eq $Username) {
            $LoggedOn = $True
        } Else {
            $LoggedOn = $False
        }                
        $Key = $rootkey.OpenSubKey("$_\Software\Microsoft\Windows NT\CurrentVersion\")
        If ($Key.GetSubKeyNames() -Contains "Windows Messaging Subsystem") {
            $User = $hash.$_
            $Profiles = $Key.OpenSubKey("Windows Messaging SubSystem\Profiles")
            Write-Verbose ("Getting list of profiles")
            $Profiles.GetSubKeyNames() | ForEach {
                $OutlookProfile = $_
                $Profile = $Profiles.OpenSubKey(("$_"))
                $profile.GetSubKeyNames() | ForEach {
                    If ($profile.OpenSubKey("$_").GetValueNames() -Contains "00036601") {
                        Write-Verbose ("Checking value for Cached Mode on profile: {0}" -f $OutlookProfile)
                        $RegKey = $_
                        $Key = $profile.OpenSubKey("$_")
                        $Value = $Key.GetValue("00036601")
                        If ((($Value[0] -bor 0x80) -eq $Value[0]) -AND (($Value[1] -bor 0x1) -eq $Value[1])) {
                            $CachedMode = 'Enabled'
                        } Else {
                            $CachedMode = 'Disabled'
                        }
                        New-Object PSObject -Property @{
                            User = $User
                            OutlookProfile = $OutlookProfile
                            CachedMode = $CachedMode
                            RegValue =  ($Value -Join ",")
                            RegKey = $RegKey
                            RegValueName = "00036601"
                            Computername = $Computer
                            isLoggedOn = $LoggedOn
                            SID = $SID
                        }
                    }
                }
            }
        }
    } Catch {
        Write-Warning ("{0}: {1}" -f $Computer,$_.Exception.Message)
    }
} #End ForEach  

As I mentioned before, since I elected to use runspaces again, I was able to run through around 4000+ systems in a couple hours. Much nicer than a sequential run through all systems. 🙂 So with that, you can easily run this against your enterprise and have the data you need. You can of course pipe this into Export-CSV if you want so you have a nice spreadsheet to view. There may have been better ways to accomplish the same goal, but I found this way to do exactly what I wanted to in a short amount of time. I am always interested in hearing about other ways that others might have used to grab this data as well.

Example

Get-ExchangeCachedMode -Computername "Computer01","Computer02"

image

 

Download

Script Repository

This entry was posted in powershell, scripts and tagged , , , . Bookmark the permalink.

8 Responses to Finding The CachedMode Setting In Outlook 2010 Using PowerShell

  1. Jose Espitia says:

    Thanks for posting! I recently created an updated script that will query the cache mode setting in Outlook 2016.
    http://joseespitia.com/2017/04/17/outlook-2016-caching-report/

    I hope this helps someone!

  2. Mark says:

    RE: issue with Unable to translate DomainName\Userid …
    Update the regular expression for the count of digits in SIDs.
    Sub authority’s may have a count of nine or ten.
    $SIDs = $rootkey.GetSubKeyNames() | Where { $_ -match “^S-1-5-\d{2}-\d{9,10}-\d{9,10}-\d{9,10}-\d{5}$” }

  3. StarDust says:

    When I run this script “unrestricted” and “unblocked” it does not return a value and goes to a new line?

  4. JZ says:

    This probably isn’t the prettiest or most efficient way to have done this but the GUID of the Key for Cached Exchagemode was different on all of our systems so I did it like this:

    function Disable-CachedExchangeMode{
    $ItemParent = “HKCU:\software\microsoft\windows nt\CurrentVersion\Windows Messaging Subsystem\Profiles\Outlook”
    $ItemProperty = “00036601”
    $Enable = ([byte[]](0x89,0x19,0x00,0x00))
    $Disable = ([byte[]](0x04,0x10,0x00,0x00))

    $FullPath = Get-ChildItem -Path $ItemParent | %{if($_.Property -eq $ItemProperty){$_.Name}}

    Set-Location “HKCU:”

    Set-ItemProperty -Path $FullPath -Name $ItemProperty -Value $Disable

    Write-Host “Cached Exchange Mode: Disabled”
    }

  5. Chris says:

    In our case the following were possible;
    (following are in dec)

    Cacheing Disabled
    0,16,0,0
    16,0,0,0
    4,16,0,0

    Caching Enabled
    0,17,0,0
    128,17,0,0
    16,1,0,0

    Caching Enabled + Download Shared Folders
    128,25,0,0
    132,25,0,0

  6. Mark says:

    Hey Boe. I keep getting the erorr:

    WARNING: Unable to translate DOMAINNAME\USERID.
    You cannot call a method on a null-valued expression.
    WARNING: COMPUTERNAME: You cannot call a method on a null-valued expression.

    Can you provide any suggestions?

    Thank you.

  7. mwkoehler says:

    This was an interesting post to find, as I was touch code to set cached mode. The troublesome part is you set different values. I have to do some digging tomorrow to see what works.

Leave a comment