Can the Invoke-Item cmdlet launch an executable with parameters? - powershell

I'm trying to install some custom Windows services using PowerShell, and I've been unable to run InstallUtil without getting the following error:
A positional parameter cannot be found that accepts argument ''
Here's what I've run that causes the error above:
Invoke-Command -ComputerName <remote machine> -ScriptBlock {Invoke-Item C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe "<path to service exe>"}

From the docs:
The Invoke-Item cmdlet performs the default action on the specified item.
I don't believe commandline arguments can be used with a default action.
However...
In this example, invoke-item shouldn't be necessary to invoke the executable.
Invoke-Command -ComputerName <remote machine> -ScriptBlock { C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe "<path to service exe>"}

Related

Accessing cmd remotely using PowerShell

I need to run a couple of bcdedit commands on a remote computer's cmd using a PowerShell script that runs on my computer. I am able to create a PSSession but I'm not sure how I can run cmd on the remote computer. When I run the code in the 'Invoke-Command' line, I get an error Connection to remote server failed with the following error message: Access is denied. When I just run Invoke-Command, I am prompted to enter the ScriptBlock, but when I do, I get yet another error: "Cannot bind parameter 'ScriptBlock' Cannot convert the "cmd /c 'bcdedit /copy {current} /d "Description"'} value of type System.String to type System.Management.Automation.ScriptBlock
I have never worked with PowerShell before. I need to do this in a couple of hours, and I am absolutely clueless right now.
Enable-PSRemoting -Force
Set-Item WSMan:\localhost\Client\TrustedHosts $ip -Concatenate -Force
$session = New-PSSession -ComputerName $ip -Credential $cred -ConfigurationName $config -UseSSL -SessionOption $sessopt
#problematic code
Invoke-Command -ComputerName $ip -ScriptBlock {cmd /c 'bcdedit /copy {current} /d "Description"'}
#works fine
Restart-Computer -ComputerName $ip -Force
ping.exe -t $ipaddr | Foreach{"{0}-{1}" -f (Get-Date -f "yyyy/MM/dd HH:mm:ss"), $_}
Assume that $ip, $ipaddr, $config, $sessopt and $cred store valid parameters.
You can run bcedit.exe directly in PowerShell, but because in PowerShell { and } are metacharacters, you need to quote identifiers such as {current}:
bcdedit /copy '{current}' /d 'Description'
See this answer for a discussion and list of PowerShell's metacharacters.
If you get an error on connecting to a remote computer, the implication is that your user account either doesn't have sufficient privileges to connect remotely or the target computer isn't set up for PowerShell remoting.
Note that Enable-PSRemoting -Force must be run on the target (server) machine, not on the calling (client) machine.
See the conceptual about_Remote_Troubleshooting topic.
The Restart-Computer cmdlet's -ComputerName parameter does not use PowerShell remoting, so the fact that it succeeds does not imply that PowerShell remoting, such as via Invoke-Command, works.
When I just run Invoke -Command, I am prompted to enter the ScriptBlock
PowerShell's automatic prompting feature for mandatory parameter values that weren't specified on the command line has severe limitations, and not being able to prompt for a script-block parameter value is one of them - see GitHub issue #4068; however, this additional problem is incidental to your real problem.
Thanks for all the suggestions, I was able to fix the error by adding -Credential to the Invoke-Command and Restart-Computer commands:
#problematic code
Invoke-Command -ComputerName $ip -Credential $cred -ScriptBlock {cmd /c 'bcdedit /copy {current} /d "Description"'}
Restart-Computer -ComputerName $ip -Credential $cred -Force

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

Manage IIS Remotely through TFS2015

Currently using TFS 2015 update 3 for deployments and I have added "PowerShell on target machine" task, which calls for PowerShell script saved on IIS server to stop website before deployments:
icm -ComputerName $server -ScriptBlock {Import-Module WebAdministration; Stop-Website -Name $app}
with session variables as: $server = abc.xyz.com, $app = DefaultWebSite
The error I get is:
The running command stopped because the preference variable "ErrorActionPreference" or common parameter is set to Stop: Cannot validate argument on parameter 'Name'. The argument is null. Provide a valid value for the argument, and then try running the command again.”
The same script works if I hard code the server & application name.
With a scriptblock, you can't use the variables from your script scope unless you use param with an argument list or with PowerShell 3+ use the using: scope modifier.
icm -ComputerName $server -ScriptBlock {Import-Module WebAdministration; Stop-Website -Name $using:app}

Passing a script with Invoke-Command

I have a script which uses a set of cmdlets which are on a Windows 2012 Domain Controller. I am using a Windows 7 machine which does not have these (Get-DHCPServerV4Lease for example).
At first I created a script module and applied it like this:
Invoke-Command -Computername <Server> -scriptblock {Get-ComputerStatus}
I then get an error message stating that the cmdlet is not recognised. Then I just converted it into a straight-forward PS1 script:
icm -cn <server> -FilePath .\ComputerInfo.ps1 -ArgumentList "Computer"
After running this I get an error saying that the various cmdlets I am using in the script are not recognised.
Get-DnsServerResourceRecord : The term 'Get-DnsServerResourceRecord'
is not recognized as the name of a cmdlet,......
How can I run my script against a DC using Invoke-Command?
Adding to Frode F's comment, you need to first install the corresponding module on the target machine, which you specify as <server> in your Invoke-Command cmdlet. Then within your script, you need to import that module before you can execute cmdlets which are available via that module.
Invoke-Command -Computername <Server> -scriptblock
{
Import-Module <moduleName> -ErrorAction SilentlyContinue
Get-ComputerStatus
#<Any othe cmdlets from the module>
}