I'm writing a Powershell Cmdlet for which I need to pass in a PSRemotingJob
object as a parameter. The MCVE follows:
function My-Cmdlet {
[CmdletBinding()]
Param(
[Parameter(Position=0,
Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[PSRemotingJob[]]$Job
)
BEGIN {}
PROCESS {
ForEach ($j in $Job) {
$j
}
}
END {}
}
The issue is that when I pass a job into the cmdlet, I get an error, as follows:
PS C:\Temp> Invoke-Command -AsJob -CN svr001 -Command {Start-Sleep 10} | My-Cmdlet
My-Cmdlet : Unable to find type [PSRemotingJob]. Make sure that the assembly that contains this type is loaded.
At line:1 char:63
+ Invoke-Command -AsJob -CN svr001 -Command {Start-Sleep 10} | My-Cmdlet
+ ~~~~~~~~~
+ CategoryInfo : InvalidOperation: (PSRemotingJob:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
PS C:\Temp>
I realize that this should be a simple matter of substituting the correct object
type or fully-qualified object, but I've also tried using
[System.Management.Automation.PSRemotingJob] with the same results.
I'm using Powershell 4.0.
System.Management.Automation.PSRemotingJob is not public type and thus can not be expressed in PowerShell type syntax. But you can use its base public type instead: [System.Management.Automation.Job].
Related
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.
This question already has an answer here:
Passing string[] from batch file (which contains double quotes ") to powershell script
(1 answer)
Closed 3 years ago.
I have problems to pass an array to PowerShell script as parameter from CMD.
Here an example of the PS code:
[CmdletBinding()]
Param(
[string[]]$serverArray,
)
$serviceName = 'service1'
function getState {
Process {
$serverArray
foreach ($server in $serverArray) {
$servState = (Get-WmiObject Win32_Service -ComputerName $server -Filter "name='$serviceName'").State
}
}
getState
How I call script from CMD:
powershell -file .\script.ps1 -serverArray Server1,Server2
I get an error because $serverArray is not passed an array:
Server1,Server2
Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT:
0x800706BA)
At C:\script.ps1:58 char:29
+ $servState = (Get-WmiObject <<<< Win32_Service -ComputerName $server -Filter "name='$serviceName'").State
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject], COMException
+ FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
If I run the same command from a PowerShell window it works because the script accepts $serverArray as an array:
.\script.ps1 -serverArray Server1,Server2
Server1
Server2
CMD doesn't know anything about PowerShell arrays. You can pass the server list as individual tokens
powershell -File .\script.ps1 Server1 Server2
and use the automatic variable $args instead of a named parameter in your script:
foreach ($server in $args) {
...
}
or you can split the value of the parameter at commas in the body:
[CmdletBinding()]
Param(
[string]$Servers
)
$serverArray = $Servers -split ','
You can run this from command prompt:
powershell script.ps1 "Server1,Server2"
And if you add more parameters to your script :
powershell script.ps1 "Server1,Server2" "parameter2 argument" "parameter3 argument"
I am trying to individually monitor memory usage of a process (w3wp.exe) that has multiple instances of itself by filtering out a string found in the process' CommandLine property.
It works when I run this script locally:
$proc = (WmiObject Win32_Process -Filter "Name = 'w3wp.exe'" | Where-Object {$_.CommandLine -like "*SomeTextFromCl*"})
$id = $proc.ProcessId
$ws = [math]::round((Get-Process -Id $id).WS/1MB)
Write-Host $ws
However, when I try to run it remotely through Invoke-Command, I get an error telling that the Id property's value is null:
Cannot bind argument to parameter 'Id' because it is null.
+ CategoryInfo : InvalidData: (:) [Get-Process], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.GetProcessCommand
+ PSComputerName : RemoteServerName
My Invoke-Command syntax is:
Invoke-Command -ComputerName RemoteServerName -FilePath script.ps1 -Credential $mycredential
I'm sure it's simple but I'm back to PS after a long absence and I had a look around but couldn't find anything really helpful.
You are writing the answer to the console. You use the ps1 as a function, so you should use:
return $ws
instead of
write-host $ws
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?
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'