Cannot bind parameter to argument 'command' because it is null. Powershell - powershell

I had a function similar to below code. It receives command and the command arguments. I had to run this command in background and collect the output. But that last statement is bugging me with this error
Error:
Cannot bind argument to parameter 'Command' because it is null.
+ CategoryInfo : InvalidData: (:) [Invoke-Expression], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.InvokeExpre
ssionCommand
+ PSComputerName : localhost
Code:
$cmd = 'Get-content'
$Arg = 'path to file'
$sb = "$cmd $Arg -ErrorVariable e -ErrorAction Stop"
invoke-Expression $sb #This printsoutput
$job = Start-job -ScriptBlock {Invoke-Expression $sb}
wait-job -id $job.Id
$job | Receive-job #this should print output but throwing error
I am pretty sure last line is the one throwing the error.

Another alternative to get the $sb into the scope of the scriptblock besides -argumentlist is to use the $using: scope. (PowerShell 3+)
$cmd = 'Get-content'
$Arg = 'path to file'
$sb = "$cmd $Arg -ErrorVariable e -ErrorAction Stop"
$job = Start-job -ScriptBlock {Invoke-Expression $using:sb}
wait-job -id $job.Id
$job | Receive-job

The issue here is that you are not actually giving Invoke-Expression a command.
Whenever you create a new context ( in this case, a job ) you loose your access to the parent session's environment. In your case $sb is currently null.
The way that you manage this is to pass the values as arguments via the -ArgumentList parameter of Start-Job:
start-job -ScriptBlock {} -ArgumentList
In order to facilitate handing $sb to the ScriptBlock you would do this:
$sb = "$cmd $Arg -ErrorVariable e -ErrorAction Stop"
$job = start-job -ScriptBlock { Param([string]$sb)
Invoke-Expression $sb
} -ArgumentList $sb
This can be confusing so this is the same code written with more friendly names:
$OuterSB = "$cmd $Arg -ErrorVariable e -ErrorAction Stop"
$job = start-job -ScriptBlock { Param([string]$InnerSB)
Invoke-Expression $InnerSB
} -ArgumentList $OuterSB

I got the same error but when I run PowerShell using administrator privileges, I don't get this error. Good luck!

I also got the same error when running command with user without proper authorizations.
This in deed can be related to that.
For example my code:
$allParams = #(10, "Some Process")
Start-Job -ScriptBlock $restartProcessRobot -ArgumentList $allParams
<#
#descr
Function that restarts given proces -DisplayName after given number of seconds.
#param1 Integer number of seconds after which the process should be restarted.
#param2 String display name of process(es) that must be affected. Wild cards accepted.
#>
$restartServiceAfter = {
param($seconds, $processName)
Start-Sleep $seconds
Restart-Service -DisplayName $processName -Force
}
And user is not authorized to restart services.

Related

How to run script against windows servers WinRM

I am trying to run a script that searches/downloads/installs windows updates on remote computers using WinRM. I am running this script as a domain user with Admin access. However, I get an ACCESS Denied error.
Now, I have the script copied over to the remote servers but I am unable to view output to see whether the script is running or not.
OUTPUT I want to see:
# Continue running on other servers on error
$ErrorActionPreference = "Continue"
# Server list
$servers = Get-Content "C:\Users\admin\Desktop\vm-nonprod.txt"
# Logs
$log = "C:\Users\admin\Desktop\log-nonprod.txt"
# Path to script on server list
$scriptpath = "C:\Patch.ps1"
$results = #()
foreach ($server in $servers) {
try {
$Credential = Import-CliXml -Path "C:\Users\admin\Desktop\admin.Cred"
#New-PSSession -ComputerName $server -Credential $Credential
Invoke-Command -ComputerName $server -Credential $Credential -ScriptBlock {$scriptpath} -ArgumentList "Y" | Out-File -FilePath C:\Users\admin\Desktop\WinPatch.txt
#Invoke-Command -ComputerName $server -Credential hhq\admin -FilePath "C:\Users\admin\Documents\Patch.ps1"
#Copy-Item -Path C:\Users\admin\Documents\Patch.ps1 -Destination 'C:\' -ToSession (New-PSSession –ComputerName $server -Credential $Credential)
}
catch {
Write-Output ("Error running script on remote host: " + $server)
}
}
$results | Export-Csv -NoTypeInformation $log
There's a few issues here.
Does the script exist on the server?
Sounds like yes, you have Patch.ps1 in C:\ on each $server
The scriptblock does not run the script - just prints the variable.
To run it, change {$scriptpath} to {. $scriptpath} or {& $scriptpath}
The variable $scriptpath is not in the scriptblock scope - you will have to pass it in the -ArgumentList
Change: {$scriptpath} -ArgumentList "Y"
____To: {param($p); . $p} -ArgumentList $scriptpath
The argument "Y" is being passed to the scriptbock, not the script. The scriptblock is not looking for it, so this value is being lost.
Assume you want it to be passed to the script - this needs to be done in the scriptblock:
{$scriptpath "Y"}
I would recommend getting rid of Out-File until you are happy with the output in the console.
Putting it all together:
-ScriptBlock {$scriptpath} -ArgumentList "Y" | Out-File -FilePath C:\Users\admin\Desktop\WinPatch.txt
-ScriptBlock {param($p); . $p "Y"} -ArgumentList $scriptpath
I believe you have the wrong Invoke-Command commented out. The one that is running only has the user name hhq\admin in the credential parameter. It might be failing due to that because it would be prompting for the password during run-time.

Trying to pass variable to a PowerShell function which is then run as a Start-Job

I am trying to pass the values of a variable (or more but for testing just the one right now) from a function to a Powershell Start-Job.
This works if using the invoke-expression but I have to get rid of the | out-string if passing to the start-job.
Works:
$thecomp = "127.0.0.1"
$func = {$compr = $thecomp; function TestConnection {Test-Connection -ComputerName $compr} } | Out-String
invoke-expression $func
TestConnection
 
This does not work and notice the difference in the $func line:
$thecomp = "127.0.0.1"
$func = {$compr = $thecomp; function TestConnection {Test-Connection -ComputerName $compr} }
Start-Job -Name testjob -InitializationScript $func -ScriptBlock {TestConnection} -ArgumentList $compr,$thecomp | Out-Null
Wait-Job -Name testjob | Out-Null
Receive-Job -Name testjob
Remove-Job *
I get the same error whether this is the local machine or a remote machine:
Cannot validate argument on parameter 'ComputerName'. The argument is
null or empty. Provide an argument that is not null or empty, and then
try the command again.
+ CategoryInfo : InvalidData: (:) [Test-Connection], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.TestConnectionCommand
+ PSComputerName : localhost
I've tried a number of different things including using -ArgumentList and other things. I'm really at a loss as to what I am missing. If I request the variables $compr and $thecomp in the ISE after running the above, I get the IP but it is not passing to the function that's within the Start-Job.
I got it working using a different method. This is what I am doing that works.
$system = '127.0.0.1'
$func = {
param ([string]$system)
Process {Test-Connection -ComputerName $system }
}
Start-Job -Name testjob -ScriptBlock $func -ArgumentList $system | Out-Null
Wait-Job -Name testjob | Out-Null
Receive-Job -Name testjob
Remove-Job *
It seems that the function is no longer explicitly named but is rather called or used with the variable defining it.
This also works and coincides better with what i was originally trying to do.
$system = '127.0.0.1'
$func = {function TestConnection ($system) {Test-Connection -ComputerName $system } } # | Out-String
Start-Job -Name testjob -InitializationScript $func -ScriptBlock {TestConnection $args} -ArgumentList $system | Out-Null

input objects in powershell jobs

I'm trying to implement a job in PowerShell that would look something like this:
$cred = Get-Credential
$job1 = Start-Job -InputObject $cred -ScriptBlock {
Get-ADUser -Credential $cred -Filter *
}
$res1 = Wait-Job -Job $job1 | Receive-Job
But I am receiving an error message:
"Wait-Job : The Wait-Job cmdlet cannot finish working, because one or more jobs are blocked waiting for user interaction. Process interactive job output by using the Receive-Job cmdlet, and then try again.
....
Deadlock detected: (System.Manageme...n.PSRemotingJob:PSRemotingJob) [Wait-J
ob],
....
"
But if I create this seemingly identical job like this:
$job2 = Start-Job -ScriptBlock {
$pass = ConvertTo-SecureString "pass" -AsPlainText -Force
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist "usr",$pass
Get-ADUser -Credential $cred -Filter *
}
$res2 = Wait-Job -Job $job2 | Receive-Job
Everything works perfectly.
Can you please help me understand why?
Thanks!
To pass arguments to the script block, you need to use the -ArgumentList parameter, not -InputObject. Try this:
$cred = Get-Credential
$job1 = Start-Job -ScriptBlock {PARAM($cred)
Get-ADUser -Credential $cred -Filter *
} -ArgumentList $cred
Note that -ArgumentList has to be the last parameter in the Start-Job command.
Well, this would probably be evident if you had but read the Help for Start-Job. If you use the -InputObject argument you then reference it from within the scriptblock with the automatic variable $Input. Since you reference $Cred out of scope like that it's trying to get credentials again. Here's the Help text for that parameter.
-InputObject <PSObject>
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.
You could alter your script to look like this and it should work fine:
$cred = Get-Credential
$job1 = Start-Job -InputObject $cred -ScriptBlock {
Get-ADUser -Credential $Input -Filter *
}
$res1 = Wait-Job -Job $job1 | Receive-Job

Wait-Job doesn't wait

Here is code
try{
$SetupJob = invoke-command -ComputerName $j -Credential $credentials -ScriptBlock $sb -AsJob | Out-Null
}
catch [System.Exception]{
continue
}
$SetupJob|Wait-Job
$disable_command = "D:\PSTools\PsExec.exe $comp -u Administrator -p $str -accepteula powershell.exe c:\share\ps_disable.ps1"
$CmdOutput = Invoke-Expression $disable_command 2>&1
Remove-Item Z:\ps_*able.ps1
Remove-Item Z:\setup.exe
$SetupJob executes setup.exe on a remote_computer. After execution, it is supposed to be removed. But, I get the following
Remove-Item : Cannot remove item \\remote_computer\share\setup.exe: The process cannot access the file '\\remote_computer\share\setup.exe' because it is being used by another process.
How to fix?
Don't pipe the output of Invoke-Command to Out-Null. If you do then $SetupJob is going to be null and Wait-Job will return immediately.

How to start remotely process in PowerShell

I have a problem, I have a script which:
Connect with PSSession (I use PSSession with admin account)
Stop 2 process
Do change on them files
Start the 2 process (Problem here)
I want to start process on server, so i'm connect with PSSession (No problem)
I do Invoke-Command :
# $pathProg path to my program
Invoke-Command -session $mySession -command {Start-Process $($args[0])} -ArgumentList $pathProg
But it does nothing (I verify with VNC)
I do Invoke-Command too :
# $pathProg path to my program
Invoke-Command -session $mySession -command {&$($args[0])} -ArgumentList $pathProg
It lauch the program (Good) but my script wait the end program (Not good)
Anyone have an idea ?
Thanks
You can try using WMI:
$command = "notepad.exe"
$process = [WMICLASS]"\\$CompName\ROOT\CIMV2:win32_process"
$result = $process.Create($command)
If you need passing credentials:
$cred = get-credential
$process = get-wmiobject -query "SELECT * FROM Meta_Class WHERE __Class = 'Win32_Process'" -namespace "root\cimv2" -computername $CompName -credential $cred
$results = $process.Create( "notepad.exe" )
$pathProg may be not be available within the script block which gets run eventually. You might want to pass it as an argument to the script block
Invoke-Command -session $mySession -command { param($progPath) ... } -argumentlist $progPath
Not that the outer -argumentlist, passes the arguments to the scriptblock.
Have you tried building the command as a string locally, then passing it to the Invoke-Command script as a ScriptBlock?
$remoteSession = New-PSSession -ComputerName 'MyServer'
$processName = 'MyProcess'
$command = 'Start-Service ' + $processName + ';'
Invoke-Command -Session $remoteSession `
-ScriptBlock ([ScriptBlock]::create($command))
Remove-PSSession $remoteSession
If you want feedback from the remote server then you can get the output via Write-Output, like this:
$command = 'Start-Service ' + $processName + ' | Write-Output ;'