Passing string $variable to invoke-command scriptblock parameter -name [duplicate] - windows

This question already has answers here:
How do I pass variables with the Invoke-Command cmdlet?
(3 answers)
Closed 6 years ago.
I'm currently writing a relatively easy PowerShell script to restart/stop/start a service on a remote machine. Everything is working well up until I decide to pass a $Service variable to -Name parameter in the Invoke-Command Scriptblock. I know I'm doing something wrong or forgetting something but your help would be greatly appreciated. Here is the code with the part giving me problems highlighted
[CmdletBinding()] Param([Parameter(Mandatory=$True,Position=1)]
[string]$Server,
[Parameter(Mandatory=$True)]
[string]$Service)
get-service -ComputerName $Server -Name "$Service"
Write-Host("------------------------------------------------")
Write-Host("Execute action on selected service: ")
Write-Host("1. Restart service ")
Write-Host("2. Stop service ")
Write-Host("3. Start service")
$choice = Read-Host -Prompt "Your choice"
switch ($choice)
{
1 {Invoke-command -Computername $Server {Restart-Service -Name "$Service" } }
2 {Invoke-command -ComputerName $Server {Stop-Service -Name "$Service" } }
3 {Invoke-command -ComputerName $Server {Start-Service -Name "$Service" } }
}
I have tried:
Single quotes around $Service
Double quotes around $Service
using param([String]$Service) in scriptblock
I keep getting the same error over and over:
Cannot bind argument to parameter 'Name' because it is an empty string.
+ CategoryInfo : InvalidData: (:) [Stop-Service], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.StopServiceCommand
I know it has to do with the fact Im trying to run local variable on a remote machine. but could someone point me in the right direction , I would love for the script to simply use the mandatory parameters
Using the approach mentionned here How to pass local variable to Invoke-Command's I modified the code like so:
1 {Invoke-command -Computername $Server {param ([string] $srv = $Service) Restart-Service -Name "$srv" } }
Unfortunately the error persists

the syntax for parameter passing into a scriptblock should be as follows:
(Corrected as #pk198105 suggested)
Invoke-command -Computername $Server {
param($service)
Restart-Service -Name "$Service"
} -ArgumentList $service
both argumentlist and param are required

ok thanks in part to Abhijith pk
the correct way to implement this is as follows:
Invoke-command -Computername $Server {param ($Service) Restart-Service -Name "$service" } -ArgumentList $service
Thank you

Related

How to correctly run 'rename-item' remotely?

I have declared the following variables:
$dir = 'C:\Users\user1\folder1'
$fname = 'abc.txt'
$tmp_fname = 'abc1.txt'
Now, I am remotely trying to execute below command:
invoke-command -cn $mycomp -Credential $mycred -ScriptBlock {
param($fname, $tmp_fname)
rename-item $dir\$fname -NewName $dir\$tmp_fname
} -ArgumentList ($fname, $tmp_fname)
Upon executing the above command, I am getting below error:
+ invoke-command -cn $server -Credential $host_cred -ScriptBlock {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Rename-Item], PSInvalidOperationException
+ FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.RenameItemCommand
You almost got it, here are some comments and suggestion.
You are not passing all the arguments, in your case is $dir
$dir is an alias for $env:windir so try to use another name.
In your example $dir = 'C:\Users\user1\folder1' , you reference a specific user folder on a remote computer, that might work but you better be mindful with that reference.
Although it might work, I would try to avoid symbols between variables like that $dir\$fname , a better way would be to include the backslash in the $dir and then combine both like so $($dir+$fname)
With all that said, here is what I think should work for you
$dPath = 'C:\Users\user1\folder1\'
$fname = 'abc.txt'
$tmp_fname = 'abc1.txt'
invoke-command -ComputerName $server -Credential $host_cred -ScriptBlock {
param($fname, $tmp_fname)
rename-item -LiteralPath $($dPath + $fname) -NewName $($dPath + $tmp_fname)
} -ArgumentList $fname, $tmp_fname , $dPath

Running a powershell from rundeck(linux) display different result

I'm trying to run a powershell script from rundeck(linux), If I run the script locally[Deletes some files from multiple terminal servers](Windows server) it is working as expected however if I call it from rundeck server(winrm configured) it seems that the script cant access the remote folders I'm trying to access.
I tried running the script using the same user but still shows different result.
Script bellow:
$userAD = "someuser"
$servers = Get-Content C:\TSList.csv
$Folder = "c$\Users\$userAD\"
$TSFolderShare = "\\sharepath"
Write-Output "#####Start of script#####"
Write-output `n
Write-output "Checking if $userAD user profile exist in Terminal servers..."
sleep -seconds 1
foreach ($server in $servers) {
Test-Path "\\$server\$Folder" -PathType Any
Get-ChildItem "\\$server\$Folder"
if (Test-Path "\\$server\$Folder" -PathType Any) {
Write-output "Resetting user profile in $server.."
Get-ChildItem "\\$server\$Folder" -Recurse -Force -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
sleep -seconds 1
Write-output "Done."
if( (Get-ChildItem "\\$server\$Folder" | Measure-Object).Count -eq 0)
{
Write-output "Done."
}
}
else
{
Write-output "Resetting user profile in $server.."
sleep -seconds 1
Write-output "User profile does not exist in $server."
#Write-output "\\$server\$Folder does not exist in $server!" -ForegroundColor Red
}
}
EDIT: It seems my problem is when running my script from another script with RunAS.
Below I'm trying to access a folder from another server using ps script, but since I want to integrate this to Rundeck I need to call my ps script from my linux server using python. I did a test running the ps script directly and calling the test path script using another script with RunUs using the same user I used to run the script manually
Scenario 1
Running PS script via separate PS script with RunAS(my_account)
$username = "my_account"
$password = "my_password"
$secstr = New-Object -TypeName System.Security.SecureString
$password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr
Invoke-Command -FilePath "C:\testpath.ps1" -Credential $cred -Computer localhost
(C:\testpath.ps1) Content below:
Test-Path "\\server\c$\Users\myaccount\"
result:
Access is denied
+ CategoryInfo : PermissionDenied: (\server\c$\Users\myaccount:String) [Test-Path], UnauthorizedAccessException
+ FullyQualifiedErrorId : ItemExistsUnauthorizedAccessError,Microsoft.PowerShell.Commands.TestPathCommand
+ PSComputerName : localhost
False
Scenario 2
Running C:\testpath.ps1 directly as my_account
Test-Path "\\server\c$\Users\myaccount\"
result:
True
I used session configuration in powershell to solve the issue. This way allows you to tie a credential to a PowerShell session configuration and reuse this configuration for all future connections.
https://4sysops.com/archives/solve-the-powershell-multi-hop-problem-without-using-credssp/
Thanks a lot!
You're facing a double-hop issue with Rundeck and Powershell, here the explanation. That's asked before, take a look a this, and here a good workaround. Also this to solve it.

trying to get all the GPO's related to the OU with invoke command

I have a this set of code:
#Find the OU with the selected Canonical name and save it to this variable
$OUObject = Invoke-Command -Session $S -ScriptBlock {Get-ADOrganizationalUnit -filter * -Property CanonicalName | Where-Object {$_.CanonicalName -eq $using:listBox2.SelectedItem}}
So after this code i get a an OU stored in a variable $OUObject.
i now want to get all the gpo's linked to this ou.
so my next step is this:
$test = $OUObject.LinkedGroupPolicyObjects
and now $test hold all the gpos linked to its ou. problem now is i want to get them by name. so i can do this:
invoke-command -session $s -scriptblock {get-gpo -guid $test}
but i will get this error:
PS C:\WINDOWS\system32> invoke-command -session $s -scriptblock {get-gpo -guid $test}
Cannot validate argument on parameter 'Guid'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
+ CategoryInfo : InvalidData: (:) [Get-GPO], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.GroupPolicy.Commands.GetGpoCommand
+ PSComputerName : DC01
so i look at $test and this is what it holds:
PS C:\WINDOWS\system32> $test
cn={5873971D-F689-4E83-8AFA-389FDD7F34CD},cn=policies,cn=system,DC=bla,DC=local
cn={2B7F8931-038E-46BC-B1DB-FBFA86097C08},cn=policies,cn=system,DC=bla,DC=local
cn={C74CADA1-B609-44A3-8D3C-F733CF3112E2},cn=policies,cn=system,DC=bla,DC=local
so what i acually need is to past to the get-gpo command only the part inside the cn{..}
if i hardcode for example and do this:
invoke-command -session $s -scriptblock {get-gpo -guid 5873971D-F689-4E83-8AFA-389FDD7F34CD}
i get the result right.
can anyone help me achive this please?
Use the regex -replace operator to extract the GUID from the DN, then pass the value to Invoke-Command using the $using: modifier:
$GUIDs = $test -replace '^cn=(\{[0-9a-f-]+\}).*$'
Invoke-Command -Session $s { $using:GUIDs |ForEach-Object { Get-GPO -Guid $_ } }

How can I handle remote errors with Invoke-Command

I have the following code:
Invoke-Command -ComputerName $remoteComputerName -Credentials $cred {& c:/program.exe}
How can I return the rc from program.exe as the Invoke-Command return code, particularly when it is non-zero.?
By default Invoke-Command will pass back whatever the result of the script was. If you are not sending back any other data you can always do something like this:
Invoke-Command -ComputerName $remoteComputerName -Credentials $cred {& c:/program.exe;$lastexitcode}
That should return the exit code of whatever application you were trying to run.
as an extension for what TheMadTechnician says, you can insert what ever happend in the remote computer to a powershell object, you can even wrap it with try{} catch{} or send back only $? (same as $LASTEXITCODE) and pass it back to the script:
$rc_oporation = Invoke-Command -ComputerName $remoteComputerName -Credentials $cred {& c:/program.exe; $?}
$rc_other_option = Invoke-Command -ComputerName $remoteComputerName -Credentials $cred { try{& c:/program.exe} catch{"there was a problem"} }
now "$rc_oporation" will hold your answers as 0 refer to success with no errors
hope that helps :)
before the above anwsers I got the following working (thanks to themadtechnician):
$s = New-PSSession -Name autobuild -ComputerName <ip address> -Credential $cred
Invoke-Command -Session $s -ScriptBlock {& 'C:\program.exe'}
$rc = Invoke-Command -Session $s -ScriptBlock {$lastexitcode}
if ($rc -ne 0)
{
write-output "run failed ..."
Remove-PSSession -Name autobuild
exit 1
}
else
{
write-output "run complete ..."
Remove-PSSession -Name autobuild
exit 0
}

Passing variable to several scriptblocks used in remote sessions Powershell

I have a script that runs from a domain server and 'does work' on a domain server.
The work is divided up into jobs that are in scriptblocks that are called with invoke-command using a stored PSsession.
Results are logged on the domain server in a log file that includes a datetime stamp in the logfile name.
Now, I need to add another log that needs to reside on the remote on which the work is done. The log name format also needs to include a date and time stamp.
My problem is passing the name of the log to each of the jobs so that they write to the same file. I've played around with -ArgumentList, #args, and $args which I can get to run without errors but do nothing so I am not passing the logfile name correctly.
Below is a super simplified version of how I've structured my script.
Is it a mistake to nest the Start-Job in another script block? How would I pass my unique log file name to a number of these scriptblocks to capture success/failure and specific points?
#log file names, ps session and other variables declared here
$DoDomainWorkScriptBlock = {
Try {
start-job -name DoDomainWorkjob -scriptblock{
$command = "C:\Program Files\someprogram\someprogram.exe"
& $command -f someargs
If ($? -ne "True") {Throw 'Do work failed’}
" Do non-domain work job completed. "
}
} Catch {$Error[0] | Out-File $ErrorLog -Append}
}
#other jobs nested in other scriptblocks like the one above here
Invoke-Command -session $RemoteSession -scriptblock $DoDomainWorkScriptblock | Out-File $DomainProgressLog -Append
Invoke-Command -session $RemoteSession -command{Wait-Job -name DoDomainWorkjob } | Out-File $DomainProgressLog -Append
Invoke-Command -session $RemoteSession -command{Receive-Job -Name DoDomainWorkjob } | Out-File $DomainProgressLog –Append
#invoke start, wait, and receive job commands for the other jobs
You can pass arguments to script blocks like this:
$code = {
param( $foo )
Write-Host $foo
}
$bar = "bar"
Invoke-Command -ScriptBlock $code -ArgumentList $bar

Resources