PowerShell script working locally but not when pushed via SCCM

I have a script that I am pushing via SCCM. For some reason, it is not working properly. It doesn’t seem to be actually running the installations and returning the exit codes. The log file basically just says it’s doing everything, but doesn’t return an exit code for anything and isn’t behaving like it should (using passthru to show exit code and all installs appear to be failing). I am even logging the current directory of the script to make sure that is not the issue. I can run psexec with the system switch rermotely with the same computer in the same directory that SCCM pushed the files to and it will work perfectly.

Here is the script. Does anything look off that may be causing an issue when executed via SCCM? By the way, the command line to run the script via SCCM is: powershell.exe -nologo -noprofile -noninteractive -File .\FlashRemediation.ps1. It works perfectly when running the same command via psexec with the -s (system) switch. I have spent SEVERAL hours on this and not sure why it’ s not working as it should with SCCM, but is fine locally and with psexec via the system account.

$dir = Split-Path -Parent $MyInvocation.MyCommand.Path
$log = "C:\Temp\FlashInstall.txt"
$Folders = @( "C:\Windows\SysWOW64\Macromed\Flash", "C:\Windows\system32\Macromed\Flash", "C:\Users\*\Appdata\Roaming\Macromedia\Flash Player", "C:\Users\*\Appdata\Roaming\Adobe\Flash Player" )
$FlashApps = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall  |
            Get-ItemProperty |
            Where-Object {$_.DisplayName -like "Adobe Flash*" } |
            Select-Object -Property DisplayName, UninstallString

######## Stop web browser processes and uninstall Flash ###########################

If (Get-Process -Name iexplore -ErrorAction SilentlyContinue){Stop-Process -Name iexplore -Force}
If (Get-Process -Name chrome -ErrorAction SilentlyContinue){Stop-Process -Name chrome -Force}
If (Get-Process -Name firefox -ErrorAction SilentlyContinue){Stop-Process -Name firefox -Force}
If (Test-Path -path C:\Temp\FlashInstall.txt){Remove-Item C:\Temp\FlashInstall.txt -Force}

"Current directory: $dir" | Out-File $log -Force -Append

If (Test-Path "$dir\install_flash_player_20_active_x.msi"){"MSI EXISTS IN $dir" | Out-File $log -Force -Append}ELSE{"MSI DOES NOT EXIST IN $dir" | Out-File $log -Force -Append}

### REMOVE REGISTRY ENTRIES FOR FLASH THAT CAUSE ISSUES WITH UNINSTALL/INSTALL ##############
New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT | Out-Null

$Reg = Get-ChildItem -Path HKCR:\Installer\Products  |  Get-ItemProperty `
                                                     |  Where-Object {$_.ProductName -match "Adobe Flash"} `
                                                     |  Select-Object -Property ProductName,Pspath

If ($Reg) {

    Foreach ($item in $Reg) {
        "Deleting old Flash registry key for $($item.ProductName) under $($item.pspath)" | Out-File $log -Force -Append
        Remove-Item $item.pspath -Recurse -ea SilentlyContinue -Force -Confirm:$false
    }
}
Else {
"Old flash registry key(s) do not exist or have already been deleted." | Out-File $log -Force -Append
}

### UNINSTALL FLASH #################################
Try {
    "Uninstalling Flash" | Out-File $log -Force -Append
    $arg = "-uninstall -force"
    $UninstallFlash = Start-Process "$dir\uninstall_flash_player.exe" -arg $arg -WorkingDirectory $dir -Wait -ea Stop -WarningAction SilentlyContinue -PassThru -NoNewWindow
    $ExitCode = $UninstallFlash.ExitCode | Out-String
    $ExitCode = $ExitCode.Trim()
    }
Catch {
      "Error: $($_.Exception.Message)" | Out-File $log -Force -Append
      "Error Code: $ExitCode" | Out-File $log -Force -Append
}
Finally {
        "Exit Code: $ExitCode" | Out-File $log -Force -Append
}

If (Test-Path -path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Macromedia\FlashPlayer\SafeVersions) {
    "Removing Flash registry value" | Out-File $log -Append -Force
    Remove-ItemProperty -path HKLM:\SOFTWARE\Macromedia\FlashPlayer\SafeVersions -Name 20.0 -Force
}
Else {
    "SafeVersion value does not exist or has already been deleted" | Out-File $log -Append -NoClobber
}

### Delete Folders and Install ActiveX and Plugin ################################

Foreach ($Folder in $Folders) {
    Try {
        "Removing contents in $Folder" | Out-File $log -Force -Append
        Remove-Item $Folder -Force -Recurse -ea silentlycontinue
        }
    Catch {
        "Error: $($_.Exception.Message)" | Out-File $log -Force -Append
    }
}

Try {
    "Installing Flash ActiveX" | Out-File $log -Append -Force
    $InstallActiveX = start-process "$env:systemroot\system32\msiexec.exe" -arg "/I $dir\install_flash_player_20_active_x.msi /qn /norestart" -WorkingDirectory $dir -Wait -ea stop -PassThru -NoNewWindow
    $ExitCode = $InstallActiveX.ExitCode | Out-String
    $ExitCode = $ExitCode.Trim()
}
Catch {
      "Error: $($_.Exception.Message)" | Out-File $log -Force -Append
      "Error Code: $ExitCode" | Out-File $log -Force -Append
}
Finally {
        "Exit Code: $ExitCode" | Out-File $log -Force -Append
}

Try {
    "Installing Flash Plugin" | Out-File $log -Append -Force
    $InstallPlugin = start-process "$env:systemroot\system32\msiexec.exe" -arg "/I $dir\install_flash_player_20_plugin.msi /qn /norestart" -WorkingDirectory $dir -Wait -ea stop -PassThru -NoNewWindow
    $ExitCode = $InstallPlugin.ExitCode | Out-String
    $ExitCode = $ExitCode.Trim()
}
Catch {
      "Error: $($_.Exception.Message)" | Out-File $log -Force -Append
      "Error Code: $ExitCode" | Out-File $log -Force -Append
}
Finally {
        "Exit Code: $ExitCode" | Out-File $log -Force -Append
}

"Copying Flash configuration file" | Out-File $log -Append -Force
Try {
    Copy-Item -Path "$dir\mms.cfg" -Destination "C:\Windows\System32\Macromed\Flash\" -Force -ea stop
}
Catch {
      "Error: $($_.Exception.Message)" | Out-File $log -Force -Append
}

### UNINSTALL FLASH ACTIVEX IF 32 BIT MACHINE AND 32 BIT ACTIVEX FILE DOESN'T EXIST or 64 BIT MACHINE AND 64 BIT FILE ACTIVEX FILE DOESN'T EXIST ############
If (([System.IntPtr]::Size -eq 4 -and (!(test-path "C:\Windows\System32\Macromed\Flash\FlashUtil32_20_0_0_270_ActiveX.exe"))) -or `
    ([System.IntPtr]::Size -ne 4 -and (!(test-path "C:\Windows\System32\Macromed\Flash\FlashUtil64_20_0_0_270_ActiveX.exe")))) {
    
    ForEach ($app in $FlashApps) {

        If ($app.UninstallString) {
            Try {
                $uninstall = $app.UninstallString -Replace "msiexec.exe","" -Replace "/I","" -Replace "/X",""
                $uninstall = $uninstall.Trim()
                "FlashUtil32 or FlashUtil64 doesn't exist - Uninstalling $($app.DisplayName)" | Out-File $log -Force -Append
                $UninstallActiveX = start-process "msiexec.exe" -arg "/X $uninstall /qn /norestart" -Wait -PassThru -ea stop -NoNewWindow
                $ExitCode = $UninstallActiveX.ExitCode | Out-String
                $ExitCode = $ExitCode.Trim()
                }
            Catch {
                  "Error: $($_.Exception.Message)" | Out-File $log -Force -Append
                  "Error Code: $ExitCode" | Out-File $log -Force -Append
            }
            Finally {
                    "Exit Code: $ExitCode" | Out-File $log -Force -Append
            }
        }
    }

    ### REINSTALL FLASH ACTIVEX ###############################################

    Try {
        "Installing Flash ActiveX" | Out-File $log -Append -Force
        $InstallActiveX = start-process "$env:systemroot\system32\msiexec.exe" -arg "/I $dir\install_flash_player_20_active_x.msi /qn /norestart" -WorkingDirectory $dir -Wait -ea stop -PassThru -NoNewWindow
        $ExitCode = $InstallActiveX.ExitCode | Out-String
        $ExitCode = $ExitCode.Trim()
    }
    Catch {
          "Error: $($_.Exception.Message)" | Out-File $log -Force -Append
          "Error Code: $ExitCode" | Out-File $log -Force -Append
    }
    Finally {
            "Exit Code: $ExitCode" | Out-File $log -Force -Append
    }

    Try {
    "Installing Flash Plugin" | Out-File $log -Append -Force
    $InstallPlugin = start-process "$env:systemroot\system32\msiexec.exe" -arg "/I $dir\install_flash_player_20_plugin.msi /qn /norestart" -WorkingDirectory $dir -Wait -ea stop -PassThru -NoNewWindow
    $ExitCode = $InstallPlugin.ExitCode | Out-String
    $ExitCode = $ExitCode.Trim()
    }
    Catch {
          "Error: $($_.Exception.Message)" | Out-File $log -Force -Append
          "Error Code: $ExitCode" | Out-File $log -Force -Append
    }
    Finally {
            "Exit Code: $ExitCode" | Out-File $log -Force -Append
    }
    
    "Copying Flash configuration file" | Out-File $log -Append -Force
    Try {
        Copy-Item -Path "$dir\mms.cfg" -Destination "C:\Windows\System32\Macromed\Flash\" -Force -ea stop
    }
    Catch {
          "Error: $($_.Exception.Message)" | Out-File $log -Force -Append
    }
}

It’s most likely security context. SCCM isn’t necessarily running this under an account that has a user profile, or permissions to do whatever needs to be done. Many installers will fail without a profile loaded. More of an SCCM security context problem, I imagine.

From an SCCM perspective as well :

Would recommend minimizing the length of your PowerShell scripts and splitting them into separate sections in a Task Sequence. You can better control the logic there and also have separate log files.

Test your script as well (locally logged in and via SCCM) on a base build that you use, and reinitialise each time you need to adjust something.

You can also perform more post installation checking by checking the uninstall keys for each product

Would also think it might be an idea to test the script by starting the process asynchronously, and then performing subsequently a get process followed by a wait if the process still exists.

Finally, use /L*V (think that’s the format for verbose logging with msiexec), so you can check in depth how the installations have performed. Installshield based installs used to require different parameters, which i can’t recall. You can also specify the log file location. This log file contains details for each step of the msi installation attempt, so you’ll have exact details on what is or is not happening.

Also bear in mind that if the process you initialise sparks of another installation, and then exits to let the other process continue, this often won’t be trapped by an installation script. You will get an error 0 because the process you launched worked successfully, whilst the action installation exe might have failed.

Jeez, I’m so glad I don’t do packaging anymore!

I’m not sure how it would be security context. It is supposed to use the machines local system account for the installation. I will try the verbose logging with the msiexec, but what’s strange is I don’t receive a single exit code, even for the uninstall that is done locally via the uninstall string in the registry (not using msi or exe). Seems like nothing is working properly on SOME machines. Keep in mind this works fine on some machines via SCCM, but the majority are showing a blank exit code (something is not working right, not sure what it is).

I’m starting to wonder if it has something to do with mutex. I have -wait switches after everything, but I just checked a log after using verbose logging and it shows “MSI (c) (AC:70) [08:49:05:038]: Failed to grab execution mutex. System error 258.” I found the following thread, but not sure how to use it in the context of my script. Any suggestions?
http://stackoverflow.com/questions/32049193/how-to-prevent-msi-error-another-program-is-being-installed

Do you think putting the following code before every uninstallation/installation will work (as far as mutex goes)?

while($true)
{
 try
 {
   $mutex = [System.Threading.Mutex]::OpenExisting('Global\_MSIExecute')
   $mutex = $null
   Sleep 10
   'MSI running...' | Out-Default
 }
 catch [System.Threading.WaitHandleCannotBeOpenedException]
 {
   # Mutex not found; MSI isn't running
   break
 }
}

Although, I’ve realized that this may put some machines into an endless loop. I see that with one machine now. Just repeated “MSI running…”