Installing an MSI file on a remote machine with PowerShell - powershell

I'm working on a utility to automate some processes and one task is to install a .msi file on a remote machine. The file is found in C:\Users\username on the remote machine and for simplicity's sake, the filename is file.msi. The command I'm using is:
Invoke-Command -ComputerName $remoteMachine -ScriptBlock{cmd /c start /wait msiexec /i $installPath /quiet}
When I execute this on my local dev machine, it doesn't show any errors, but doesn't install the file.
However, when I copy the exact command inside the brackets and run it in a PowerShell script on the remote machine, it installs successfully. I know my $remoteMachine is correct because I use it extensively throughout the rest of the script.
I know the $installPath variable also isn't the issue because for testing purposes I hardcoded the full path and it still doesn't install.
I also have proper permissions on the remote machine because earlier in the script I copy and paste the .msi from one machine to another without a problem.
I've tried a combination of commands and have been stuck here for a while, so any help would be greatly appreciated!

Ideally, this should work.
Invoke-Command -ComputerName $remoteMachine -ScriptBlock{msiexec /i $installPath /quiet}
The reason it is failing is coz you are not passing the $installPath as argumentlist. Modify it like this.
Invoke-Command -ComputerName $remoteMachine -ScriptBlock{
param(
[Parameter(Mandatory=$true,
Position=0)]
$installPath
)
cmd /c start /wait msiexec /i $installPath /quiet
} -ArgumentList $installPath
But if it isn't working, here is a workaround that I used a while ago.
Create a .bat file with the command msiexec /i $installPath /quiet and push it to the location just like you pushed the msi file.
Now from the invoke scriptblock, simply call the bat file instead.
Invoke-Command -ComputerName $remoteMachine -ScriptBlock{C:\Users\Username\Install.bat}
where Install.bat is the name of your bat file.
Note: You might want to use the /norestart switch as well if you are not looking to cause a reboot. Depends on what you are trying to install.

Beginning in PowerShell 3.0, you can use the Using scope modifier to identify a local variable in a remote command.
syntax of Using :- $Using:<VariableName>
In your case :
Invoke-Command -ComputerName $remoteMachine -ScriptBlock{cmd /c start /wait msiexec /i $Using:installPath /quiet}
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote_variables?view=powershell-7.2#using-local-variables

Related

Remotely running Acrobat Distller via powershell command line

I have a task to remotely run Acrobat Distller (AD) remotely.
I was able to locally run AD in the command prompt:
"C:\Program Files (x86)\Adobe\Acrobat DC\Acrobat\acrodist.exe" /O $OutputFolder $InputFolder\test.ps
I tried invoking the same command using powershell:
powershell.exe -NoExit -Command Invoke-Command -ComputerName $server -ScriptBlock {'"C:\Program Files (x86)\Adobe\Acrobat DC\Acrobat\acrodist.exe" /O $OutputFolder $InputFolder\test.ps'}
When ran, the powershell command did prompt any error BUT it did not generate the expected PDF output as well.
Can I get some assistance on what I am doing wrong here?
Thanks
You probably need to use $using:OutputFolder and $using:InputFolder.
Normally, powershell variables are only set for your session and don't carry over to remote servers. The $Using: format allows you to do this with Invoke-Command.

Start-process: both positional parameter and can't locate file specified

Ok, So I am working on the powershell remote deployment of software to ADUsers.
I have remote access to the computer and all, no problems there.
Right now I am at the point where I have a exe file from Ninite to just install 7zip onto the client pc just to see when it works so i can start deploying some bigger programs to it.
The guide I have found to help me deploy out the software for now looks like this:
Invoke-Command -ComputerName *REDACTED* -Scriptblock {
Start-Process C:\Users\Administrator\Documents\ninite_7_zip\Ninite 7Zip Installer.exe '/silent' -wait
}
When I do run this code, I get the error:
A positional parameter cannot be found that accepts argument 'Installer.exe'.
So I thought to myself, that it might be because of the spaces in the name. So therefore I changed it to:
Invoke-Command -ComputerName *REDACTED* -Scriptblock {
Start-Process C:\Users\Administrator\Documents\ninite_7_zip\Ninite_7Zip_Installer.exe '/silent' -wait
}
And ofcourse also changed it's name within the folder to match the "newly made" code.
But the error now changed into:
This command cannot be run due to the error: The system cannot find the file specified
Even though I use Powershell ISE, and I used it's guideboxes when writing, to enter the folder and find it, when I wrote the directory.
My only goal in this, is that I want to remotely run and complete this installer on the client PC when deployed from the DC upon which the file lies.
Anybody got a qualified guess? Or maybe even so, a solution.
Thanks in advance for your kind answers.
When I do run this code, I get the error:
A positional parameter cannot be found that accepts argument 'Installer.exe'.
You'll want to use quotation marks to qualify path names with spaces in them:
Start-Process 'C:\Users\Administrator\Documents\ninite_7_zip\Ninite 7Zip Installer.exe' '/silent' -wait
But the error now changed into:
This command cannot be run due to the error: The system cannot find the file specified
Even though I use Powershell ISE, and I used it's guideboxes when writing, to enter the folder and find it, when I wrote the directory.
ISE is not smart enough to realize that the scriptblock is to be executed on a remote computer, so it completes the path based on your local file system.
You still need to copy the executable to the remote machine in order to execute it:
# first copy the installer to remote file system
$remoteSession = New-PSSession -ComputerName $computerName
$localInstaller = 'C:\Users\Administrator\Documents\ninite_7_zip\Ninite 7Zip Installer.exe'
$remotePath = Invoke-Command -Session $remoteSession -ScriptBlock { $env:TEMP }
Copy-Item $localInstaller -Destination (Join-Path $remotePath "7zInstaller.exe") -ToSession $remoteSession
# now we can invoke the executable on the remote machine (re-using the same remoting session)
Invoke-Command -Session $remoteSession -ScriptBlock {
Start-Process (Join-Path $env:TEMP "7zInstaller.exe") '/silent' -Wait
}
# clean up
$remoteSession |Remove-PSSession |Out-Null

Unable to run the parametrized batch file on remote machine using PowerShell

Execute the remote server parametrized batch file from PowerShell.
Doesn't throw an error nor executed command on remote machine.
$path = "D:\run\test-5.2.bat";
Invoke-Command -ComputerName testserver -Scriptblock { "$path" }
Script inside the bat file is msiexec with parameters, which shall execute through Command Prompt only.
Based on this msdn link, you can run a ps1 script file on remote computers. So if it is possible to "port" the content of the bat file in a ps1 it should work. Here is the msdn example:
Example 11: Run a script on all the computers listed in a text file
PS C:\> Invoke-Command -ComputerName (Get-Content Servers.txt) -FilePath C:\Scripts\Sample.ps1 -ArgumentList Process, Service
This example uses the Invoke-Command cmdlet to run the Sample.ps1 script on all of the computers listed in the Servers.txt file. The command uses the FilePath parameter to specify the script file. This command lets you run the script on the remote computers, even if the script file is not accessible to the remote computers.
When you submit the command, the content of the Sample.ps1 file is copied into a script block and the script block is run on each of the remote computers. This procedure is equivalent to using the ScriptBlock parameter to submit the contents of the script.
Hope that helps
$path is a string. PowerShell simply echoes bare strings instead of executing them, unlike CMD or bash. Use the call operator (&):
& "$path"
or Start-Process:
Start-Process cmd.exe -ArgumentList '/c', $path -NoNewWindow -Wait
to have PowerShell execute a string as a command. Since you say you're running msiexec.exe from the batch script using the latter may be required.
On top of that you have a scope issue. The variable $path inside the scriptblock is not the same as the one in the global scope. You can mitigate that via the using: scope qualifier:
Invoke-Command -Computer testserver -Scriptblock { & "$using:path" }
or by passing $path as an argument to the scriptblock:
Invoke-Command -Computer testserver -Scriptblock { & "$($args[0])" } -ArgumentList $path

Run .bat file with Administrative rights

My PowerShell script runs a .bat file to install an .msu file. But I need to run this .bat file with Administrator rights.
The .bat file is:
WUSA C:\temp\Win8.1AndW2K12R2-KB3191564-x64.msu /quiet /norestart
I have Domain Controller and a lot of clients. With PowerShell PS session I interactively connect to every client. I need to use this bat file with Domain Admin credentials, how can I do this?
You could use Invoke-Command
You could save the servers in list in a text file and then use the Get-Content command to save the array in a variable:
$clients = Get-Content C:\ExampleClientList.txt
Then use the variable for the ComputerName parameter of Invoke-Command. Then in the scriptblock parameter is where you run the command, since you can run executables in PowerShell there isn't any need for the bat file. Last the Credential parameter will allow you run this as the Local administrator.
Invoke-Command -Computername $clients -ScriptBlock {WUSA C:\temp\Win8.1AndW2K12R2-KB3191564-x64.msu /quiet /norestart} -Credential (Get-Credential)
I am not sure i understand your question.
To start batch file from powershell you can use start-proccess command:
powershell start-process <path to your file> -verb RunAs

Set Executable's Working Directory When PowerShell Remoting

I'm using PowerShell remoting to execute an exe file on a remote server. The problem is that the exe needs to have its Working Directory set to the directory that the exe is in for it to run properly. If I run the exe locally (on the server) from a command prompt it works fine, and if I use Enter-PSSession (from my workstation) and then use Start-Process -FilePath [PathToExe] -WorkingDirectory [DirectoryPath] that works fine, but if I use Invoke-Command -ComputerName [Blah] -ScriptBlock [MyScriptBlock] or $session = New-PSSession -ComputerName [Blah]; Invoke-Command -Session $session -ScriptBlock [MyScriptBlock] (from my workstation) then the working directory does not get set.
This is what [MyScriptBlock] looks like:
$scriptBlock = {
param($version, $database)
$hubSyncronizerExeDirectoryPath = "C:\inetpubLive\ScheduledJobs\$version\"
$hubSyncronizerExePath = Join-Path $hubSyncronizerExeDirectoryPath 'Test.exe'
Set-Location -Path $hubSyncronizerExeDirectoryPath
Get-Location
Write-Output "$version $database"
Start-Process -FilePath $hubSyncronizerExePath -WorkingDirectory $hubSyncronizerExeDirectoryPath -ArgumentList '/database',$database
}
I've also tried using Invoke-Command instead of Start-Process, but it has the same effect; the Working Directory does not get set.
I've verified this by using the SysInternals Process Explorer, right-clicking on the process and choosing Properties. When I launch it locally or use Enter-PSSession, the Command Line and Current Directory properties are set, but not when using New-PSSession or just Invoke-Command with ComputerName.
I'm using both Set-Location and setting the -WorkingDirectory, which are the 2 typical recommended approaches for setting the working directory, and Get-Location does display the expected (server's local) path (e.g. C:\inetpubLive\ScheduledJobs\1.2.3.4). I'm guessing that this is just a bug with PowerShell (I'm using V4 on workstation and server), or maybe there's something I'm missing?
UPDATE
It turns out that the working directory was a red herring (at least, I think it was). For some reason everything works fine if I called my executable from a command prompt.
So in my Invoke-Command (I replaced Start-Process with Invoke-Command), changing this:
& ""$hubSyncronizerExePath"" /database $database
to this:
& cmd /c ""$hubSyncronizerExePath"" /database $database
fixed the problem.
Thanks for all of the suggestions guys :)
Try looking at New-PSDrive and see if that helps
I guess you'll want something like
New-PSDrive -Name WorkingDir -PSProvider FileSystem -Root "\\RemoteServer\c$\inetpubLive\ScheduledJobs\1.2.3.4"
CD WorkingDir:
I assume you should be able to amend your script to include and put in the $version variable in to the path on the New-PSDrive command...
Not certain this will do what you need it to do, but it's the first thing that sprang to mind...
Alternatively, try amending your script as follows:
$hubSyncronizerExeDirectoryPath = "\\remoteserver\C$\inetpubLive\ScheduledJobs\$version\"