27 July 2015

MSI Analysis Reporting Tool

Here is a tool I have written that generates a report on all of the variables used for each entry during an MSI installation. I wrote this script to make customizing the installation of MSI files much easier and quicker. The way this script works is that it will install the MSI file and then come back and uninstall it. It will produce a verbose text file which the script will read from to produce the report.

To use this script, just copy it to the same location as the MSI and execute it. If there is more than one MSI within the directory, you will be prompted on which one to execute. The script will then pop up a blank notepad file. This is to populate with all of your test responses during the setup. For each box you type a text entry into, copy and paste that into the notepad file making a single line entry for each text box. 

If you see a folder window, click change and then copy the directory listed. This will then report the variable associated with the folder.

For radio button boxes, you can enter part of the name of the button, or the entire name. Some radio buttons have the ampersand within the name. It is does, this will not detect it, therefor you will need to enter part of the name. 

The features will be automatically detected, so there is no need to enter those into the text file. The selected features will be read from the log file.

In the end, the report will be published on the screen and to the report.txt file. The following data will be in the report: Product Name, Product Code, MSI Filename, Properties, Features, and Buttons.

NOTE: If you are going to execute the script from a command prompt, make sure the command prompt is also residing in the same directory as the script.

Here is an example report.txt file of packaging InterAction



This was the text input file used during the installation that helped generate the above report:
You can download the script from here.


1:  <#       
2:       .NOTES  
3:       ===========================================================================  
4:        Created with:      SAPIEN Technologies, Inc., PowerShell Studio 2015 v4.2.90  
5:        Created on:       27 July 2015 9:24 AM  
6:        Created by:       Mick Pletcher  
7:        Organization:        
8:        Filename:        MSIAnalyzer.ps1  
9:       ===========================================================================  
10:       .DESCRIPTION  
11:            This script will uninstall all previous versions of SCCM Client,  
12:            including the execution of the ccmclean.exe to rid the system of  
13:            any remnants of the previous client. It will then install the new  
14:            client and wait until the client is installed and is communicating  
15:            with the SCCM server.  
16:    
17:  #>  
18:    
19:  #Declare Global Variables  
20:  Set-Variable -Name ApplicationName -Scope Global -Force  
21:  Set-Variable -Name MSI -Scope Global -Force  
22:  Set-Variable -Name ProductCode -Scope Global -Force  
23:    
24:  function Get-RelativePath {  
25:       #Declare Local Variables  
26:       Set-Variable -Name RelativePath -Scope Local -Force  
27:         
28:       $RelativePath = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent) + "\"  
29:       Return $RelativePath  
30:         
31:       #Cleanup Local Variables  
32:       Remove-Variable -Name RelativePath -Scope Local -Force  
33:  }  
34:    
35:  function Get-Architecture {  
36:       #Declare Local Variables  
37:       Set-Variable -Name Architecture -Scope Local -Force  
38:         
39:       $Architecture = Get-WmiObject -Class Win32_OperatingSystem | Select-Object OSArchitecture  
40:       $Architecture = $Global:Architecture.OSArchitecture  
41:       Return $Architecture  
42:         
43:       #Cleanup Local Variables  
44:       Remove-Variable -Name Architecture -Scope Local -Force  
45:  }  
46:    
47:  function ProcessTextFiles {  
48:       #Declare Local Variables  
49:       Set-Variable -Name RelativePath -Scope Local -Force  
50:         
51:       If ((Test-Path -Path $RelativePath"Installer.log") -eq $true) {  
52:            Remove-Item -Path $RelativePath"Installer.log" -Force  
53:       }  
54:       If ((Test-Path -Path $RelativePath"UserInput.txt") -eq $true) {  
55:            Remove-Item -Path $RelativePath"UserInput.txt" -Force  
56:       }  
57:       If ((Test-Path -Path $RelativePath"Report.txt") -eq $true) {  
58:            Remove-Item -Path $RelativePath"Report.txt" -Force  
59:       }  
60:         
61:       #Cleanup Local Variables  
62:       Remove-Variable -Name RelativePath -Scope Local -Force  
63:  }  
64:    
65:  function Get-MSIFile {  
66:       #Declare Local Variables  
67:       Set-Variable -Name FileCount -Value 1 -Scope Local -Force  
68:       Set-Variable -Name MSIFile -Scope Local -Force  
69:       Set-Variable -Name RelativePath -Scope Local -Force  
70:         
71:       $RelativePath = Get-RelativePath  
72:       $MSIFile = Get-ChildItem $RelativePath -Filter *.msi  
73:       Write-Host $MSIFile.Count  
74:       If ($MSIFile.Count -eq 1) {  
75:            Return $MSIFile  
76:       } else {  
77:            Do {  
78:                 Clear-Host  
79:                 $FileCount = 1  
80:                 Write-Host "Select MSI to process:"  
81:                 foreach ($MSI in $MSIFile) {  
82:                      Write-Host $FileCount" - "$MSI  
83:                      $FileCount++  
84:                 }  
85:                 Write-Host  
86:                 Write-Host "Selection:"  
87:                 [int]$input = Read-Host  
88:            } while (($input -eq $null) -or ($input -eq "") -or ($input -gt $MSIFile.Count) -or (!($input -as [int] -is [int])))  
89:            $input = $input - 1  
90:            $MSIFile = $MSIFile[$input]  
91:            $global:MSI = $RelativePath + $MSIFile  
92:       }  
93:                   
94:       #Cleanup Local Variables  
95:       Remove-Variable -Name FileCount -Scope Local -Force  
96:       Remove-Variable -Name MSIFile -Scope Local -Force  
97:       Remove-Variable -Name RelativePath -Scope Local -Force  
98:  }  
99:    
100:  function Get-MSIFileInfo {  
101:       param (  
102:            [parameter(Mandatory = $true)][IO.FileInfo]  
103:            $Path,  
104:            [parameter(Mandatory = $true)][ValidateSet("ProductCode", "ProductVersion", "ProductName")][string]  
105:            $Property  
106:       )  
107:         
108:       #Declare Local Variables  
109:       Set-Variable -Name MSIDatabase -Scope Local -Force  
110:       Set-Variable -Name Query -Scope Local -Force  
111:       Set-Variable -Name Record -Scope Local -Force  
112:       Set-Variable -Name Value -Scope Local -Force  
113:       Set-Variable -Name View -Scope Local -Force  
114:       Set-Variable -Name WindowsInstaller -Scope Local -Force  
115:         
116:       try {  
117:            $WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer  
118:            $MSIDatabase = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $Null, $WindowsInstaller, @($Path.FullName, 0))  
119:            $Query = "SELECT Value FROM Property WHERE Property = '$($Property)'"  
120:            $View = $MSIDatabase.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $MSIDatabase, ($Query))  
121:            $View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null)  
122:            $Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $View, $null)  
123:            $Value = $Record.GetType().InvokeMember("StringData", "GetProperty", $null, $Record, 1)  
124:            return $Value  
125:       } catch {  
126:            Write-Output $_.Exception.Message  
127:       }  
128:         
129:       #Declare Local Variables  
130:       Remove-Variable -Name MSIDatabase -Scope Local -Force  
131:       Remove-Variable -Name Query -Scope Local -Force  
132:       Remove-Variable -Name Record -Scope Local -Force  
133:       Remove-Variable -Name Value -Scope Local -Force  
134:       Remove-Variable -Name View -Scope Local -Force  
135:       Remove-Variable -Name WindowsInstaller -Scope Local -Force  
136:  }  
137:    
138:  function New-UserInputFile {  
139:       #Declare Local Variables  
140:       Set-Variable -Name ErrCode -Scope Local -Force  
141:       Set-Variable -Name RelativePath -Scope Local -Force  
142:         
143:       $RelativePath = Get-RelativePath  
144:       If ((Test-Path -Path $RelativePath"UserInput.txt") -eq $true) {  
145:            Remove-Item -Path $RelativePath"UserInput.txt" -Force  
146:       }  
147:       Write-Host "Creating UserInput.txt File....." -NoNewline  
148:       $ErrCode = New-Item -Path $RelativePath"UserInput.txt" -Type File -Force  
149:       If ((Test-Path -Path $RelativePath"UserInput.txt") -eq $true) {  
150:            Write-Host "Success" -ForegroundColor Yellow  
151:       } else {  
152:            Write-Host "Failed" -ForegroundColor Red  
153:       }  
154:         
155:       #Cleanup Local Variables  
156:       Remove-Variable -Name ErrCode -Scope Local -Force  
157:       Remove-Variable -Name RelativePath -Scope Local -Force  
158:         
159:  }  
160:    
161:  function Install-MSI {  
162:       #Declare Local Variables  
163:       Set-Variable -Name ErrCode -Scope Local -Force  
164:       Set-Variable -Name Executable -Scope Local -Force  
165:       Set-Variable -Name MSI -Scope Local -Force  
166:       Set-Variable -Name Parameters -Scope Local -Force  
167:       Set-Variable -Name RelativePath -Scope Local -Force  
168:       Set-Variable -Name Switches -Scope Local -Force  
169:         
170:       $RelativePath = Get-RelativePath  
171:       $Global:ApplicationName = Get-MSIFileInfo -Path $global:MSI -Property 'ProductName'  
172:       $Global:ProductCode = Get-MSIFileInfo -Path $global:MSI -Property 'ProductCode'  
173:       If ((Test-Path -Path $RelativePath"Installer.log") -eq $true) {  
174:            Remove-Item -Path $RelativePath"Installer.log" -Force  
175:       }  
176:       $Executable = $Env:windir + "\system32\msiexec.exe"  
177:       $Switches = "/qb- /norestart"  
178:       $Parameters = "/x" + [char]32 + $Global:ProductCode + [char]32 + $Switches  
179:       $ErrCode = (Start-Process -FilePath $Executable -ArgumentList $Parameters -Wait -Passthru).ExitCode  
180:       Write-Host "Uninstalling"$Global:ApplicationName"....." -NoNewline  
181:       if (($ErrCode -eq 0) -or ($ErrCode -eq 3010)) {  
182:            Write-Host "Success" -ForegroundColor Yellow  
183:       } else {  
184:            Write-Host "Failed with error code "$ErrCode -ForegroundColor Red  
185:       }  
186:       $Notepad = $env:windir + "\notepad.exe"  
187:       $Parameters = $RelativePath + "UserInput.txt"  
188:       $ErrCode = (Start-Process -FilePath $Notepad -ArgumentList $Parameters -PassThru).ExitCode  
189:       $Switches = "/norestart"  
190:       $Parameters = "/i " + [char]34 + $Global:MSI + [char]34 + [char]32 + $Switches + [char]32 + "/lvx " + [char]34 + $RelativePath + "Installer.log" + [char]34  
191:       Write-Host $Parameters  
192:       Write-Host "Installing"$Global:ApplicationName"....." -NoNewline  
193:       $ErrCode = (Start-Process -FilePath $Executable -ArgumentList $Parameters -Wait -Passthru).ExitCode  
194:       if (($ErrCode -eq 0) -or ($ErrCode -eq 3010)) {  
195:            Write-Host "Success" -ForegroundColor Yellow  
196:       } else {  
197:            Write-Host "Failed with error code "$ErrCode -ForegroundColor Red  
198:       }  
199:       Write-Host "Creating Log File....." -NoNewline  
200:       If ((Test-Path $RelativePath"Installer.log") -eq $true) {  
201:            Write-Host "Success" -ForegroundColor Yellow  
202:       } else {  
203:            Write-Host "Failed" -ForegroundColor Red  
204:       }  
205:         
206:       #Cleanup Local Variables  
207:       Remove-Variable -Name ErrCode -Scope Local -Force  
208:       Remove-Variable -Name Executable -Scope Local -Force  
209:       Remove-Variable -Name MSI -Scope Local -Force  
210:       Remove-Variable -Name Parameters -Scope Local -Force  
211:       Remove-Variable -Name RelativePath -Scope Local -Force  
212:       Remove-Variable -Name Switches -Scope Local -Force  
213:  }  
214:    
215:  function Uninstall-MSI {  
216:       #Declare Local Variables  
217:       Set-Variable -Name ErrCode -Scope Local -Force  
218:       Set-Variable -Name Executable -Scope Local -Force  
219:       Set-Variable -Name Line -Scope Local -Force  
220:       Set-Variable -Name LogFile -Scope Local -Force  
221:       Set-Variable -Name MSI -Scope Local -Force  
222:       Set-Variable -Name Parameters -Scope Local -Force  
223:       Set-Variable -Name Process -Scope Local -Force  
224:       Set-Variable -Name RelativePath -Scope Local -Force  
225:       Set-Variable -Name Switches -Scope Local -Force  
226:         
227:       $RelativePath = Get-RelativePath  
228:       $Process = Get-Process -Name notepad -ErrorAction SilentlyContinue  
229:       If ($Process -ne $null) {  
230:            Stop-Process -Name notepad -ErrorAction SilentlyContinue -Force  
231:       }  
232:       $Executable = $Env:windir + "\system32\msiexec.exe"  
233:       $Switches = "/qb- /norestart"  
234:       $Parameters = "/x" + [char]32 + $Global:ProductCode + [char]32 + $Switches  
235:       Write-Host "Uninstalling"$Global:ApplicationName"....." -NoNewline  
236:       $ErrCode = (Start-Process -FilePath $Executable -ArgumentList $Parameters -Wait -Passthru).ExitCode  
237:       if (($ErrCode -eq 0) -or ($ErrCode -eq 3010)) {  
238:            Write-Host "Success" -ForegroundColor Yellow  
239:       } else {  
240:            Write-Host "Failed with error code "$ErrCode -ForegroundColor Red  
241:       }  
242:         
243:       #Cleanup Local Variables  
244:       Remove-Variable -Name ErrCode -Scope Local -Force  
245:       Remove-Variable -Name Executable -Scope Local -Force  
246:       Remove-Variable -Name Line -Scope Local -Force  
247:       Remove-Variable -Name LogFile -Scope Local -Force  
248:       Remove-Variable -Name MSI -Scope Local -Force  
249:       Remove-Variable -Name Parameters -Scope Local -Force  
250:       Remove-Variable -Name Process -Scope Local -Force  
251:       Remove-Variable -Name RelativePath -Scope Local -Force  
252:       Remove-Variable -Name Switches -Scope Local -Force  
253:  }  
254:    
255:  function Get-ProductName {  
256:       #Declare Local Variables  
257:       Set-Variable -Name Database -Scope Local -Force  
258:       Set-Variable -Name MSIFileName -Scope Local -Force  
259:       Set-Variable -Name Output -Scope Local -Force  
260:       Set-Variable -Name OutputFile -Scope Local -Force  
261:       Set-Variable -Name PropertyName -Scope Local -Force  
262:       Set-Variable -Name PropertyValue -Scope Local -Force  
263:       Set-Variable -Name Record -Scope Local -Force  
264:       Set-Variable -Name RelativePath -Scope Local -Force  
265:       Set-Variable -Name View -Scope Local -Force  
266:       Set-Variable -Name WindowsInstaller -Scope Local -Force  
267:         
268:       $RelativePath = Get-RelativePath  
269:       $MSIFileName = $global:MSI  
270:       $OutputFile = $RelativePath + "Report.txt"  
271:       $Output = [char]13  
272:       Write-Host $Output  
273:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
274:       $Output = "Product Name"  
275:       Write-Host $Output  
276:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
277:       $Output = "------------"  
278:       Write-Host $Output  
279:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
280:       $WindowsInstaller = New-Object -com WindowsInstaller.Installer  
281:       $Database = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $Null, $WindowsInstaller, @($MSIFileName, 0))  
282:       $View = $Database.GetType().InvokeMember("OpenView", "InvokeMethod", $Null, $Database, ("SELECT * FROM Property"))  
283:       $View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null)  
284:       $Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $Null, $View, $Null)  
285:       while ($Record -ne $Null) {  
286:            $PropertyName = $Record.GetType().InvokeMember("StringData", "GetProperty", $Null, $Record, 1)  
287:            [string]$PropertyValue = $Record.GetType().InvokeMember("StringData", "GetProperty", $Null, $Record, 2)  
288:            IF ($PropertyName -like "*ProductName*") {  
289:                 $Output = $PropertyValue  
290:                 Write-Host $Output  
291:                 Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
292:            }  
293:            $Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $Null, $View, $Null)  
294:       }  
295:         
296:       #Cleanup Local Variables  
297:       Remove-Variable -Name Database -Scope Local -Force  
298:       Remove-Variable -Name MSIFileName -Scope Local -Force  
299:       Remove-Variable -Name Output -Scope Local -Force  
300:       Remove-Variable -Name OutputFile -Scope Local -Force  
301:       Remove-Variable -Name PropertyName -Scope Local -Force  
302:       Remove-Variable -Name PropertyValue -Scope Local -Force  
303:       Remove-Variable -Name Record -Scope Local -Force  
304:       Remove-Variable -Name RelativePath -Scope Local -Force  
305:       Remove-Variable -Name View -Scope Local -Force  
306:       Remove-Variable -Name WindowsInstaller -Scope Local -Force  
307:  }  
308:    
309:  function Get-ProductCode {  
310:       #Declare Local Variables  
311:       Set-Variable -Name Database -Scope Local -Force  
312:       Set-Variable -Name MSIFileName -Scope Local -Force  
313:       Set-Variable -Name Output -Scope Local -Force  
314:       Set-Variable -Name OutputFile -Scope Local -Force  
315:       Set-Variable -Name PropertyName -Scope Local -Force  
316:       Set-Variable -Name PropertyValue -Scope Local -Force  
317:       Set-Variable -Name Record -Scope Local -Force  
318:       Set-Variable -Name RelativePath -Scope Local -Force  
319:       Set-Variable -Name View -Scope Local -Force  
320:       Set-Variable -Name WindowsInstaller -Scope Local -Force  
321:         
322:       $RelativePath = Get-RelativePath  
323:       $MSIFileName = $global:MSI  
324:       $OutputFile = $RelativePath + "Report.txt"  
325:       $Output = [char]13  
326:       Write-Host $Output  
327:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
328:       $Output = "Product Code"  
329:       Write-Host $Output  
330:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
331:       $Output = "------------"  
332:       Write-Host $Output  
333:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
334:       $WindowsInstaller = New-Object -com WindowsInstaller.Installer  
335:       $Database = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $Null, $WindowsInstaller, @($MSIFileName, 0))  
336:       $View = $Database.GetType().InvokeMember("OpenView", "InvokeMethod", $Null, $Database, ("SELECT * FROM Property"))  
337:       $View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null)  
338:       $Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $Null, $View, $Null)  
339:       while ($Record -ne $Null) {  
340:            $PropertyName = $Record.GetType().InvokeMember("StringData", "GetProperty", $Null, $Record, 1)  
341:            [string]$PropertyValue = $Record.GetType().InvokeMember("StringData", "GetProperty", $Null, $Record, 2)  
342:            IF ($PropertyName -like "*ProductCode*") {  
343:                 $Output = $PropertyValue  
344:                 Write-Host $Output  
345:                 Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
346:            }  
347:            $Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $Null, $View, $Null)  
348:       }  
349:         
350:       #Cleanup Local Variables  
351:       Remove-Variable -Name Database -Scope Local -Force  
352:       Remove-Variable -Name MSIFileName -Scope Local -Force  
353:       Remove-Variable -Name Output -Scope Local -Force  
354:       Remove-Variable -Name OutputFile -Scope Local -Force  
355:       Remove-Variable -Name PropertyName -Scope Local -Force  
356:       Remove-Variable -Name PropertyValue -Scope Local -Force  
357:       Remove-Variable -Name Record -Scope Local -Force  
358:       Remove-Variable -Name RelativePath -Scope Local -Force  
359:       Remove-Variable -Name View -Scope Local -Force  
360:       Remove-Variable -Name WindowsInstaller -Scope Local -Force  
361:  }  
362:    
363:  function Get-MSIFileName {  
364:       #Declare Local Variables  
365:       Set-Variable -Name Output -Scope Local -Force  
366:       Set-Variable -Name OutputFile -Scope Local -Force  
367:         
368:       $OutputFile = $RelativePath + "Report.txt"  
369:       $Output = [char]13  
370:       Write-Host $Output  
371:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
372:       $Output = "MSI Filename"  
373:       Write-Host $Output  
374:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
375:       $Output = "------------"  
376:       Write-Host $Output  
377:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
378:       $Output = $global:MSI  
379:       Write-Host $Output  
380:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
381:         
382:       #Cleanup Local Variables  
383:       Remove-Variable -Name Output -Scope Local -Force  
384:       Remove-Variable -Name OutputFile -Scope Local -Force  
385:         
386:  }  
387:    
388:  function Get-Properties {  
389:       #Declare Local Variables  
390:       Set-Variable -Name Entries -Scope Local -Force  
391:       Set-Variable -Name Entry -Scope Local -Force  
392:       Set-Variable -Name File -Scope Local -Force  
393:       Set-Variable -Name FormattedEntry -Scope Local -Force  
394:       Set-Variable -Name Line -Scope Local -Force  
395:       Set-Variable -Name Output -Scope Local -Force  
396:       Set-Variable -Name OutputFile -Scope Local -Force  
397:       Set-Variable -Name Position -Scope Local -Force  
398:       Set-Variable -Name Property -Scope Local -Force  
399:       Set-Variable -Name RelativePath -Scope Local -Force  
400:       Set-Variable -Name Value -Scope Local -Force  
401:         
402:       $OutputArray = @()  
403:       $RelativePath = Get-RelativePath  
404:       $File = Get-Content -Path $RelativePath"Installer.log"  
405:       $OutputFile = $RelativePath+"Report.txt"  
406:       $Entries = Get-Content -Path $RelativePath"UserInput.txt"  
407:       $Output = [char]13  
408:       Write-Host $Output  
409:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
410:       $Output = "Properties"  
411:       Write-Host $Output  
412:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
413:       $Output = "----------"  
414:       Write-Host $Output  
415:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
416:       If ($Entries -ne $null) {  
417:            foreach ($Line in $File) {  
418:                 If ($Line -like "*PROPERTY CHANGE: Adding*") {  
419:                      foreach ($Entry in $Entries) {  
420:                           $FormattedEntry = [char]42 + [char]39 + $Entry + [char]39 + [char]42  
421:                           If ($Line -like $FormattedEntry) {  
422:                                $Property = $Line  
423:                                $Value = $Line  
424:                                $Property = $Property.split(':')[-1]  
425:                                If ($Property[0] -eq "\") {  
426:                                     $Property = $Line  
427:                                     $Property = $Property.split(':')[-2]  
428:                                }  
429:                                $Property = $Property.Trim()  
430:                                $Property = $Property.Trim("Adding")  
431:                                $Property = $Property.Trim()  
432:                                $Property = $Property.Trim(" ")  
433:                                $Position = $Property.IndexOf(" ")  
434:                                If ($Property -notlike "*:\*") {  
435:                                     $Property = $Property.Substring(0, $Position)  
436:                                }  
437:                                $Output = $Property + ": " + $Entry  
438:                                $OutputArray += $Output  
439:                           }  
440:                      }  
441:                 }  
442:            }  
443:            $OutputArray = $OutputArray | select -Unique  
444:            $OutputArray = $OutputArray | Sort  
445:            foreach ($Item in $OutputArray) {  
446:                 Write-Host $Item  
447:                 If ($Item -ne $null) {  
448:                      Out-File -FilePath $OutputFile -InputObject $Item -Append -Force  
449:                 }  
450:            }  
451:       } else {  
452:            $Output = "No User Input Properties Exist"  
453:            Write-Host $Output  
454:            Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
455:       }  
456:         
457:       #Cleanup Local Variables  
458:       Remove-Variable -Name Entries -Scope Local -Force  
459:       Remove-Variable -Name Entry -Scope Local -Force  
460:       Remove-Variable -Name File -Scope Local -Force  
461:       Remove-Variable -Name FormattedEntry -Scope Local -Force  
462:       Remove-Variable -Name Line -Scope Local -Force  
463:       Remove-Variable -Name Output -Scope Local -Force  
464:       Remove-Variable -Name OutputFile -Scope Local -Force  
465:       Remove-Variable -Name Position -Scope Local -Force  
466:       Remove-Variable -Name Property -Scope Local -Force  
467:       Remove-Variable -Name RelativePath -Scope Local -Force  
468:       Remove-Variable -Name Value -Scope Local -Force  
469:  }  
470:    
471:  function Get-Features {  
472:       #Declare Local Variables  
473:       Set-Variable -Name Entries -Scope Local -Force  
474:       Set-Variable -Name File -Scope Local -Force  
475:       Set-Variable -Name Line -Scope Local -Force  
476:       Set-Variable -Name Output -Scope Local -Force  
477:       Set-Variable -Name OutputFile -Scope Local -Force  
478:       Set-Variable -Name RelativePath -Scope Local -Force  
479:       Set-Variable -Name Value -Scope Local -Force  
480:       Set-Variable -Name Values -Scope Local -Force  
481:         
482:       $RelativePath = Get-RelativePath  
483:       $OutputFile = $RelativePath + "Report.txt"  
484:       $Entries = Get-Content -Path $RelativePath"UserInput.txt"  
485:       $Output = [char]13  
486:       Write-Host $Output  
487:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
488:       $Output = "Features"  
489:       Write-Host $Output  
490:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
491:       $Output = "--------"  
492:       Write-Host $Output  
493:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
494:       If ($Entries -ne $null) {  
495:            $File = Get-Content -Path $global:RelativePath"Installer.log"  
496:            foreach ($Line in $File) {  
497:                 If ($Line -like "*ADDLOCAL*") {  
498:                      $Value = $Line.split(' ')[-1]  
499:                      $Value = $Value -replace '''', ''  
500:                      $Value = $Value.SubString(0, $Value.Length - 1)  
501:                      $Values = $Value.Split(",")  
502:                      foreach ($Value in $Values) {  
503:                           Write-Host $Value  
504:                           Out-File -FilePath $OutputFile -InputObject $Value -Append -Force  
505:                      }  
506:                 }  
507:            }  
508:       } else {  
509:            $Output = "No User Input Features Exist"  
510:            Write-Host $Output  
511:            Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
512:       }  
513:         
514:       #Cleanup Local Variables  
515:       Remove-Variable -Name Entries -Scope Local -Force  
516:       Remove-Variable -Name File -Scope Local -Force  
517:       Remove-Variable -Name Line -Scope Local -Force  
518:       Remove-Variable -Name Output -Scope Local -Force  
519:       Remove-Variable -Name OutputFile -Scope Local -Force  
520:       Remove-Variable -Name RelativePath -Scope Local -Force  
521:       Remove-Variable -Name Value -Scope Local -Force  
522:       Remove-Variable -Name Values -Scope Local -Force  
523:  }  
524:    
525:  function Get-Buttons {  
526:       #Declare Local Variables  
527:       Set-Variable -Name Database -Scope Local -Force  
528:       Set-Variable -Name Entries -Scope Local -Force  
529:       Set-Variable -Name Entry -Scope Local -Force  
530:       Set-Variable -Name MSIFileName -Scope Local -Force  
531:       Set-Variable -Name Output -Scope Local -Force  
532:       Set-Variable -Name OutputFile -Scope Local -Force  
533:       Set-Variable -Name PropertyName -Scope Local -Force  
534:       Set-Variable -Name PropertyValue -Scope Local -Force  
535:       Set-Variable -Name Record -Scope Local -Force  
536:       Set-Variable -Name RelativePath -Scope Local -Force  
537:       Set-Variable -Name View -Scope Local -Force  
538:       Set-Variable -Name WindowsInstaller -Scope Local -Force  
539:         
540:       $RelativePath = Get-RelativePath  
541:       $MSIFileName = $global:MSI  
542:       $Entries = Get-Content -Path $RelativePath"UserInput.txt"  
543:       $OutputFile = $RelativePath + "Report.txt"  
544:       $Output = [char]13  
545:       Write-Host $Output  
546:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
547:       $Output = "Buttons"  
548:       Write-Host $Output  
549:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
550:       $Output = "--------"  
551:       Write-Host $Output  
552:       Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
553:       foreach ($Entry in $Entries) {  
554:            $WindowsInstaller = New-Object -com WindowsInstaller.Installer  
555:            $Database = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $Null, $WindowsInstaller, @($MSIFileName, 0))  
556:            $View = $Database.GetType().InvokeMember("OpenView", "InvokeMethod", $Null, $Database, ("SELECT * FROM RadioButton"))  
557:            $View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null)  
558:            $Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $Null, $View, $Null)  
559:            $Entry = "*" + $Entry + "*"  
560:            while ($Record -ne $Null) {  
561:                 $PropertyName = $Record.GetType().InvokeMember("StringData", "GetProperty", $Null, $Record, 1)  
562:                 if (-not ($PropertyName -cmatch "[a-z]")) {  
563:                      [string]$PropertyValue = $Record.GetType().InvokeMember("StringData", "GetProperty", $Null, $Record, 8)  
564:                      IF ($PropertyValue -like $Entry) {  
565:                           $Output = $PropertyName + " = " + $PropertyValue  
566:                           Write-Host $Output  
567:                           Out-File -FilePath $OutputFile -InputObject $Output -Append -Force  
568:                           #Write-Host ($PropertyName + " = " + $PropertyValue)  
569:                      }  
570:                 }  
571:                 $Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $Null, $View, $Null)  
572:            }  
573:       }  
574:         
575:       #Cleanup Local Variables  
576:       Remove-Variable -Name Database -Scope Local -Force  
577:       Remove-Variable -Name Entries -Scope Local -Force  
578:       Remove-Variable -Name Entry -Scope Local -Force  
579:       Remove-Variable -Name MSIFileName -Scope Local -Force  
580:       Remove-Variable -Name Output -Scope Local -Force  
581:       Remove-Variable -Name OutputFile -Scope Local -Force  
582:       Remove-Variable -Name PropertyName -Scope Local -Force  
583:       Remove-Variable -Name PropertyValue -Scope Local -Force  
584:       Remove-Variable -Name Record -Scope Local -Force  
585:       Remove-Variable -Name RelativePath -Scope Local -Force  
586:       Remove-Variable -Name View -Scope Local -Force  
587:       Remove-Variable -Name WindowsInstaller -Scope Local -Force  
588:  }  
589:    
590:  Clear-Host  
591:  ProcessTextFiles  
592:  Get-MSIFile  
593:  New-UserInputFile  
594:  Install-MSI  
595:  Uninstall-MSI  
596:  Get-ProductName  
597:  Get-ProductCode  
598:  Get-MSIFileName  
599:  Get-Properties  
600:  Get-Features  
601:  Get-Buttons  
602:    
603:  #Cleanup Global Variables  
604:  Remove-Variable -Name ApplicationName -Scope Global -Force  
605:  Remove-Variable -Name MSI -Scope Global -Force  
606:  Remove-Variable -Name ProductCode -Scope Global -Force  

16 July 2015

Running Programs as an Application Installation in SCCM 2012

Recently, I had to deploy an application, IntApp, with minimal intrusion and no reboots. Part of the process was to restart the application after a successful installation. The crux was that the application has to run under the user credentials. The app starts every time a system is logged into, but with laptops, that could have been a long time until the user actually rebooted the machine. This could also have been done as a package in which the executable would have been executed, but I wanted verification that when it was executed, it was actually running. To achieve this, I wrote two powershell scripts. The first one is the detection method and the second is the one that actually executes the app.

The detection method first makes sure the executable is installed. If it is, it then looks for the process to see if it is running. If it's running, a success is returned to SCCM. If not, nothing is returned back to SCCM, therefor SCCM interprets that as a failure and proceeds to execute the installer. If the application is not installed, it also returns a failure to SCCM.

The installation will test to see if the executable is present. If not, it automatically fail the install with an exit code 1. If it is installed, it will then test for the process. If the process is present, it will return a success code back to SCCM. If it is not, it will then run the executable and retest to make sure it is present in memory.

Here is the detection method:


 $DesktopExtension = $Env:ProgramFiles + "\IntApp\Desktop Extension\IntappTimeDesktopExtension.exe"  
 If ((Test-Path -Path $DesktopExtension) -eq $true) {  
      $ProcessActive = Get-Process -Name IntappTimeDesktopExtension -ErrorAction SilentlyContinue  
      If ($ProcessActive.ProcessName -eq "IntappTimeDesktopExtension") {  
           Write-Host "Success"  
           exit 0  
      } else {  
           exit 0  
      }  
 } else {  
      exit 0  
 }  


Here is the application installation script:


 $DesktopExtension = $Env:ProgramFiles + "\IntApp\Desktop Extension\IntappTimeDesktopExtension.exe"  
 If ((Test-Path -Path $DesktopExtension) -eq $true) {  
      $ProcessActive = Get-Process -Name IntappTimeDesktopExtension -ErrorAction SilentlyContinue  
      If ($ProcessActive.ProcessName -ne "IntappTimeDesktopExtension") {  
           Start-Process -FilePath $env:ProgramFiles"\IntApp\Desktop Extension\IntappTimeDesktopExtension.exe" -ErrorAction SilentlyContinue  
           Start-Sleep -Seconds 2  
      } else {  
           Write-Host "Success"  
           exit 0  
      }  
      $ProcessActive = Get-Process -Name IntappTimeDesktopExtension -ErrorAction SilentlyContinue  
      If ($ProcessActive.ProcessName -eq "IntappTimeDesktopExtension") {  
           Write-Host "Success"  
           exit 0  
      } else {  
           exit 0  
      }  
 } else {  
      exit 1  
 }