Powershell Invoke-Command works when run manually, but not when in a script

powershell

I am building a script to remotely install the PSWindowsUpdate PowerShell module, open the correct firewall ports, and then run a command to install waiting updates. In order to install the PSWindowsUpdate module, I need to install an MSI on some of my machines to enable the "Install-Module" commandlet.

If I run the following code manually, in an admin PowerShell session on my local machine, it works fine:

$RemoteMachine = "DCSvrRDS16"
write-host "Server Name is: $RemoteMachine"

#Copy the MSI Local to the computer
Write-Host "Copying MSI locally"
Copy "\\dcsvrstorage2\software\microsoft\powershell\PackageManagement_x64.msi" \\$RemoteMachine\c$\

#Run the MSI remotely
Write-Host "Running MSI"
Invoke-Command -ComputerName $RemoteMachine -Credential $domaincredentials -ScriptBlock { c:\windows\system32\msiexec /i c:\PackageManagement_x64.msi /quiet /lvx* c:\PackageManagement.log } 4>&1

But if I run THIS full script IN THE EXACT SAME ALREADY OPEN PowerShell SESSION, the MSI does not install and the log file is not created:

Import-Module PSWindowsUpdate
$domaincredentials = Get-Credential


Function ProcessMachine($RemoteMachine) {

    write-host "Server Name is: $RemoteMachine"

    #Copy the MSI Local to the computer
    Write-Host "Copying MSI locally"
    Copy "\\dcsvrstorage2\software\microsoft\powershell\PackageManagement_x64.msi" \\$RemoteMachine\c$\

    #Run the MSI remotely
    Write-Host "Running MSI"
    Invoke-Command -ComputerName $RemoteMachine -Credential $domaincredentials -ScriptBlock { c:\windows\system32\msiexec /i c:\PackageManagement_x64.msi /quiet /lvx* c:\PackageManagement.log } 4>&1

    #Install the module
    Invoke-Command -ComputerName $RemoteMachine -Credential $domaincredentials -ScriptBlock { Install-Module PSWindowsUpdate -Force }

    #Turn on the firewall rules
    Invoke-Command -ComputerName $RemoteMachine -Credential $domaincredentials -ScriptBlock { Set-NetFirewallRule -DisplayName "COM+ Network Access (DCOM-In)" -Enabled True }
    Invoke-Command -ComputerName $RemoteMachine -Credential $domaincredentials -ScriptBlock { Set-NetFirewallRule -DisplayName "COM+ Remote Administration (DCOM-In)" -Enabled True }
    Invoke-Command -ComputerName $RemoteMachine -Credential $domaincredentials -ScriptBlock { Remove-NetFirewallRule -DisplayName "Remote WSUS Install" }
    Invoke-Command -ComputerName $RemoteMachine -Credential $domaincredentials -ScriptBlock { New-NetFirewallRule -DisplayName "Remote WSUS Install" -Profile Domain -Enabled True -Direction Inbound -Action Allow -Protocol TCP -LocalPort RPC }

    #Try the update
    Install-WindowsUpdate -ComputerName $RemoteMachine -AcceptAll
}

foreach ($machine in (Get-Content NeedsWSUS.txt)) {

    ProcessMachine $machine

}

It is driving me crazy. I have run ProcessMonitor on the remote machine, and I can see msiexec start and then stop without ever accessing the MSI in the root of the C: drive or trying to open the log file for write.

Has anyone seen anything similar? Client machine is Windows 7 pro, Powershell 4, and the destination machine is Windows 2012 R2 and also PowerShell 4.

Thanks in advance.

Best Answer

This issue can occur when attempting to run script blocks or commands that take some time to execute (especially with installation activities). The script is returning before the command in the script block has completed it's activity. Using Start-Process with the -wait parameter has the script wait until the command being run has completed and returned back to the script. I've noticed this before when installing on remote machines whereas running locally without Start-Process appeared to work fine.

The proper command change as you have noted is:

Invoke-Command -ComputerName $RemoteMachine -Credential $domaincredentials -ScriptBlock { Start-Process msiexec "/i c:\PackageManagement_x64.msi /quiet /lvx* c:\PackageManagement.log" -wait }

Related Topic