07 July 2016

PowerShell: CMTrace Installer

This is an update to the last CMTrace installer I wrote for Windows 7. This installer has been completely rewritten that allows it to function both for installing and configuring CMTrace. I included all of the requirements within the same script for installing as an administrator and configuring CMTrace to be the default .log file viewer under the user context. Thanks to Sapien's PowerShell Studio, the script was a breeze to write!

The script can be executed to both install CMTrace and configure it under user context if the end user has local administrator privileges. This can be done by including both -SystemInstall and -UserConfig when executing the script. I have it configured in our build to execute the script with the -SystemInstall. When a user logs in for the first time, the script executes and configures CMTrace as the default viewer. The user configuration is not completed until the system is rebooted. This can be done by several methods. You could use SCCM to execute the script one time unpon user logon, which is what I have configured. It could also be setup to execute from the HKCU runonce key. I have also included error checking within the script. If any errors are encountered, the script will return an error code 1 thereby reporting it to MDT/SCCM. One more thing I found and have not been able to get around is that the user will still get the following message the first time they open a .log file:


It does not matter if they click Yes or No. CMTrace remains as the default viewer.


Here is a video of the script executing during a system install:



Here is a picture  of the script after it completes configuring a user profile:



Here are examples on how to execute the script:

  • Install CMTrace with administrator privileges during a build process or SCCM deployment
    • powershell.exe -executionpolicy bypass -file Install_CMTrace.ps1 -SystemInstall
  • Configure CMTrace as default .log file viewer via user context
    • powershell.exe -executionpolicy bypass -windowstyle hidden -file Install_CMTrace.ps1 -UserConfig
  • Specify a different installation directory to place CMTrace.exe
    • powershell.exe -executionpolicy bypass -file Install_CMTrace.ps1 -SystemInstall -InstallLocation 'C:\Temp'
  • Install CMTrace on a machine where users also have local administrator privileges
    • powershell.exe -executionpolicy bypass -file Install_CMTrace.ps1 -SystemInstall -UserConfig


You can download the script from here.



Install_CMTrace_Win10.ps1

1:  <#  
2:       .SYNOPSIS  
3:            SystemInstall CMTrace  
4:         
5:       .DESCRIPTION  
6:            This script will SystemInstall CMTrace on a system. It will make the appropriate registry changes necessary for the association of .log files with CMTrace. The script will run an initial check on the system to see if CMTrace is already installed. This is so the script can function both for installing CMTrace from an administrator perspective and associating CMTrace with .log files from a user perspective. If so, it will then make the appropriate changes to the HKCU.  
7:         
8:       .PARAMETER SystemInstall  
9:            This puts the script into SystemInstall mode thereby installing the CMTrace.exe with no user association of .log extensions to CMTrace.exe  
10:         
11:       .PARAMETER InstallLocation  
12:            Directory where to install CMTrace.exe  
13:         
14:       .PARAMETER UserConfig  
15:            This tells the script to associate .log files with CMTrace for the current logged on user  
16:         
17:       .PARAMETER PSConsoleTitle  
18:            Title of the PowerShell Console  
19:         
20:       .EXAMPLE  
21:            Install CMTrace with administrator privileges during a build process or SCCM deployment  
22:            powershell.exe -executionpolicy bypass -file Install_CMTrace.ps1 -SystemInstall  
23:              
24:            Install CMTrace on a machine where users also have local administrator privileges  
25:            powershell.exe -executionpolicy bypass -file Install_CMTrace.ps1 -SystemInstall -UserConfig  
26:              
27:            Configure CMTrace as default for user(s)  
28:            powershell.exe -executionpolicy bypass -windowstyle hidden -file Install_CMTrace.ps1 -UserConfig  
29:              
30:            Specify a different installation directory to place CMTrace.exe  
31:            powershell.exe -executionpolicy bypass -file Install_CMTrace.ps1 -SystemInstall -InstallLocation 'C:\Temp'  
32:         
33:       .NOTES  
34:            ===========================================================================  
35:            Created with:     SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.124  
36:            Created on:       7/6/2016 10:37 AM  
37:            Created by:       Mick Pletcher  
38:            Organization:  
39:            Filename:         InstallCMTrace.ps1  
40:            ===========================================================================  
41:  #>  
42:  [CmdletBinding()]  
43:  param  
44:  (  
45:            [switch]$SystemInstall,  
46:            [ValidateNotNullOrEmpty()][string]$InstallLocation = 'C:\windows\system32',  
47:            [switch]$UserConfig,  
48:            [ValidateNotNullOrEmpty()][string]$PSConsoleTitle = 'CMTrace Installation'  
49:  )  
50:    
51:  function Close-Process {  
52:  <#  
53:       .SYNOPSIS  
54:            Close Process if running  
55:         
56:       .DESCRIPTION  
57:            Check if specified process is running and close if true.  
58:         
59:       .PARAMETER ProcessName  
60:            Name of the process. Do not include the extension such as .exe.  
61:         
62:       .EXAMPLE  
63:            PS C:\> Close-Process -Process 'Value1'  
64:         
65:       .NOTES  
66:            Additional information about the function.  
67:  #>  
68:         
69:       [CmdletBinding()]  
70:       param  
71:       (  
72:                 [ValidateNotNullOrEmpty()][string]$ProcessName  
73:       )  
74:         
75:       $Process = Get-Process $ProcessName -ErrorAction SilentlyContinue  
76:       If ($Process) {  
77:            Do {  
78:                 $Count++  
79:                 Write-Host "Closing"$Process.ProcessName"....." -NoNewline  
80:                 $Process | Stop-Process -Force  
81:                 Start-Sleep -Seconds 5  
82:                 $Process = Get-Process $ProcessName -ErrorAction SilentlyContinue  
83:                 If ($Process) {  
84:                      Write-Host "Failed" -ForegroundColor Red  
85:                 } else {  
86:                      Write-Host "Success" -ForegroundColor Yellow  
87:                 }  
88:            } while (($Process) -and ($Count -lt 5))  
89:       }  
90:  }  
91:    
92:  function Set-ConsoleTitle {  
93:  <#  
94:       .SYNOPSIS  
95:            Console Title  
96:         
97:       .DESCRIPTION  
98:            Sets the title of the PowerShell Console  
99:         
100:       .PARAMETER ConsoleTitle  
101:            Title of the PowerShell Console  
102:         
103:       .NOTES  
104:            Additional information about the function.  
105:  #>  
106:         
107:       [CmdletBinding()]  
108:       param  
109:       (  
110:                 [Parameter(Mandatory = $true)][String]$ConsoleTitle  
111:       )  
112:         
113:       $host.ui.RawUI.WindowTitle = $ConsoleTitle  
114:  }  
115:    
116:  function Get-RelativePath {  
117:  <#  
118:       .SYNOPSIS  
119:            Get the relative path  
120:         
121:       .DESCRIPTION  
122:            Returns the location of the currently running PowerShell script  
123:         
124:       .NOTES  
125:            Additional information about the function.  
126:  #>  
127:         
128:       [CmdletBinding()][OutputType([string])]  
129:       param ()  
130:         
131:       $Path = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent) + "\"  
132:       Return $Path  
133:  }  
134:    
135:  function Install-CMTraceExecutable {  
136:  <#  
137:       .SYNOPSIS  
138:            Install CMTrace  
139:         
140:       .DESCRIPTION  
141:            This function will install the CMTrace to the system32 directory, thereby giving access to CMTrace to the entire operating system.  
142:         
143:       .EXAMPLE  
144:            PS C:\> Install-CMTrace  
145:         
146:       .NOTES  
147:            Additional information about the function.  
148:  #>  
149:         
150:       [CmdletBinding()]  
151:       param ()  
152:         
153:       Close-Process -ProcessName 'CMTrace'  
154:       $RelativePath = Get-RelativePath  
155:       $SourceFile = $RelativePath + 'CMTrace.exe'  
156:       Write-Host "Installing CMTrace.exe....." -NoNewline  
157:       Copy-Item -Path $SourceFile -Destination $InstallLocation -Force  
158:       If ((Test-Path $InstallLocation) -eq $true) {  
159:            Write-Host "Success" -ForegroundColor Yellow  
160:            $Success = $true  
161:       } else {  
162:            Write-Host "Failed" -ForegroundColor Red  
163:            $Success = $false  
164:       }  
165:       Return $Success  
166:  }  
167:    
168:  function Register-CMTraceToHKCR {  
169:  <#  
170:       .SYNOPSIS  
171:            Add CMTrace registry keys to the HKCR  
172:         
173:       .DESCRIPTION  
174:            This will associate CMTrace with .log files within the HKCR hive  
175:         
176:       .EXAMPLE  
177:            PS C:\> Register-CMTraceToHKCR  
178:         
179:       .NOTES  
180:            Additional information about the function.  
181:  #>  
182:         
183:       [CmdletBinding()][OutputType([boolean])]  
184:       param ()  
185:         
186:       New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT | Out-Null  
187:       $Success = $true  
188:       $MUICacheRegKey = 'HKCR:\Local Settings\Software\Microsoft\Windows\Shell\MuiCache'  
189:       $ApplicationCompany = $InstallLocation + '.ApplicationCompany'  
190:       $ApplicationCompanyValue = 'Microsoft Corporation'  
191:       $FriendlyName = $InstallLocation + '.FriendlyAppName'  
192:       $FriendlyNameValue = "CMTrace.exe"  
193:       $LogfileRegKey = "HKCR:\Logfile\Shell\Open\Command"  
194:       $TestKey = Get-ItemProperty $MUICacheRegKey -Name $ApplicationCompany -ErrorAction SilentlyContinue  
195:       Write-Host 'Register HKCR Application Company.....' -NoNewline  
196:       If ($TestKey.$ApplicationCompany -ne $ApplicationCompanyValue) {  
197:            New-ItemProperty -Path $MUICacheRegKey -Name $ApplicationCompany -Value $ApplicationCompanyValue -PropertyType String | Out-Null  
198:            $TestKey = Get-ItemProperty -Path $MUICacheRegKey -Name $ApplicationCompany -ErrorAction SilentlyContinue  
199:            If ($TestKey.$ApplicationCompany -eq $ApplicationCompanyValue) {  
200:                 Write-Host 'Success' -ForegroundColor Yellow  
201:            } else {  
202:                 Write-Host 'Failed' -ForegroundColor Red  
203:                 $Success = $false  
204:            }  
205:       } else {  
206:            Write-Host 'Already Registered' -ForegroundColor Yellow  
207:       }  
208:       Write-Host 'Register HKCR Friendly Application Name.....' -NoNewline  
209:       $TestKey = Get-ItemProperty $MUICacheRegKey -Name $FriendlyName -ErrorAction SilentlyContinue  
210:       If ($TestKey.$FriendlyName -ne $FriendlyNameValue) {  
211:            New-ItemProperty -Path $MUICacheRegKey -Name $FriendlyName -Value $FriendlyNameValue -PropertyType String -ErrorAction SilentlyContinue | Out-Null  
212:            $TestKey = Get-ItemProperty -Path $MUICacheRegKey -Name $FriendlyName -ErrorAction SilentlyContinue  
213:            If ($TestKey.$FriendlyName -eq $FriendlyNameValue) {  
214:                 Write-Host 'Success' -ForegroundColor Yellow  
215:            } else {  
216:                 Write-Host 'Failed' -ForegroundColor Red  
217:                 $Success = $false  
218:            }  
219:       } else {  
220:            Write-Host 'Already Registered' -ForegroundColor Yellow  
221:       }  
222:       If ((Test-Path $LogfileRegKey) -eq $true) {  
223:            Write-Host "Removing HKCR:\Logfile....." -NoNewline  
224:            Remove-Item -Path "HKCR:\Logfile" -Recurse -Force  
225:            If ((Test-Path "HKCR:\Logfile") -eq $false) {  
226:                 Write-Host "Success" -ForegroundColor Yellow  
227:            } else {  
228:                 Write-Host "Failed" -ForegroundColor Red  
229:            }  
230:       }  
231:       Write-Host 'Register HKCR Logfile Classes Root.....' -NoNewline  
232:       New-Item -Path $LogfileRegKey -Force | Out-Null  
233:       New-ItemProperty -Path $LogfileRegKey -Name '(Default)' -Value $InstallLocation -Force | Out-Null  
234:       $TestKey = Get-ItemProperty -Path $LogfileRegKey -Name '(Default)' -ErrorAction SilentlyContinue  
235:       If ($TestKey.'(Default)' -eq $InstallLocation) {  
236:            Write-Host 'Success' -ForegroundColor Yellow  
237:       } else {  
238:            Write-Host 'Failed' -ForegroundColor Red  
239:            $Success = $false  
240:       }  
241:       Return $Success  
242:  }  
243:    
244:  function Register-CMTraceToHKCU {  
245:  <#  
246:       .SYNOPSIS  
247:            Add CMTrace registry keys to the HKLM  
248:         
249:       .DESCRIPTION  
250:            This will associate CMTrace with .log files within the HKLM hive  
251:         
252:       .NOTES  
253:            Additional information about the function.  
254:  #>  
255:         
256:       [CmdletBinding()][OutputType([boolean])]  
257:       param ()  
258:         
259:       $Success = $true  
260:       #HKCU:\SOFTWARE\Classes\Log.file\Shell\Open\Command Key  
261:       $ClassesLogFileRegKey = "HKCU:\SOFTWARE\Classes\Log.file\Shell\Open\Command"  
262:       $ClassesLogFileRegKeyValue = [char]34 + $InstallLocation + [char]34 + [char]32 + [char]34 + "%1" + [char]34  
263:       If ((Test-Path "HKCU:\SOFTWARE\Classes\Log.file") -eq $true) {  
264:            Write-Host "Removing HKCU Log.File association....." -NoNewline  
265:            Remove-Item -Path "HKCU:\SOFTWARE\Classes\Log.file" -Recurse -Force  
266:            If ((Test-Path $ClassesLogFileRegKey) -eq $false) {  
267:                 Write-Host "Success" -ForegroundColor Yellow  
268:            } else {  
269:                 Write-Host "Failed" -ForegroundColor Red  
270:                 $Success = $false  
271:            }  
272:       }  
273:       Write-Host "Register HKCU Log.File association....." -NoNewline  
274:       New-Item -Path $ClassesLogFileRegKey -Force | Out-Null  
275:       New-ItemProperty -Path $ClassesLogFileRegKey -Name '(Default)' -Value $ClassesLogFileRegKeyValue -Force | Out-Null  
276:       $TestKey = Get-ItemProperty -Path $ClassesLogFileRegKey -Name '(Default)' -ErrorAction SilentlyContinue  
277:       If ($TestKey.'(Default)' -eq $ClassesLogFileRegKeyValue) {  
278:            Write-Host 'Success' -ForegroundColor Yellow  
279:       } else {  
280:            Write-Host 'Failed' -ForegroundColor Red  
281:            $Success = $false  
282:       }  
283:       #HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.log  
284:       $FileExtsRegKey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.log"  
285:       If ((Test-Path $FileExtsRegKey) -eq $true) {  
286:            Write-Host "Removing HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.log....." -NoNewline  
287:            Remove-Item -Path $FileExtsRegKey -Recurse -Force  
288:            If ((Test-Path $FileExtsRegKey) -eq $false) {  
289:                 Write-Host "Success" -ForegroundColor Yellow  
290:            } else {  
291:                 Write-Host "Failed" -ForegroundColor Red  
292:                 $Success = $false  
293:            }  
294:       }  
295:       Write-Host "Registering .log key....." -NoNewline  
296:       New-Item -Path $FileExtsRegKey -Force | Out-Null  
297:       If ((Test-Path $FileExtsRegKey) -eq $true) {  
298:            Write-Host "Success" -ForegroundColor Yellow  
299:       } else {  
300:            Write-Host "Failed" -ForegroundColor Red  
301:            $Success = $false  
302:       }  
303:       #HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.log\OpenWithList  
304:       $OpenWithList = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.log\OpenWithList"  
305:       Write-Host "Registering HKCU OpenWithList....." -NoNewline  
306:       New-Item -Path $OpenWithList -Force | Out-Null  
307:       New-ItemProperty -Path $OpenWithList -Name "a" -Value "CMTrace.exe" -PropertyType String -Force | Out-Null  
308:       New-ItemProperty -Path $OpenWithList -Name "b" -Value "NOTEPAD.EXE" -PropertyType String -Force | Out-Null  
309:       New-ItemProperty -Path $OpenWithList -Name "MRUList" -Value "ab" -PropertyType String -Force | Out-Null  
310:       $TestKeyA = Get-ItemProperty -Path $OpenWithList -Name 'a' -ErrorAction SilentlyContinue  
311:       $TestKeyB = Get-ItemProperty -Path $OpenWithList -Name 'b' -ErrorAction SilentlyContinue  
312:       $TestKeyMRUList = Get-ItemProperty -Path $OpenWithList -Name 'MRUList' -ErrorAction SilentlyContinue  
313:       If (($TestKeyA.a -eq "CMTrace.exe") -and ($TestKeyB.b -eq "NOTEPAD.EXE") -and ($TestKeyMRUList.MRUList -eq "ab")) {  
314:            Write-Host "Success" -ForegroundColor Yellow  
315:       } else {  
316:            Write-Host "Failed" -ForegroundColor Red  
317:            $Success = $false  
318:       }  
319:       #HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.log\OpenWithProgids  
320:       $OpenWithProgids = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.log\OpenWithProgids"  
321:       Write-Host "Registering HKCU OpenWithProgids....." -NoNewline  
322:       New-Item -Path $OpenWithProgids -Force | Out-Null  
323:       New-ItemProperty -Path $OpenWithProgids -Name "txtfile" -PropertyType Binary -Force | Out-Null  
324:       New-ItemProperty -Path $OpenWithProgids -Name "Log.File" -PropertyType Binary -Force | Out-Null  
325:       #HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.log\UserChoice  
326:       $UserChoice = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.log\UserChoice"  
327:       Write-Host "Setting CMTrace as default viewer....." -NoNewline  
328:       New-Item -Path $UserChoice -Force | Out-Null  
329:       New-ItemProperty -Path $UserChoice -Name "Progid" -Value "Applications\CMTrace.exe" -PropertyType String -Force | Out-Null  
330:       $TestKey = Get-ItemProperty -Path $UserChoice -Name "Progid"  
331:       If ($TestKey.Progid -eq "Applications\CMTrace.exe") {  
332:            Write-Host "Success" -ForegroundColor Yellow  
333:       } else {  
334:            Write-Host "Failed" -ForegroundColor Red  
335:            $Success = $false  
336:       }  
337:       Return $Success  
338:  }  
339:    
340:  function Register-CMTraceToHKLM {  
341:  <#  
342:       .SYNOPSIS  
343:            Add CMTrace registry keys to the HKLM  
344:         
345:       .DESCRIPTION  
346:            This will associate CMTrace with .log files within the HKLM hive  
347:         
348:       .NOTES  
349:            Additional information about the function.  
350:  #>  
351:         
352:       [CmdletBinding()][OutputType([boolean])]  
353:       param ()  
354:         
355:       $Success = $true  
356:       $LogFileRegKey = "HKLM:\SOFTWARE\Classes\Logfile\Shell\Open\Command"  
357:       If ((Test-Path $LogFileRegKey) -eq $true) {  
358:            Remove-Item -Path "HKLM:\SOFTWARE\Classes\Logfile" -Recurse -Force  
359:       }  
360:       Write-Host 'Register HKLM Logfile Classes Root.....' -NoNewline  
361:       New-Item -Path $LogFileRegKey -Force | Out-Null  
362:       New-ItemProperty -Path $LogFileRegKey -Name '(Default)' -Value $InstallLocation -Force | Out-Null  
363:       $TestKey = Get-ItemProperty -Path $LogFileRegKey -Name '(Default)' -ErrorAction SilentlyContinue  
364:       If ($TestKey.'(Default)' -eq $InstallLocation) {  
365:            Write-Host 'Success' -ForegroundColor Yellow  
366:       } else {  
367:            Write-Host 'Failed' -ForegroundColor Red  
368:            $Success = $false  
369:       }  
370:       Return $Success  
371:  }  
372:    
373:  function Set-CMTraceFileLocation {  
374:  <#  
375:       .SYNOPSIS  
376:            Set the CMTrace File Location  
377:         
378:       .DESCRIPTION  
379:            Set the location and filename of CMTrace.exe  
380:         
381:       .EXAMPLE  
382:            PS C:\> Set-CMTraceFileLocation  
383:         
384:       .NOTES  
385:            Additional information about the function.  
386:  #>  
387:         
388:       [CmdletBinding()][OutputType([string])]  
389:       param ()  
390:         
391:       If ($InstallLocation -notlike '*CMTrace.exe*') {  
392:            If ($InstallLocation[$InstallLocation.count - 1] -eq '\') {  
393:                 $NewLocation = $InstallLocation + 'CMTrace.exe'  
394:            } else {  
395:                 $NewLocation = $InstallLocation + '\CMTrace.exe'  
396:            }  
397:       } else {  
398:            $NewLocation = $InstallLocation  
399:       }  
400:       Return $NewLocation  
401:  }  
402:    
403:    
404:  Set-ConsoleTitle -ConsoleTitle $PSConsoleTitle  
405:  Clear-Host  
406:  $Success = $true  
407:  $InstallLocation = Set-CMTraceFileLocation  
408:  If ($SystemInstall.IsPresent) {  
409:       $Status = Install-CMTraceExecutable  
410:       If ($Status = $false) {  
411:            $Success = $false  
412:       }  
413:       $Status = Register-CMTraceToHKCR  
414:       If ($Status = $false) {  
415:            $Success = $false  
416:       }  
417:       $Status = Register-CMTraceToHKLM  
418:       If ($Status = $false) {  
419:            $Success = $false  
420:       }  
421:  }  
422:  If ($UserConfig.IsPresent) {  
423:       $Status = Register-CMTraceToHKCU  
424:       If ($Status = $false) {  
425:            $Success = $false  
426:       }  
427:  }  
428:  If ($Success -eq $false) {  
429:       Exit 1  
430:  }  
431:    

3 comments:

  1. Thanks for the script man, it seems like a bunch of code though to accomplish a task that should be done in a couple of lines. Do you have a stripped down block of this code without all of the prompting and host output?

    ReplyDelete
    Replies
    1. It should not prompt you if you pre-populate the parameters at the command line. Most of the code there was reusable code. The script goes through and verifies all processes required to install and register CMTrace are successful.

      Delete
  2. Love this... I have been baking CM Trace into WIMs, it's a must have for admins...

    ReplyDelete