Powershell Quoted variables parameters in a job - powershell

$buildDef = "Service.xxxx"
$buildDefFull="MyProject/$buildDef"
Start-Job -Name 'Service1' -ScriptBlock { tfsbuild start /collection:"http://yyyy:8080/tfs/DefaultCollection" /builddefinition:"$buildDefFull" }
i get this error:
Option builddefinition requires a value.
+ CategoryInfo : NotSpecified: (Option builddefinition requires a value.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
+ PSComputerName : localhost
i can't seem to get tfsbuild to accept the parameter in a start job... it actually runs fine if i just do the tfsbuild part with no job.
Any ideas how i'm supposed to pass that value?
tx

The $buildDefFull variable is outside the scope of the scriptblock.
You have 2 options:
PowerShell 3+
Use the Using scope modifier:
$buildDef = "Service.xxxx"
$buildDefFull="MyProject/$buildDef"
Start-Job -Name 'Service1' -ScriptBlock { tfsbuild start /collection:"http://yyyy:8080/tfs/DefaultCollection" /builddefinition:"$Using:buildDefFull" }
Any Version
Define and pass parameters to the scriptblock:
$buildDef = "Service.xxxx"
$buildDefFull="MyProject/$buildDef"
Start-Job -Name 'Service1' -ScriptBlock { param($bdf) tfsbuild start /collection:"http://yyyy:8080/tfs/DefaultCollection" /builddefinition:"$bdf" } -ArgumentList $buildDefFull

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.

Powershell class inside script block for Invoke-Command

I've faced up with a problem to execute powershell class methods inside a scriptblock passed to invoke-command.
Let's start with some examples
FooClass.psm1
class Foo {
static [string]Func() {
return "bar"
}
}
FooToScriptblock.ps1
using module .\FooClass.psm1
Function FooToScriptBlock {
$m = [Foo]::new()
write-host "from func:" $m.Func()
$sb1 = {$m = [Foo]::new(); $m.Func()}
$sb2 = {param($foo)$foo.Func()}
$sb3 = [scriptblock]::Create('$m.Foo()')
$s = New-PSSession -ComputerName "computer" -Credential "someuser"
$r1 = Invoke-Command -Session $s -ScriptBlock $sb1
write-host $r1
$r2 = Invoke-Command -Session $s -ScriptBlock $sb2 -ArgumentList $m
write-host $r2
$r3 = Invoke-Command -Session $s -ScriptBlock $sb3
write-host $r3
}
FooToScriptBlock
After executing I'm getting output like this
PS <scripts> $> .\FooToScriptblock.ps1
from func: bar
Unable to find type [Foo].
+ CategoryInfo : InvalidOperation: (Foo:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
+ PSComputerName :
You cannot call a method on a null-valued expression.
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
+ PSComputerName :
Method invocation failed because [Deserialized.Foo] does not contain a method named 'Func'.
+ CategoryInfo : InvalidOperation: (Func:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
+ PSComputerName :
You cannot call a method on a null-valued expression.
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
+ PSComputerName :
So now the question. Is it possible to execute PowerShell classes inside a script block on another computer?
The easiest solution is to specify the 'Using' statement in the PSSession by adding something like the following.
$sb = {using module UNC_Path_to_Module\FooClass.psm1}
$r = Invoke-Command -Session $s -ScriptBlock $sb
It should be noted that since PSSessions do not pass credentials to remote session by default and will not allow access to network resources requiring authentication. In order to authenticate to network resources you will need to specify the authentication method as CredSSP. This carries a security risk if the remote system is compromised.
New-PSSession -Authentication CredSSP

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.

Invoke-Command AsJob parameter set not working

I am able to do
Invoke-Command -ScriptBlock { Z:\prog.bat }
However, when I do
Invoke-Command -ScriptBlock { Z:\prog.bat } -AsJob
I keep getting
Invoke-Command : Parameter set cannot be resolved using the specified named parameters.
At line:1 char:1
+ Invoke-Command -ScriptBlock { z:\prog.bat } - ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Invoke-Command], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,Microsoft.PowerShell.Commands.InvokeCommandCommand
My intention is to run Z:\Prog.bat in the background since I will be executing this thru Ansible
Per the comments, this error message is telling you that it's not possible for PowerShell to know which Parameter Set you are trying to use. A Parameter Set is a collection of Parameters that are used together, some mandatory and some optional. Some cmdlets have a single set, some cmdlets have different combinations allowing them to be used in different ways.
You are using -ScriptBlock and -AsJob. Invoke-Command has quite a large number of Parameter Sets and to make your call of these parameters unique you need to use them with one of these parameters:
-Session
-ComputerName
-ConnectionUri
-VMId
-VMName
-ContainerID
E.g:
Invoke-Command -ScriptBlock { Z:\prog.bat } -AsJob -Computername SomeComputer
Alternatively if you're just attempting to run a script block as a background job on your local machine, don't use Invoke-Command instead consider using Start-Job:
Start-Job { Z:\prog.bat }

powershell Start-Service fails

I have very simple powershell script that starts service remotely.
Invoke-Command -Session $session -ScriptBlock { Start-Service "My test service v1" }
works fine but
$myval="My test service v1"
Invoke-Command -Session $session -ScriptBlock { Start-Service $myval }
fails with
Cannot validate argument on parameter 'InputObject'. The argument is
null or empty. Supply an argument that is not null or empty and then
try the command again.
+ CategoryInfo : InvalidData: (:) [Start-Service], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.StartServiceCommand
+ PSComputerName : mdfiletest
To me they are the same. Why is this not working? thanks
It does not work because when the scriptblock is executed on the remote server, the variable $myval does not exist in session state; it only exists on the local (client) side. The powershell v2/v3 compatible way to do this is:
invoke-command -session $session -scriptblock {
param($val); start-service $val } -args $myval
Another powershell v3 (only) way is like this:
invoke-command -session $session -scriptblock { start-service $using:myval }
The $using prefix is a special pseudo-scope which will capture the local variable and try to serialize it and send it remotely. Strings are always serializable (remotable.)