Recently, the firm I work at is going to cached exchange mode. Due to the sensitive nature of the industry I work in, we are turning on cached mode in blocks of users instead of all at once. We wanted to be able to track what machines have cached exchange mode turned on and what machines have it turned off. PowerShell and SCCM are the best method I devised.
I decided to write this PowerShell script in which it will report back to SCCM via WMI the status of cached exchange mode. It will iterate through all all HKEY_USER profiles to get the status of the exchange mailboxes and reports each to SCCM. The script will create a new WMI entry to write the status of cached exchange mode. In order for this to report to SCCM, you will need to import the WMI entry into the SCCM hardware inventory under Default Client Settings.
While writing this script, I decided to make it an option to either write the output to SCCM or to a text file for those admins that do not have SCCM. You can select a centralized location to write the text files to, which are named <ComputerName>.log with the status written inside the log.
The firm I am at skipped Office 2013, so I was not able to get the location and values of those keys. If you are wanting to use this with Office 2013, you will need to edit the Get-OfficeVersion and Find-RegistryKey functions to add those keys.
I have included examples on how to setup the script to execute at commandline within the script's .Example area. One more thing, SAPIEN's PowerShell Studio made writing this script a breeze! If it was not for PowerShell Studio, the script would not have the depth documentation it does.
This is what the report looked like in the SCCM Queries:
You will likely see some profiles saying unknown. This is caused by profiles that might have 2013 or Office 365 installed.
You can download the script from my GitHub site here.
CachedMode.ps1
I decided to write this PowerShell script in which it will report back to SCCM via WMI the status of cached exchange mode. It will iterate through all all HKEY_USER profiles to get the status of the exchange mailboxes and reports each to SCCM. The script will create a new WMI entry to write the status of cached exchange mode. In order for this to report to SCCM, you will need to import the WMI entry into the SCCM hardware inventory under Default Client Settings.
While writing this script, I decided to make it an option to either write the output to SCCM or to a text file for those admins that do not have SCCM. You can select a centralized location to write the text files to, which are named <ComputerName>.log with the status written inside the log.
The firm I am at skipped Office 2013, so I was not able to get the location and values of those keys. If you are wanting to use this with Office 2013, you will need to edit the Get-OfficeVersion and Find-RegistryKey functions to add those keys.
I have included examples on how to setup the script to execute at commandline within the script's .Example area. One more thing, SAPIEN's PowerShell Studio made writing this script a breeze! If it was not for PowerShell Studio, the script would not have the depth documentation it does.
This is what the report looked like in the SCCM Queries:
You will likely see some profiles saying unknown. This is caused by profiles that might have 2013 or Office 365 installed.
You can download the script from my GitHub site here.
CachedMode.ps1
1: <#
2: .SYNOPSIS
3: Cached Exchange Mode Report
4:
5: .DESCRIPTION
6: This script is to be executed on a machine to report if Microsoft Outlook is in cached exchange mode or not. It will report a status of on/off/unknown. The status was tracked down to registry key 00036601. The script queries under HKEY_USERS\%SID%\SOFTWARE\Microsoft\Office\16.0\Outlook\Profiles to find which key contains 00036601. The key under the above listed key is the name of the outlook profile and the key that contains 00036601 is a GUID key. Neither of those are standard across different systems, so the script has to find the actual key path. The script can then write the data to either a log file located at a centralized network path, or it can write it to the WMI so that it can be reported back to SCCM. The script was written so that it can be used in an environment that either has SCCM or does not. This script has not been written for Office 365 or Office 2013 as the firm I work at never used that version. If you need include Office 2013, you will need to add Office 15 to the Find-Registry Function
7:
8: .PARAMETER SCCM
9: Write output to WMI for reporting to SCCM
10:
11: .PARAMETER TextFile
12: Write output to a text file stored at a centralized repository
13:
14: .PARAMETER TextFileLocation
15: Location to write the text file to
16:
17: .EXAMPLE
18: Write output to WMI entry for reporting to SCCM
19: powershell.exe -file CachedMode.ps1 -SCCM
20:
21: Write output to a text file at a centralized location NOTE: -TextFileLocation can be prepopulated
22: powershell.exe -file CachedMode.ps1 -TextFile -TextFileLocation '\\mick\Systems'
23:
24: .NOTES
25: ===========================================================================
26: Created with: SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.3.131
27: Created on: 12/29/2016 12:45 PM
28: Created by: Mick Pletcher
29: Organization:
30: Filename: CachedModeReporting.ps1
31: ===========================================================================
32: #>
33: [CmdletBinding()]
34: param
35: (
36: [Switch]$SCCM,
37: [switch]$TextFile,
38: [string]$TextFileLocation
39: )
40:
41: function Find-RegistryKey {
42: <#
43: .SYNOPSIS
44: Find Registry Key Value
45:
46: .DESCRIPTION
47: Find the registry key that contains the specified value entry
48:
49: .PARAMETER Value
50: Value to search registry key for
51:
52: .PARAMETER SID
53: HKEY_USERS SID
54:
55: .EXAMPLE
56: PS C:\> Find-RegistryKey
57:
58: .NOTES
59: Additional information about the function.
60: #>
61:
62: [CmdletBinding()][OutputType([string])]
63: param
64: (
65: [ValidateNotNullOrEmpty()][string]$Value,
66: [ValidateNotNullOrEmpty()][string]$SID
67: )
68:
69: $Version = Get-OfficeVersion
70: switch ($Version) {
71: "Office 14" { $Key = "HKEY_USERS\" + $SID + "\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles" }
72: "Office 16" { $Key = "HKEY_USERS\" + $SID + "\SOFTWARE\Microsoft\Office\16.0\Outlook\Profiles" }
73: }
74: If ((Test-Path REGISTRY::$Key) -eq $true) {
75: [string]$CachedMode = get-childitem REGISTRY::$Key -recurse -ErrorAction SilentlyContinue | where-object { $_.property -eq "00036601" }
76: If ($CachedMode -ne $null) {
77: [string]$CachedModeValue = (Get-ItemProperty REGISTRY::$CachedMode).'00036601'
78: switch ($Version) {
79: "Office 14" {
80: switch ($CachedModeValue) {
81: #Values below are converted to decimal from the registry hex value commented to the right
82: '128 25 0 0' { Return "Enabled" } #'80 19 0 0'
83: '0 16 0 0' { Return "Disabled" } #'0 10 0 0'
84: default { Return "Unknown" }
85: }
86: }
87: "Office 16" {
88: switch ($CachedModeValue) {
89: #Values below are converted to decimal from the registry hex value commented to the right
90: '132 25 0 0' { Return "Enabled" } #'84 19 0 0'
91: '4 16 0 0' { Return "Disabled" } #'4 10 0 0'
92: default { Return "Unknown" }
93: }
94: }
95: }
96: Return $CachedModeValue
97: } else {
98: Return $null
99: }
100: } else {
101: Return $null
102: }
103: }
104:
105: function Get-HKEY_USERS_List {
106: <#
107: .SYNOPSIS
108: Retrieve list of HKEY_Users
109:
110: .DESCRIPTION
111: Retrieve list of HKEY_Users while excluding the built-in and administrator accounts
112:
113: .EXAMPLE
114: PS C:\> Get-HKEY_USERS_List
115:
116: .NOTES
117: Additional information about the function.
118: #>
119:
120: [CmdletBinding()][OutputType([array])]
121: param ()
122:
123: #Get list of HKEY_USERS registry keys filtering out built-in users
124: $HKEY_USERS = Get-ChildItem REGISTRY::HKEY_USERS | where-object { ($_.Name -like "*S-1-5-21*") -and ($_.Name -notlike "*_Classes") }
125: $Users = @()
126: foreach ($User in $HKEY_USERS) {
127: #Get the SID of the first profile
128: $PROFILESID = Get-ChildItem REGISTRY::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_.name -like "*" + $USER.PSChildName + "*" }
129: $SID = $PROFILESID.PSChildName
130: #Determine if cached mode is on or off
131: $CachedMode = Find-RegistryKey -Value "00036601" -SID $SID
132: If ($CachedMode -ne $null) {
133: #Get the username associated with the SID
134: $ProfileName = ((Get-ItemProperty REGISTRY::$PROFILESID).ProfileImagePath).Split("\")[2]
135: #Write username and sid to object
136: $SystemInfo = New-Object -TypeName System.Management.Automation.PSObject
137: Add-Member -InputObject $SystemInfo -MemberType NoteProperty -Name Profile -Value $ProfileName
138: Add-Member -InputObject $SystemInfo -MemberType NoteProperty -Name Status -Value $CachedMode
139: $Users += $SystemInfo
140: }
141: }
142: Return $Users
143: }
144:
145: function New-WMIClass {
146: [CmdletBinding()]
147: param
148: (
149: [ValidateNotNullOrEmpty()][string]$Class
150: )
151:
152: $WMITest = Get-WmiObject $Class -ErrorAction SilentlyContinue
153: If ($WMITest -ne "") {
154: $Output = "Deleting " + $Class + " WMI class....."
155: Remove-WmiObject $Class
156: $WMITest = Get-WmiObject $Class -ErrorAction SilentlyContinue
157: If ($WMITest -eq $null) {
158: $Output += "Success"
159: } else {
160: $Output += "Failed"
161: Exit 1
162: }
163: Write-Output $Output
164: }
165: $Output = "Creating " + $Class + " WMI class....."
166: $newClass = New-Object System.Management.ManagementClass("root\cimv2", [String]::Empty, $null);
167: $newClass["__CLASS"] = $Class;
168: $newClass.Qualifiers.Add("Static", $true)
169: $newClass.Properties.Add("Profile", [System.Management.CimType]::String, $false)
170: $newClass.Properties["Profile"].Qualifiers.Add("key", $true)
171: $newClass.Properties["Profile"].Qualifiers.Add("read", $true)
172: $newClass.Properties.Add("Status", [System.Management.CimType]::String, $false)
173: $newClass.Properties["Status"].Qualifiers.Add("key", $true)
174: $newClass.Properties["Status"].Qualifiers.Add("read", $true)
175: $newClass.Put() | Out-Null
176: $WMITest = Get-WmiObject $Class -ErrorAction SilentlyContinue
177: If ($WMITest -eq $null) {
178: $Output += "Success"
179: } else {
180: $Output += "Failed"
181: Exit 1
182: }
183: Write-Output $Output
184: }
185:
186: function New-WMIInstance {
187: <#
188: .SYNOPSIS
189: Write new instance
190:
191: .DESCRIPTION
192: Write a new instance for each profile along with its cached mode status
193:
194: .PARAMETER Username
195: Username
196:
197: .PARAMETER CachedModeStatus
198: Status of exchange cached mode
199:
200: .PARAMETER Class
201: WMI Class to write information to
202:
203: .PARAMETER MappedDrives
204: List of mapped drives
205:
206: .EXAMPLE
207: PS C:\> New-WMIInstance
208:
209: .NOTES
210: Additional information about the function.
211: #>
212:
213: [CmdletBinding()]
214: param
215: (
216: [ValidateNotNullOrEmpty()][string]$Username,
217: [ValidateNotNullOrEmpty()][string]$CachedModeStatus,
218: [ValidateNotNullOrEmpty()][string]$Class
219: )
220:
221: $Output = "Writing Cached Exchange information instance to" + [char]32 + $Class + [char]32 + "class....."
222: $Return = Set-WmiInstance -Class $Class -Arguments @{ Profile = $Username; Status = $CachedModeStatus }
223: If ($Return -like "*" + $Username + "*") {
224: $Output += "Success"
225: } else {
226: $Output += "Failed"
227: }
228: Write-Output $Output
229: }
230:
231: function Get-OfficeVersion {
232: <#
233: .SYNOPSIS
234: Get Microsoft Office Version
235:
236: .DESCRIPTION
237: Execute the OSPP.vbs to display the license information, which also contains the current version of Microsoft Office.
238:
239: .EXAMPLE
240: PS C:\> Get-OfficeVersion
241:
242: .NOTES
243: Additional information about the function.
244: #>
245:
246: [CmdletBinding()][OutputType([string])]
247: param ()
248:
249: If ((Test-Path $env:ProgramFiles"\Microsoft Office") -eq $true) {
250: $File = get-childitem -path $env:ProgramFiles"\Microsoft Office" -filter ospp.vbs -recurse
251: }
252: If ((Test-Path ${env:ProgramFiles(x86)}"\Microsoft Office") -eq $true) {
253: $File = get-childitem -path ${env:ProgramFiles(x86)}"\Microsoft Office" -filter ospp.vbs -recurse
254: }
255: #Get current version of office
256: $Version = (cscript.exe $File.Fullname /dstatus | where-object { $_ -like "LICENSE NAME:*" }).split(":")[1].Trim().Split(",")[0]
257: Return $Version
258: }
259:
260: function Initialize-HardwareInventory {
261: <#
262: .SYNOPSIS
263: Perform Hardware Inventory
264:
265: .DESCRIPTION
266: Perform a hardware inventory via the SCCM client to report the WMI entry.
267:
268: .EXAMPLE
269: PS C:\> Initialize-HardwareInventory
270:
271: .NOTES
272: Additional information about the function.
273: #>
274:
275: [CmdletBinding()]
276: param ()
277:
278: $Output = "Initiate SCCM Hardware Inventory....."
279: $SMSCli = [wmiclass] "\\localhost\root\ccm:SMS_Client"
280: $ErrCode = ($SMSCli.TriggerSchedule("{00000000-0000-0000-0000-000000000001}")).ReturnValue
281: If ($ErrCode -eq $null) {
282: $Output += "Success"
283: } else {
284: $Output += "Failed"
285: }
286: Write-Output $Output
287: }
288:
289: Clear-Host
290: #Get list of users and report if they are running Outlook in cached exchange mode
291: $Users = Get-HKEY_USERS_List
292: If ($SCCM.IsPresent) {
293: #Create new WMI Class to report status to
294: New-WMIClass -Class "Cached_Exchange_Mode"
295: #Write Cached Mode Status reports to WMI instances
296: foreach ($User in $Users) {
297: New-WMIInstance -Username $User.Profile -CachedModeStatus $User.Status -Class "Cached_Exchange_Mode"
298: }
299: Initialize-HardwareInventory
300: }
301: If ($TextFile.IsPresent) {
302: #Check if $TextFileLocation is populated
303: If (($TextFileLocation -ne "") -and ($TextFileLocation -ne $null)) {
304: #Check if $TextFileLocation exists
305: If ((Test-Path $TextFileLocation) -eq $true) {
306: #Insert backslash at the end of the $TextFileLocation and define the name of the text file
307: If ($TextFileLocation.Length - 1 -ne '\') {
308: $File = $TextFileLocation + '\' + $env:COMPUTERNAME + ".log"
309: } else {
310: $File = $TextFileLocation + $env:COMPUTERNAME + ".log"
311: }
312: #Delete the old log file if it exists
313: If ((Test-Path $File) -eq $true) {
314: Remove-Item $File -Force
315: }
316: #Write the results to the log file
317: $Users | Out-File $File -Encoding UTF8 -Force
318: } else {
319: Write-Host "Text file location does not exist"
320: }
321: } else {
322: Write-Host "No text file location was defined."
323: }
324: }
325: #Display Results to Screen
326: $Users
0 comments:
Post a Comment