Invoke-Command fails with msiexec because the remote session cannot access local file paths or interact with the interactive desktop. To fix this, you must copy the MSI installer to the remote machine using Copy-Item and execute the command locally within the remote session using Start-Process or a direct invocation, ensuring the path is valid on the target system.
Understanding the Remote Execution Context
When you useInvoke-Command in PowerShell, the code block executes in a separate process space on the remote computer. This creates a significant challenge when dealing with installation tools like msiexec.exe. The most common reason for failure is that the path to the MSI file exists only on your local machine. If you pass a UNC path like \\server\share\TortoiseVNC.msi directly into the script block without proper credential delegation, the remote session often lacks the necessary permissions to read the network share. Even if permissions are correct, some installers require interactive desktop access, which is disabled by default in remote sessions for security reasons.
Another critical factor is the execution environment. By default, Invoke-Command runs under the context of the user specified in the -Credential parameter. If that user does not have administrative privileges on the remote host, the installation will fail silently or throw an access denied error. Furthermore, msiexec might behave differently depending on whether it is called via cmd.exe or directly as an executable. Understanding these constraints is essential before attempting a workaround.
Verifying Network Access and Permissions
Before changing your script structure, verify that the remote account can actually see the source file. You can test this by running a simpleTest-Path command inside the Invoke-Command block. If this returns false, the issue is strictly related to network authentication or path resolution. For detailed troubleshooting of registry or path issues in similar contexts, you may find our guide on Registry Key PowerShell: Troubleshooting with Get-Item and Test-Path helpful.
Copying the Installer Locally
The most reliable solution is to stop trying to run the installer from a network share and instead copy the file to the remote machine first. This eliminates network latency issues and ensures the file is stored in a location where the remote session has full control. You should useCopy-Item to transfer the MSI file to a temporary directory on the remote host, such as $env:TEMP.
Here is the recommended workflow:
- Define the source path on your local machine and the destination path on the remote machine.
- Use
Copy-Itemwith the-ToSessionparameter to push the file securely. - Execute the installation command within the same
Invoke-Commandblock after the copy operation completes.
msiexec operates on a local file, which significantly reduces the chance of permission-related errors. It also allows you to clean up the file after installation by adding a removal step in the same script block.
Handling Interactive Installers
Some versions of TortoiseVNC or other MSI packages may display UI elements during installation, such as license agreements or progress bars. Remote sessions do not have a visible desktop by default, so any attempt to show a window will hang the script indefinitely. To prevent this, you must force the installer to run in silent mode. You should append the/qn flag to your msiexec arguments. This tells Windows Installer to run quietly without any user interaction. If you need to log the installation process for debugging purposes, add the /l*v C:\Temp\install.log argument. This redirects all verbose logging to a text file, allowing you to diagnose failures later without blocking the script.
If you encounter internal errors during this process, such as Win32 exceptions, refer to our article on Powershell (Win) Stopped Working: Win32 Internal Error for deeper diagnostic steps.
Using Start-Process for Better Control
While callingmsiexec directly works, using Start-Process provides better control over the execution state. You can use the -Wait parameter to ensure your script pauses until the installation finishes completely. Additionally, Start-Process allows you to capture the exit code more reliably than piping output from a direct call.
Example usage within the remote session:
Start-Process -FilePath "msiexec.exe" -ArgumentList "/i `"$env:TEMP\TortoiseVNC.msi`" /qn /norestart" -Wait -NoNewWindow
The -NoNewWindow flag helps keep the background clean, while -Wait synchronizes the PowerShell thread with the installation process. This method is particularly useful when uninstalling existing versions before installing new ones, as you need to ensure the old version is fully removed before proceeding.
Alternative Approaches for Complex Deployments
If copying files and running local commands still fails, consider using the Task Scheduler. Sometimes, specific services or drivers require a higher level of trust or different session IDs. Running a scheduled task can bypass certain session isolation issues. For insights into why scheduled tasks might fail or succeed where direct scripts do not, check out our resource on Powershell Task Scheduler Fails to Launch. Additionally, if the issue persists due to application package conflicts rather than installer mechanics, reviewing how PowerShell handles appx packages might provide clues. See our troubleshooting guide for Troubleshooting PowerShell Get-Appx -AllUsers Failure for related diagnostic patterns.Frequently Asked Questions
Why does Invoke-Command hang when installing software?
It usually hangs because the installer is waiting for user input, such as clicking "Next" on a license agreement. Since remote sessions lack an interactive desktop, the prompt never gets answered. Always use silent flags like/qn to prevent this behavior.
Can I use UNC paths directly in Invoke-Command?
Technically yes, but it requires Kerberos double-hop authentication. If your credentials are not delegated properly, the remote server cannot authenticate to the file share. Copying the file locally avoids this complex authentication chain entirely.How do I verify if the MSI was installed successfully?
Check the exit code returned byStart-Process or the last line of the log file if you enabled verbose logging. An exit code of 0 indicates success, while non-zero values correspond to specific Windows Installer error codes.
What if the MSI file is too large to copy quickly?
Large files can cause timeouts inCopy-Item. You can increase the timeout duration using the -TimeoutSec parameter or compress the MSI before transfer and decompress it on the remote side. Ensure your network bandwidth can handle the payload during business hours.
Does msiexec work with Invoke-Command in PowerShell Core?
PowerShell Core (CoreCLR) has limited support for Windows-specific modules likeDism or some msiexec interactions. For best compatibility with legacy Windows installers, use Windows PowerShell 5.1 on Windows clients, or ensure you are using the latest compatible modules.