Reputation: 609
I need to update server names in a bunch of config files on multiple servers. I have a quick script to do it, but don't want to log in to every server individually and move the script local to it just to run it. So I'm using Invoke-Command. Everything is working getting the contents of the files and updating it, but when I try to write the new content back to the file, I'm getting a PositionalParameterNotFound error on the Set-Content. I assume because it's multi-line?
Here's the block of code that iterates through the files and attempts to write them back out:
ForEach($file in $Files){
$content = Invoke-command -computerName $computerName -ScriptBlock {get-Content $args[0] -raw} -argumentlist $($file.FullName)
$content = $content -replace $serverRegEx, $newServer
Invoke-command -computerName $computerName -ScriptBlock {Set-Content -path $args[0] -value "$($args[1])"} -argumentList $($file.FullName) $content
}
How do I pass this multi-line content back to the remote command parameter?
Upvotes: 0
Views: 1572
Reputation: 36297
So the issue is really simple here... -ArgumentList
accepts an array of objects, but you aren't passing it as an array. Here's what you have:
Invoke-command -computerName $computerName -ScriptBlock {Set-Content -path $args[0] -value "$($args[1])"} -argumentList $($file.FullName) $content
Here is, what I believe, your intent for parameters:
-computerName = $computerName
-ScriptBlock = {Set-Content -path $args[0] -value "$($args[1])"}
-argumentList = $($file.FullName), $content
The issue is that you don't have a comma between $($file.FullName)
and $content
, so it does not see them as an array of objects, it sees $($file.FullName)
as the value for -argumentList
, and then sees $content
as a separate object that it attempts to evaluate as a positional parameter, but it cannot determine what positional parameter it could be. The solution is to add a comma between the two items:
Invoke-command -computerName $computerName -ScriptBlock {Set-Content -path $args[0] -value "$($args[1])"} -argumentList $($file.FullName),$content
Upvotes: 1
Reputation: 29450
The Invoke-command
argumentList
argument accepts an array, in your seconde use of Invoke-command
you are only passing a single argument ($($file.FullName)
) to argumentList
. The $content
variable gets passed as a separate argument to Invoke-command
, not your script block.
Fix this by passing your 2 arguments as an array:
Invoke-command -computerName $computerName -ScriptBlock {Set-Content -path $args[0] -value "$($args[1])"} -argumentList @($($file.FullName),$content)
Upvotes: 0
Reputation: 151
I think you are accidentally converting an array to a string with "$($args[1])". Try this.
Invoke-Command -ScriptBlock {
ForEach($file in $using:Files){
$content = get-Content $file.FullName
$content = $content %{$_ -replace $serverRegEx,$newServer}
Set-Content -path $file.FullName -value $content
}
}
Upvotes: 0
Reputation: 16096
It's a scope issue.
You cannot use local variables in a remote session without specifying to do so. This requires PowerShellv3+ and beyond.
Example:
Invoke-Command -ComputerName Server01 -ScriptBlock {
Write-Output The value of variable a is: $using:a
Write-Output The value of variable b is: $using:b
}
Get-Help about_remote_variables -Full
LONG DESCRIPTION
You can use variables in commands that you run on remote computers. Simply assign a value to the variable and then use the variable in place of the value.
By default, the variables in remote commands are assumed to be defined in the session in which the command runs. You can also use variables that are defined in the local session, but you must identify them as local variables in the command.
USING LOCAL VARIABLES
You can also use local variables in remote commands, but you must indicate that the variable is defined in the local session.
Beginning in Windows PowerShell 3.0, you can use the Using scope modifier to identify a local variable in a remote command.
Upvotes: 1