How to remotely execute a PS Script that resides on a remote server?

I’m trying to run a script from my local server where the script resides on the remote target server. I have a UNC path to the remote script so I thought I could just point to it, but that didn’t work. I saw a post somewhere about copying the script to a local variable and then use ScriptBlock to run it. The remote script does nothing but echo the parameter passed into a log file that I then copy back. I run this as a test:

$server='myserver'
$scriptpath='\\myserver\d$\AHP\pi_exceed_presentation\pi_exceed_presentation_deploy.ps1'
$SDFEnvironment='INT'
$script=Get-Content $scriptpath
Invoke-Command -ComputerName $server -ScriptBlock {$script} -ArgumentList($SDFEnvironment)

and it returns to the PS prompt, but when I inspect the target script directory there’s no log file. Since the target script does nothing but write to the log file there should be something. Here’s what I’m trying to run on the remote server:

Param
(
    # SDF Environment
    [Parameter(Position=0)]
    [string]$SDFEnvironment
)
if ($PSBoundParameters.ContainsKey('SDFEnvironment')) 
{
	"Script Ran Env $SDFEnvironment"|Out-File -FilePath .\$SDFEnvironment"_LogFile.txt" -Encoding ascii -Force
}
else
{
	"Environment Parameter missing"|Out-File -FilePath .\"ERR_LogFile.txt" -Encoding ascii -Force
}

Am I missing something basic? How can I tell (aside from script output) whether or not the script is actually running?

Rather than using a UNC, which will engage a second remote access hop, try using a local path. Also, confirm the execution policy of the remote machine.

And -ArgumentList doesn’t work quite that way.

But mainly, your script block isn’t running the script. It’s merely getting the content of the script, no?

How can I use a local path if the script resides on the remote server?

If ArgumentList doesn’t quite work that way, how should I code it to pass the SDFEnvironment variable to the remote script as the 0-th position parameter?

It’s supposed to run the script, but it appears it isn’t.

Note: Clearly I’m a newbie to PS. My company wants me to use it but won’t pay for training. I watched three videos on MS Virtual Academy and wrote a number of local scripts that worked very well. This is my first foray into remoting.

I made this change (removed the {} around the $script variable and added try…catch)

try
{
    Invoke-Command -ComputerName $server -ScriptBlock $script -ArgumentList $SDFEnvironment
}
catch
{
    Write-Output "Error running remote script"
}

and I get this:

Error running remote script

So it looks like now it’s executing the script, I just need to figure out the error which I assume is related to ArgumentList?

  • A path that’s local for the remote machine. When you ask it to use a UNC, it can’t, because that forces remote access and your credential can’t be delegated a second time by default, so the UNC access fails.

  • The -ArgumentPath parameter is intended to pass parameters to a Param() block defines in -ScriptBlock. Below, I’m using a v4 technique to pass the local variable values to the remote machine. You’ll notice that, before, even your $script variable wouldn’t have worked, because $script wasn’t defined on the remote machine. -ScriptBlock isn’t evaluated for local variables unless you use $using.

  • You weren’t trying to execute the script, you were trying to display its contents. Keep in mind that everything in -ScriptBlock is passed literally to the remote computer, which means you were having the remote computer run Get-Content. You weren’t having the remote computer run Get-Content, pass the results back to you, and then somehow executing those results as a script.

Your company’s setting you up to fail, unfortunately. PowerShell has a lot of stuff that’s non-obvious, and you’re going to end up banging your head into it a lot. I’d at least recommend investing in “Learn PowerShell in a Month of Lunches,” which is under $40, and in this specific case, “Secrets of PowerShell Remoting,” which is free. Pluralsight also has some excellent PowerShell training videos and runs around $30/month. If you’re going to make this a part of your career - and you should! - it might be worth a small investment of your own, so that you can eventually go find a better company.

$server='my server'
$scriptpath='d:\AHP\pi_exceed_presentation\pi_exceed_presentation_deploy.ps1'
$SDFEnvironment='INT'
Invoke-Command -ComputerName $server -ScriptBlock {$using:script path -SDF $using:sdfenvironment}

In your second case, you’re now telling YOUR computer to go get the script from a file share, and then pass it to Invoke-Command to run on the remote machine.

Frankly, there’s no need to be using Try/Catch at this point in your experiment. Let it show you the actual error message (which you’re suppressing by catching it) so you can see what’s happening.

Using the UNC with Get-Content the $script variable contains the entire remote script in a local variable. I was under the impression I could run the script on the remote machine that way. I’ll try your way but I have one question:

$server='my server'
$scriptpath='d:\AHP\pi_exceed_presentation\pi_exceed_presentation_deploy.ps1'
$SDFEnvironment='INT'
Invoke-Command -ComputerName $server -ScriptBlock {$using:script path -SDF $using:sdfenvironment}

The “-SDF” - is that supposed to be the parameter designation? I need to pass it positionally. I don’t know or have control over the scripts on the remote machines other than to tell them the first (0-th) parameter has to be a string with the Environment name in it. I can’t tell them what to call it. I used the same name in my test remote script as my test local script because I wrote them both. It won’t always be that way. Can I just leave out the “-SDF” and use:

$server='my server'
$scriptpath='d:\AHP\pi_exceed_presentation\pi_exceed_presentation_deploy.ps1'
$SDFEnvironment='INT'
Invoke-Command -ComputerName $server -ScriptBlock {$using:script path  $using:sdfenvironment}

“Using the UNC with Get-Content the $script variable contains the entire remote script in a local variable.”

Except, not really, no. You’re on Computer A. You’re telling Computer B to reach out to Computer B and get the contents of a script located in a file share, and then in a second step telling Computer B to execute that contents of that script. It’s overwrought, and it isn’t how Remoting works, really. And you’re engaging a second authentication hop, which won’t work. if you want Computer B running the script, you just tell it to. There’s no reason to Get-Content. What you were attempting to do would involve significantly more back-and-forth than needed, even assuming you sorted out the extra (and unnecessary) authentication hop.

“The “-SDF” – is that supposed to be the parameter designation? I need to pass it positionally.”

Yes, because in your script you defined a parameter with that name. I truncated the name, which PowerShell allows. But, if you want to pass it positionally, that’s fine. Just eliminate the “-SDF.”

OK, so this:

$server='ad1hfdahp802'
$scriptpath='d:\AHP\pi_exceed_presentation\pi_exceed_presentation_deploy.ps1'
$SDFEnvironment='INT'
Invoke-Command -ComputerName $server -ScriptBlock {$using:scriptpath $using:sdfenvironment}

Results in this:

At line:5 char:70
+ Invoke-Command -ComputerName $server -ScriptBlock {$using:scriptpath $using:sdfe ...
+                                                                      ~~~~~~~~~~~
Unexpected token '$using:sdfenvironment' in expression or statement.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : UnexpectedToken

If I put the “-SDF” back in it barfs on that first. I’m running this in the ISE and in the script editor I’m getting a squiggly under the $using:sdfenvironment and when I hover over it the prompt says “Unexpected token…”

Oh. You’re using v2 or v3. Ugh.

$server='ad1hfdahp802'
$scriptpath='d:\AHP\pi_exceed_presentation\pi_exceed_presentation_deploy.ps1'
$SDFEnvironment='INT'
Invoke-Command -ComputerName $server -ScriptBlock {
  Param($one,$two) $one $two } -Arg $scriptpath,$sdfenvironment

Which is what we had to do before $using. And, incidentally, how -ArgumentList works ;).

Nope. Local computer:

Name                           Value                                                                                                                           
----                           -----                                                                                                                           
PSVersion                      4.0                                                                                                                             
WSManStackVersion              3.0                                                                                                                             
SerializationVersion           1.1.0.1                                                                                                                         
CLRVersion                     4.0.30319.42000                                                                                                                 
BuildVersion                   6.3.9600.16406                                                                                                                  
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0}                                                                                                            
PSRemotingProtocolVersion      2.2    

and Remote Server:

Name                           Value                                                                                         
----                           -----                                                                                         
PSVersion                      4.0                                                                                           
WSManStackVersion              3.0                                                                                           
SerializationVersion           1.1.0.1                                                                                       
CLRVersion                     4.0.30319.42000                                                                               
BuildVersion                   6.3.9600.16406                                                                                
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0}                                                                          
PSRemotingProtocolVersion      2.2  

But I’ll try the alternate method anyway. I’m getting a little desperate.

Here’s what I got running the “old version” code:

At line:5 char:27
+     Param($one,$two) $one $two } -Arg $scriptpath,$sdfenvironment
+                           ~~~~
Unexpected token '$two' in expression or statement.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : UnexpectedToken

Well, let’s take the variables out of it, because your shell isn’t behaving the way I’m used to.

Invoke-Command -ComputerName ad1hfdahp802 -ScriptBlock {
d:\AHP\pi_exceed_presentation\pi_exceed_presentation_deploy.ps1 INT
}

So just one command, all static values. Eliminate complexity.

OK. That got somewhere. I got back:

PS C:\users\rs02130\Desktop\Projects\AHP\xml> Invoke-Command -ComputerName ad1hfdahp802 -ScriptBlock {
d:\AHP\pi_exceed_presentation\pi_exceed_presentation_deploy.ps1 INT }

Running Script

PS C:\users\rs02130\Desktop\Projects\AHP\xml> 

Which show the output from the Write-Output in the script. I also have a pipe to Out-File that should create a log file on the remote server and that’s not getting created. Here’s the remote script boiled down to it’s simplest

Param
(
    # SDF Environment
    [Parameter(Position=0)]
    [string]$SDFEnvironment
)
Write-Output "Running Script"
"Script Ran Env $SDFEnvironment"|Out-File -FilePath .\$SDFEnvironment"_LogFile.txt" -Encoding ascii -Force

Inside your script, you’re using a relative path. I wouldn’t do that. You don’t necessarily know what path the shell is going to default to (it won’t necessarily be the path the script is located within) when it runs.

Also, I’m not sure this:

.$SDFEnvironment"_LogFile.txt"

Is producing the results you expect.

“.$SDFEnvironment_LogFile.txt”

Would work better, I suspect.

Nope. That didn’t produce an output file either. I looked in the remote computer folder where the ps1 file is and in the local folder where I’m running the code in the ISE. No text file. I wrapped the Invoke-Command in a Try…Catch just to see if it was throwing an error I wasn’t seeing and it didn’t drop into the Catch block so…

I even took the variable out of the file name so it was just .\LogFile.txt and I didn’t get an output file. I updated the Write-Output so it writes out the value of $SDFEnvironment to make sure that is getting through and it is:

Remote Script:

Param
(
    # SDF Environment
    [Parameter(Position=0)]
    [string]$SDFEnvironment
)
Write-Output "Running Script - $SDFEnvironment"
"Script Ran Env $SDFEnvironment"|Out-File -FilePath .\LogFile.txt -Encoding ascii -Force

Tried the above with both .\LogFile.txt and “.\LogFile.txt”

Output:

PS C:\users\rs02130\Desktop\Projects\AHP\xml> 
Invoke-Command -ComputerName ad1hfdahp802 -ScriptBlock {
    d:\AHP\pi_exceed_presentation\pi_exceed_presentation_deploy.ps1 INT
}
Running Script - INT

PS C:\users\rs02130\Desktop\Projects\AHP\xml> 

Also, looking to buy your book. Latest update on Amazon is for PS V3: http://www.amazon.com/Learn-Windows-PowerShell-Month-Lunches/dp/1617291080/ref=sr_1_1?ie=UTF8&qid=1454348484&sr=8-1&keywords=Learn+PowerShell+in+a+Month+of+Lunches

Is there a PS V4 version out? If not, is one planned?

So, because I want you to learn this stuff, what would you try next?

Me, I’d try going to the server and running that script manually. Make sure the script is working before we try to get it to work remotely.

Regarding the book, Learn Windows PowerShell in a Month of Lunches, Second Edition is the latest version. There was no need to do a v4 or v5 update, as it covers basics that haven’t changed.

Sorry, should have mentioned that at the top. Before I even tried to run it remotely I logged into the server console and made sure the script ran locally. It does exactly what you’d expect. Writes the message to the console (Write-Output) and creates the LogFile.txt (or INT_LogFile.txt with the original code) in the same directory as the script.

I’m wondering if the Out-File is actually running but the LogFile.txt is ending up somewhere odd, like c:\windows\system32… I’m going to poke around the file system on the server and see if something like that is happening. Maybe I need to pass in the path I’m using so I can specify a full path/file name for the log file. Should need to but I’ve seen stranger things.

Well, that was my point with the relative path you’re using, vs. an absolute path like c:\files\whatever.txt. The shell is likely defaulting to C:\Windows\System32 when you connect.