Search code examples
powershellflashwindows-installerpowershell-remotinginvoke-command

Adobe flash player powershell remote install problem


I'm attempting to develop a script with PowerShell to remotely install/update flash player for multiple machines. No matter what I do, I can't get the install to work properly at all. I'm very limited with my tools so I have to use PowerShell, and the MSI install of Flashplayer. I'll post my script below, any help at all would be greatly appreciated.

$Computers = Get-Content C:\Users\name\Desktop\flash.txt

(tried these 3 methods to install none work)
$install = @("/a","/i", "\\$Computer\c$\temp\flash\install_flash_player_32_plugin.msi", "/qn","/norestart")



Invoke-Command -ComputerName $Computer -ScriptBlock {Start-Process "Msiexec" -arg "$using:install" -Wait -PassThru} -Filepath msiexec.exe

    #This returns with "invoke-command: parameter set cannot be resolved using the specified named parameters"

Invoke-Command -ComputerName $computer -ScriptBlock {Start-Process -Filepath msiexec.exe "$using:install" -Wait -PassThru} -Filepath msiexec.exe

    #this returns the same error.

Invoke-Command -ComputerName $Computer -ScriptBlock {start-process msiexec -argumentlist @('/a','/i','"\\$Computer\c$\temp\flash\install_flash_player_32_plugin.msi"','/qn')}

    #this seemingly skips the install entirely. 

I've used similar scripts for other programs and had no problems installing them, but none of the methods I use or have researched are working properly.


Solution

  • This should do the trick, I'll explain why it wasn't working bellow:

    $Computers = Get-Content C:\Users\name\Desktop\flash.txt
    
    $params = '/i <path to AcroPro.msi> LANG_LIST=en_US TRANSFORMS="1033.mst" /qb'
    
    $Computers | % { 
        Invoke-Command -ScriptBlock {
            Param(
                [Parameter(Mandatory=$true,Position=0)]
                [String]$arguments
            )
            return Start-Process msiexec.exe -ArgumentList $arguments -Wait -PassThru
        }  -ComputerName $_ -ArgumentList $params 
    }
    

    So, it wasn't working because the ScriptBlock on Invoke-Command cant see variables that you've declared on your powershell session, think of it like you are walking to that remote computer and inputting that code by hand, you wont have the value (metaphor).

    I did a few more changes:

    1. I moved all params into 1 single string, no need to have them in array.
    2. Added $Computers | to iterate through computer names.
    3. Removed FilePath as this is meant to be used differently, documentation(Example #1).
    4. Set $MinutesToWait to whatever amount of minutes you want.
    5. No need to try to pass msiexec, as it comes with windows the default path is "C:\WINDOWS\system32\msiexec.exe"
    6. Added a return even though its never necessary, but to make it more readable and to show you intent to return the output of the msiexec process.
    7. Replaced \\$Computer\c$ with C:\ as there's no need to use a network connection if you are pointing to the host you are running the command in/

    Hope it helps, good luck.

    EDIT:

    So, as you mentioned the pipeline execution gets stuck, I had this issue in the past when creating the computer preparation script for my department, what I did was use jobs to create parallel executions of the installation so if there's a computer that for some reason is slower or is just flat out stuck and never ends you can identify it, try the following as is to see how it works and then do the replaces:

    #region ######## SetUp #######
    $bannerInProgress = @"
    
    
    #######################
    #Jobs are still running
    #######################
    "@
    $bannerDone = @"
    
    
    ##################################################
    #DONE see results of finished installations bellow
    ##################################################
    "@
    #VARS TO SET
    $MinutesToWait = 1
    $computers = 1..10 | % {"qwerty"*$_} #REPLACE THIS WITH YOUR COMPUTER VALUES (Get-Content C:\Users\name\Desktop\flash.txt)
    #endregion
    
    #region ######## Main #######
    
    #Start Jobs (REPLACE SCRIPTBLOCK OF JOB WITH YOUR INVOKE-COMMAND)
    $jobs = [System.Collections.ArrayList]::new()
    foreach($computer in $computers){
        $jobs.Add(
            (Start-Job -Name $computer -ScriptBlock {
                Param(
                    [Parameter(Mandatory=$true, Position=0)]
                    [String]$computer
                )
                Sleep -s (Get-Random -Minimum 5 -Maximum 200)
                $computer
            } -ArgumentList $computer)
        ) | Out-Null
    }
    
    $timer = [System.Diagnostics.Stopwatch]::new()
    $timer.Start()
    $acceptedWait = $MinutesToWait * 60 * 1000 # mins -> sec -> millis
    $running = $true
    do {
        cls
        $jobsRunning = $jobs | Where-Object State -EQ 'Running'
        if ($jobsRunning) {
            Write-Host $bannerInProgress
            foreach ($job in $jobsRunning) {
                Write-Host "The job `"$($job.Name)`" is still running. It started at $($job.PSBeginTime)"
            }
            Sleep -s 3
        } else {
            $running = $false
        }
    
        if($timer.ElapsedMilliseconds -ge $acceptedWait){
            $timer.Stop()
            Write-Host "Accepted time was reached, stopping all jobs still pending." -BackgroundColor Red
            $failed = @()
            foreach($job in $jobsRunning){
                $output = $job | Receive-Job
                $failed += [PsCustomObject]@{
                                "ComputerName" = $job.Name;
                                "Output" = $output;
                            }
                $job | Remove-Job -Force
                $jobs.Remove($job)
            }
            $failed | Export-Csv .\pendingInstallations.csv -NoTypeInformation -Force
            $running = $false
        }
    
    }while($running)
    
    Write-host $bannerDone
    $results = @()
    foreach($job in $jobs){
        $output = $job | Receive-Job
        $results += [PsCustomObject]@{
                        "ComputerName" = $job.Name;
                        "Output" = $output;
                    }
    }
    $results | Export-Csv .\install.csv -NoTypeInformation -Force
    #endregion
    

    This script will trigger 10 jobs that only wait and return its names, then the jobs that got completed in the time that you set are consider correct and the ones that didn't are consider as pending, both groups get exported to a CSVfor review. You will need to replace the following to work as you intended:

    1. Add $params = '/i <path to AcroPro.msi> LANG_LIST=en_US TRANSFORMS="1033.mst" /qb' in the SetUp region
    2. Replace the declaration of $computers with $computers = Get-Content C:\Users\name\Desktop\flash.txt
    3. Replace the body of Start-Job scriptblock with Invoke-command from thew first snippet of code in this answer.

    you should end-up with something like:

    .
    .code
    .
    $params = '/i <path to AcroPro.msi> LANG_LIST=en_US TRANSFORMS="1033.mst" /qb'
    #VARS TO SET
    $MinutesToWait = 1
    $computers = Get-Content C:\Users\name\Desktop\flash.txt
    #endregion
    
    #region ######## Main #######
    
    #Start Jobs
    $jobs = [System.Collections.ArrayList]::new()
    foreach($computer in $computers){
        $jobs.Add(
            (Start-Job -Name $computer -ScriptBlock {
                Param(
                    [Parameter(Mandatory=$true, Position=0)]
                    [String]$computer
                )
                Invoke-Command -ScriptBlock {
                    Param(
                        [Parameter(Mandatory=$true,Position=0)]
                        [String]$arguments
                    )
                    return Start-Process msiexec.exe -ArgumentList $arguments -Wait -PassThru
                }  -ComputerName $computer -ArgumentList $params 
            } -ArgumentList $computer)
        ) | Out-Null
    }
    .
    . code
    .
    

    I know it looks like a complete mess, but it works. Hope it helps.