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

This topic contains 23 replies, has 2 voices, and was last updated by Profile photo of Richard Schaefer Richard Schaefer 10 months ago.

  • Author
    Posts
  • #34615
    Profile photo of Richard Schaefer
    Richard Schaefer
    Participant

    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?

  • #34617
    Profile photo of Don Jones
    Don Jones
    Keymaster

    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?

  • #34618
    Profile photo of Richard Schaefer
    Richard Schaefer
    Participant

    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.

  • #34620
    Profile photo of Richard Schaefer
    Richard Schaefer
    Participant

    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?

  • #34621
    Profile photo of Don Jones
    Don Jones
    Keymaster

    – 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}
    
  • #34622
    Profile photo of Don Jones
    Don Jones
    Keymaster

    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.

  • #34624
    Profile photo of Richard Schaefer
    Richard Schaefer
    Participant

    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}
    
  • #34625
    Profile photo of Don Jones
    Don Jones
    Keymaster

    "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."

  • #34626
    Profile photo of Richard Schaefer
    Richard Schaefer
    Participant

    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..."

  • #34627
    Profile photo of Don Jones
    Don Jones
    Keymaster

    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 ;).

  • #34628
    Profile photo of Richard Schaefer
    Richard Schaefer
    Participant

    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.

  • #34630
    Profile photo of Richard Schaefer
    Richard Schaefer
    Participant

    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
    
  • #34631
    Profile photo of Don Jones
    Don Jones
    Keymaster

    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.

  • #34634
    Profile photo of Richard Schaefer
    Richard Schaefer
    Participant

    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
    
  • #34651
    Profile photo of Don Jones
    Don Jones
    Keymaster

    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.

  • #34652
    Profile photo of Richard Schaefer
    Richard Schaefer
    Participant

    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> 
    
  • #34653
    Profile photo of Richard Schaefer
    Richard Schaefer
    Participant

    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?

  • #34654
    Profile photo of Don Jones
    Don Jones
    Keymaster

    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, https://www.manning.com/books/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.

  • #34657
    Profile photo of Richard Schaefer
    Richard Schaefer
    Participant

    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.

  • #34658
    Profile photo of Don Jones
    Don Jones
    Keymaster

    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.

  • #34660
    Profile photo of Richard Schaefer
    Richard Schaefer
    Participant

    The log files were ending up in my profile somewhere, at least that's what Search was turning up, although I couldn't access them.

    Changed the Out-File to specify the full path and it now works correctly from both local and remote.

    So, back to how to specify the script path and parameters programmatically... I can't use this:

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

    in real life...

  • #34661
    Profile photo of Don Jones
    Don Jones
    Keymaster

    Right. So, now that we've got the remote script working, we can work on that. Let's do just one thing. I find it's often easier to walk into things one step at a time, so if something breaks, you know exactly what it is.

    Try 1:

    Invoke-Command -ComputerName ad1hfdahp802 -ScriptBlock {
    param($a)
        d:\AHP\pi_exceed_presentation\pi_exceed_presentation_deploy.ps1 $a
    } -Arg 'INT'
    

    Try 2:

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

    I'm just curious to see what happens either way. Do make sure you're running the shell as someone with remote access to the remote machine – normally, someone who's in the local Administrators group on the remote box.

  • #34662
    Profile photo of Richard Schaefer
    Richard Schaefer
    Participant

    Try 1 and Try 2 both worked. Given that the server name is outside the ScriptBlock and Arg list I put that in a variable also:

    $s='ad1hfdahp802'
    $a = 'INT'
    Invoke-Command -ComputerName $s -ScriptBlock {
        d:\AHP\pi_exceed_presentation\pi_exceed_presentation_deploy.ps1 $using:a }
    

    which worked fine.

    I poked at #1 some more and tried the following which also works:

    $s='ad1hfdahp802'
    $arg='INT'
    Invoke-Command -ComputerName $s -ScriptBlock {
    param($a)
        d:\AHP\pi_exceed_presentation\pi_exceed_presentation_deploy.ps1 $a
    } -Arg $arg
    

    So the next thing is how to calculate the path to the ps1 file in a variable outside the ScriptBlock and then use that variable inside the ScriptBlock...

  • #34705
    Profile photo of Richard Schaefer
    Richard Schaefer
    Participant

    Just to be certain it wasn't just a problem with the PS install on my computer I ran this from above on another computer also running 4.0:

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

    With the same result. In the Script window of the ISE I get a squiggly under the second "using" and if I hover over it the pop-up says "Unexpected token '$using:SDFEnvironment'..." You don't get this error if you put this code into the Script window of the ISE? Can you share your $PSVersionTable?

You must be logged in to reply to this topic.