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.