MitchelWB
MitchelWB

Reputation: 609

powershell invoke-command update contents of a file with multiple lines of content

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

Answers (4)

TheMadTechnician
TheMadTechnician

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

zdan
zdan

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

BaronW
BaronW

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

postanote
postanote

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

About Remote Variables

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

Related Questions