How to pass string variable to invoke-expression? - powershell

I am running the below powershell command:
$cmd = "xxx.exe"
Invoke-Command -ComputerName localhost {Invoke-Expression $cmd}
However I get the error:
Cannot bind argument to parameter 'Command' because it is null.
+ CategoryInfo : InvalidData: (:) [Invoke-Expression], ParameterB
indingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,M
icrosoft.PowerShell.Commands.InvokeExpressionCommand

Look at the documentation for Invoke-Command.
Use either the -ArgumentList parameter or if powershell 3 see example 9 ($Using:).
http://technet.microsoft.com/en-us/library/hh849719.aspx
ArgumentList Example -
$cmd = "xxx.exe"
Invoke-Command -ComputerName localhost {Invoke-Expression $args[0]} -ArgumentList $cmd
If you use param in the script block you can used named arguments rather than the $args built-in.

I am going to show you the way I do it, passing a file with -FilePath and passing the parameters with -ArgumentList
I create a function that is going to contain the code I want to execute.
#remote_functions.ps1
param($command, $param1, $param2, $param3, $param4, $param5)
$ScriptVersion = "1.0"
function Write5Strings($string1, $string2, $string3, $string4, $string5)
{
Write-Host "String1: $string1"
Write-Host "String2: $string2"
Write-Host "String3: $string3"
Write-Host "String4: $string4"
Write-Host "String5: $string5"
throw "ERROR"
}
try
{
&$command $param1 $param2 $param3 $param4 $param5
}
catch [System.Exception]
{
Write-Error $_.Exception.ToString()
exit 1
}
And I invoke it this way:
$server="server"
$remotingPort=81
$job = Invoke-Command -AsJob -Port $remotingPort -ComputerName $server -FilePath ".\remote_functions.ps1" -ArgumentList #("Write5Strings", "apple", "banana", "orange", "pear", "watermelon")
Check also this question: How do I pass named parameters with Invoke-Command?

Related

How to use -ArgumentList with Invoke-Command to pass flags to a script?

How to use -ArgumentList with Invoke-Command to pass flags to a script?
# File: ./setup.ps1
param(
[Parameter(Mandatory=$false)]
[alias("force")]
[switch]$opt_force
)
if ($opt_force) {
write-host "FORCING something!"
}
write-host "Done"
Powershell Command Line:
PS> Invoke-Command -Computer pv3039 -FilePath ./setup.ps1 -ArgumentList "-force"
Error Message:
positional parameter cannot be found that accepts argument '-force'.
+ CategoryInfo : InvalidArgument: (:) [], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound
+ PSComputerName : pv3039
If you want to call a remote script, you can use Start-Process instead of Invoke-Command. Maybe you can try something like this.
Start-Process PowerShell -ArgumentList "YOUR_SCRIPT_PATH\setup.ps1 -Force" -NoNewWindow -Wait
This way it can accept parameters from the called script.

How run a command in PowerShell from a specified directory and wait for it to complete before moving on?

I have a script that works to run an executable and wait until done in PS but I need to modify it to use a path defined in a variable earlier in the script.
Working:
$job = Start-Job `
-InitializationScript { Set-Location C:\MyDirectory\ } `
-ScriptBlock { C:\MyDirectory\MyCmdLineExecutable.exe }
Wait-Job $job
Receive-Job $job
Not working:
$Path = "C:\MyDirectory\"
$ExePath = $path+"MyCmdLineExecutable.exe"
$job = Start-Job `
-InitializationScript { Set-Location $Path } `
-ScriptBlock { $ExePath }
Wait-Job $job
Receive-Job $job
Here's the error:
Set-Location : Cannot process argument because the value of argument "path" is null. Change the value of argument "path" to a non-null value.
At line:1 char:2
+ Set-Location $Path
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Set-Location], PSArgumentNullException
+ FullyQualifiedErrorId : ArgumentNull,Microsoft.PowerShell.Commands.SetLocationCommand
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
49 Job49 BackgroundJob Failed False localhost $ExePath
Running startup script threw an error: Cannot process argument because the value of argument "path" is null. Change the value of argument "path" to a non-null value..
+ CategoryInfo : OpenError: (localhost:String) [], RemoteException
+ FullyQualifiedErrorId : PSSessionStateBroken
Combining info from Start-Job docs with About_Scopes article, I am certain of that you need to use -InputObject parameter:
Specifies input to the command. Enter a variable that contains the
objects, or type a command or expression that generates the
objects. In the value of the ScriptBlock parameter, use the
$Input automatic variable to represent the input objects.
$Path = "C:\MyDirectory\"
$ExePath = $path+"MyCmdLineExecutable.exe"
$job = Start-Job -InputObject #( $Path, $ExePath) `
-InitializationScript { <# $Input variable isn't defined here #> } `
-ScriptBlock {
$aux = $Input.GetEnumerator()
Set-Location $aux[0]
& $aux[1] }
Wait-Job $job
Receive-Job $job
BTW, to run commands that are stored in variables and represented by strings, use & Call operator. See the difference:
$ExePath ### output only
& $ExePath ### invocation
I think you want Start-Process with the -Wait parameter. You can also specify the -WorkingDirectory parameter to specify the working directory for the new process. Example:
Start-Process notepad -WorkingDirectory "C:\Program Files" -Wait
Write-Host "Finished"
When you run this script, Notepad will open but the script won't continue until it closes. When you close Notepad, the Write-Host line runs.

Call VBS on a server with parameters

I want to call a VBScript located on a server with a PowerShell script, I want to pass parameters in the VBS by using invoke-command.
Code:
$username = [Environment]::UserName # Get the current user name
# Get with a Credential the logins to connect to an AS400 Server
$credential = $host.ui.PromptForCredential("Login", "Please enter your ID.", "", "")
$ASUser = $credential.Username.Substring(1)
$ASPwrd = $credential.GetNetworkCredential().password
# Call of the VBS on the server with these parameters
Invoke-Command -ComputerName MyServerName -ScriptBlock {
CSCRIPT "C:\MyScript.vbs"
} -ArgumentList $username, $ASUser, $ASpwrd
But I've got an issue caused by the parameters in the VBS, at these lines:
User = WScript.Arguments(0)
UID = WScript.Arguments(1)
PWD = WScript.Arguments(2)
The PowerShell script returns me this error:
C:\MyScript.vbs(64, 1) Microsoft VBScript runtime error: Subscript out of range
+ CategoryInfo : NotSpecified: (C:\Excel Script...pt out of range:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
The line 64 is User = WScript.Arguments(0).
It seems that my VBS does not recognize that I pass parameters, so I think the problem comes from the PS script, I also tried to pass the parameters like this:
Invoke-Command -ComputerName MyServerName -ScriptBlock {
CSCRIPT "C:\MyScript.vbs" $username $ASUser $ASpwrd
}
It does not work.
You're passing arguments into the scriptblock, but you never use them.
Change this:
Invoke-Command -ComputerName MyServerName -ScriptBlock {
CSCRIPT "C:\MyScript.vbs"
} -ArgumentList $username, $ASUser, $ASpwrd
into something like this:
Invoke-Command -ComputerName MyServerName -ScriptBlock {
CSCRIPT "C:\MyScript.vbs" $args[0] $args[1] $args[2]
} -ArgumentList $username, $ASUser, $ASpwrd
or this:
Invoke-Command -ComputerName MyServerName -ScriptBlock {
Param($user, $user2, $pass2)
CSCRIPT "C:\MyScript.vbs" $user $user2 $pass2
} -ArgumentList $username, $ASUser, $ASpwrd

Powershell: Issue with & in scriptblock

I face an issue when I run the following command
$x = "c:\Scripts\Log3.ps1"
$remoteMachineName = "172.16.61.51"
Invoke-Command -ComputerName $remoteMachineName -ScriptBlock {& $x}
The expression after '&' in a pipeline element produced an invalid object. It must result in a command name, script
block or CommandInfo object.
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : BadExpression
+ PSComputerName : 172.16.61.51
Issue is not seen if I dont use $x variable
Invoke-Command -ComputerName $remoteMachineName -ScriptBlock {& 'c:\scripts\log3.ps1'}
Directory: C:\scripts
Mode LastWriteTime Length Name PSComputerName
---- ------------- ------ ---- --------------
-a--- 7/25/2013 9:45 PM 0 new_file2.txt 172.16.61.51
Variables in your PowerShell session are not transferred to sessions created with Invoke-Command
You need to use the -ArgumentList parameter to send the variables your command and then use the $args array to access them in the script block so your command will look like:
Invoke-Command -ComputerName $remoteMachineName -ScriptBlock {& $args[0]} -ArgumentList $x
If you work with variables inside a script block you need to add the modifier using:. Otherwise Powershell would search for the var definition inside the script block.
You can use it also with the splatting technique. E.g.:#using:params
Like this:
# C:\Temp\Nested.ps1
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[String]$Msg
)
Write-Host ("Nested Message: {0}" -f $Msg)
# C:\Temp\Controller.ps1
$ScriptPath = "C:\Temp\Nested.ps1"
$params = #{
Msg = "Foobar"
}
$JobContent= {
& $using:ScriptPath #using:params
}
Invoke-Command -ScriptBlock $JobContent -ComputerName 'localhost'

Handle errors in ScriptBlock in Invoke-Command Cmdlet

I am trying to install a service on a remote machine using the powershell.
So far I have the following:
Invoke-Command -ComputerName $remoteComputerName -ScriptBlock {
param($password=$password,$username=$username)
$secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential ($username, $secpasswd)
New-Service -Name "XXX" -BinaryPathName "c:\XXX.exe" -DisplayName "XXX XXX XXX" -Description "XXXXXX." -Credential $credentials -ErrorVariable errortext
Write-Host("Error in: " + $errortext)
} -ArgumentList $password,$username -ErrorVariable errortext
Write-Host("Error out: " + $errortext)
When there is an error while executing New-Service the $errortext ErrorVariable get set properly inside the ScriptBlock, because the text: "Error in: shows me the error.
The ErrorVariable of the Invoke-Command does not get set (which I expected).
My question is:
Is it somehow possible to set the ErrorVariable of the Invoke-Command to the error I got inside the ScriptBlock?
I know I could also use InstalUtil, WMI and SC to install the service, but this is not relevant at the moment.
No, you can't get the Errorvariable from the Invoke-Command call to be set the same as in the scriptblock.
But if your goal is "detect and handle errors in the scriptblock, and also get errors returned back to the context of the Invoke-Command caller" then just do it manually:
$results = Invoke-Command -ComputerName server.contoso.com -ScriptBlock {
try
{
New-Service -ErrorAction 1
}
catch
{
<log to file, do cleanup, etc>
return $_
}
<do stuff that should only execute when there are no failures>
}
$results now contains the error information.
The Invoke-Command argument list is a one way deal. You can either output the error variable in the script e.g. on the last line of the scriptblock put:
$errortext
or better yet, just don't capture the error via the -ErrorVariable at all. The scriptblock output, including errors, will flow back to the caller even over a remote connection.
C:\> Invoke-Command -cn localhost { Get-Process xyzzy } -ErrorVariable errmsg 2>$null
C:\> $errmsg
Cannot find a process with the name "xyzzy". Verify the process name and call the cmdlet again.
+ CategoryInfo : ObjectNotFound: (xyzzy:String) [Get-Process], ProcessCommandException
+ FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand
+ PSComputerName : localhost
In general, I think it is much better to keep errors on the error stream, separated from the normal output.
This is almost certainly not the "correct" answer, but this is what I use when I want Invoke-Command to throw an error in the script.
$error.Clear()
Invoke-Command -ComputerName localhost -ScriptBlock {Command-ThatFails}
if ($error.Count -gt 0) { throw $error[0] }
If you wanted to keep the error in a variable, you could do the following:
$error.Clear()
Invoke-Command -ComputerName localhost -ScriptBlock {Command-ThatFails}
if ($error.Count -gt 0) { $myErrorVariable = $error[0] }
In the strictest sense, I believe the answer is no, you cannot set Invoke-Command's ErrorVariable to the contents of the ErrorVariable inside the script block. The ErrorVariable is only for the command it's attached to.
However, you can pass the variable in the script block out to Invoke-Command's scope. In your code you run your New-Service command with -ErrorVariable errortext. Instead, create your variable in the 'script' scope by prefacing the variable name with "script:", like this: -ErrorVariable script:errortext. That makes the variable available outside of the script block as well as inside.
Now your final line Write-Host("Error out: " + $errortext) will output the error that was generated inside of the script block.
More information here and here.