简体   繁体   中英

Execute a remote generic Powershell script with generic parameters

I need to write a Powershell script (let's call it "the controller script") that is able to call a generic remote Powershell script passing generic parameters. The controller script accepts as parameters the hostname, the credentials, the remote script path and the remote script's parameters as a hashtable.

The remote script, instead, may be any script which accepts any string parameter.

Using the hashtable parameter for the controller script is useful in that I can pass a dynamic dictionary of parameters (that depends on the controller call) while making PS do the work of "transform" the dictionary to a list of string parameters like -Param1 Value1 -Param2 Value2 .

I got some ideas from this answer and this is what I did (the "controller" script):

Param(
  [string] $ComputerName,
  [string] $Username,
  [string] $Password,
  [string] $ScriptPath,
  [string] $Parameters
)
$EncPassword = ConvertTo-SecureString $Password -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($Username,$EncPassword)

$ScriptBlock = [Scriptblock]::Create(".$ScriptPath $(&{$args} @Parameters)")

Invoke-Command -ComputerName $ComputerName -Credential $cred -Scriptblock $ScriptBlock

Then I execute it via the PS prompt this way:

.\controller.ps1 -ComputerName MACHINE_NAME -Username USERNAME -Password PASSWORD -ScriptPath "D:\TestScript.ps1" -Parameters @{AParameter = "asd"}

The execution fails with this error:

The term '.D:\\TestScript.ps1' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

So it seems that the Scriptblock refers to a local script (on the controller's machine), not to the remote machine where the target script resides.

Is there any way to let me execute a remote PS script using the hashtable parameter, which is the desired flexibility requirement?

UPDATE 1

I added a whitespace between the dot and the $ScriptPath variable in the ScriptBlock definition but the error is the same (without the dot).

$ScriptBlock = [Scriptblock]::Create(". $ScriptPath $(&{$args} @Parameters)")

The term 'D:\\TestScript.ps1' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

UPDATE 2

I've found a way to call the remote script without the parameters.

Param(
  [string] $ComputerName,
  [string] $Username,
  [string] $Password,
  [string] $ScriptPath,
  [hashtable] $Parameters
)
$EncPassword = ConvertTo-SecureString $Password -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($Username,$EncPassword )

Invoke-Command -ComputerName $computerName -Credential $cred -ScriptBlock {Invoke-Expression $args[0]} -ArgumentList $ScriptPath

I get the remote script output without parameters. Now the left thing to do is splatting the hashtable $Parameters remotely when calling the script at the remote path $ScriptPath . Do you have any idea? I made some trials but nothing worked.

I finally found the solution

controller.ps1

Param(
  [string] $ComputerName,
  [string] $Username,
  [string] $Password,
  [string] $ScriptPath,
  [hashtable] $Parameters
)
$EncPassword = ConvertTo-SecureString $Password -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($Username,$EncPassword )

Invoke-Command -ComputerName $computerName -Credential $cred -ScriptBlock {
  $params = $Using:Parameters
  Invoke-Expression "$Using:ScriptPath @params"
}

As you can see here we use the $Using variable in the ScriptBlock to retrieve the outside variables ( $ScriptPath and $Parameters ) and then we call the remote script splatting the parameters hashtable.

I'd recommend using the FilePath parameter in Invoke-Command rather than the scriptblock. That way PSRemoting does all the heavy lifting.

Invoke-Command -ComputerName $ComputerName -Credential $cred -FilePath $ScriptPath -ArgumentList $Parameters,$args

Otherwise you can use Sessions to copy the file and run the file.

$Session = New-PSSession -ComputerName $Computer -Credential $credential

Copy-Item -Path $ScriptPath -Destination $Dest -ToSession $Session

Invoke-Command -Session $Session -ScriptBlock $ScriptBlock

Edit: This may be more of what you were looking for:

The first issue seems to be that you don't have the correct path of the script or your user account doesn't have access to that path. Thats the error you are seeing. I've tested on my systems a few different ways and dot-sourcing should work.

The previous method expands the variables too early to use splatting. This is how to get splatting to work:

Invoke-command -ScriptBlock {$a = $args[0]; & D:\full\path\to\testscript.ps1 @a $args[1]} -ArgumentList $Parameters,$additionalArgs

Be sure to get a hash table instead of string in the params

[HashTable] $Parameters

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM