7

Using powershell, I plan to run many functions on a remote host to gather information.

Here is an example to retrieve the content of file remotely just by running a function called getcontentfile with the parameter as the name of the remote host:

function getcontentfile 
{
    [CmdletBinding()]
    param($hostname)
    $info = Get-Content "C:\fileinfo.xml"
    write-host $info
}

This function should return information about the remote host to the local instance of PowerShell. How can I modify this script to do that?

Nic
  • 13,425
  • 17
  • 61
  • 104
Aimar
  • 71
  • 1
  • 1
  • 3
  • 1
    Have you read the PSH help on remoting? [about_remote](http://technet.microsoft.com/en-us/library/hh847900.aspx) – Richard Jul 09 '12 at 13:47

4 Answers4

4

Firstly to return information don't use Write-Host (this is true always, unless you really only want to both have colours and only operate interactively locally).

Make the output the return value of the function:

function getcontentfile 
{
    [CmdletBinding()]
    param($hostname)
    $info = Get-Content "C:\fileinfo.xml"
    $info
}

Secondly: enable PSH remoting on the target systems: see the help for Enable-PSRemoting.

Thirdly: run the command remotely:

Invoke-Command -computer comp1,comp2 -ScriptBlock { Get-Content "C:\fileinfo.xml"  }

This will return the contents of the file on the two computers, to separate the results adding -AsJob will return job objects which can then be queried separately with the Job cmdlets (see gcm -noun job for a list, note Receive-Job to get the results of a job).

Richard
  • 5,324
  • 1
  • 23
  • 20
  • Hi Richard, thanks for response. In fact , I tried this command but what I need is to execute a function remotely not only simple command. I am expanding the function to be larger with many commands like get-content of many files, string operations and hyperV also requesting (get-VM) etc...I need to run a command line that executes the function not the script file on the remote host and get only informations as result. – Aimar Jul 09 '12 at 14:06
  • 1
    @Aimar There are two (relevant) forms of `Invoke-Comment`. One takes a script block: put the *whole* script in there, this can include functions, load modules, etc. Or you can use `-FilePath` to name a (script) file. If you are loading scripts/modules then you need to consider execution policy. – Richard Jul 09 '12 at 14:38
  • thanks for response: you mean Invoke-Command (not Comment)..I tried to load Module for hyperv by calling in script file in local machine as : Import-Module C:/module/hyperv.psd1 – Aimar Jul 10 '12 at 08:05
  • @Aimar Correct spot on the typo, thanks, but now too late to edit a comment :-(. Was there a problem with calling `Import-Module`? (Seems an odd path however, H-V module is part of Windows so would expect it to be in default `env:PSModulePath`.) – Richard Jul 10 '12 at 08:48
4

You can run a local loaded function on a remote Machine:

Invoke-Command -ComputerName Comp1 -cred $cred -ScriptBlock ${function:get-contentfile } -argumentlist "ParameterA", "ParameterB"
icnivad
  • 327
  • 3
  • 12
  • 1
    This works - thanks! - but I'm unfamiliar with the syntax and have no idea how, or why, it works. Do you have a link to the appropriate docs or more information about this syntax for remote function invocation? – Dylan Beattie Dec 03 '12 at 16:35
3

Your first option is to enable Powershell 2.0 Remoting.

Personally, I wasn't really interesting in remoting although it is powerful, so I wrote a script to use WMI, create a process with cmd.exe and then pipe the stdout and stderr to a log file which you can then read.

The script leaves its log file on the remote computer, so you could simply: get-content \\remotecomputer\c$\remoteExec.log to read it.

<#
.SYNOPSIS
    Remotely executes a command and logs the stdout and stderr to a file on the
    remote computer.
.DESCRIPTION
    This script accepts three parameters (one optional) and executes a program on
    a remote computer.  It will verify connectivity and optionally (verifyPath) the
    existence of the program to be executed.  If either verifications fail, it will
    not attempt to create the process on the remote computer.
.EXAMPLE
    .\remoteExec.ps1 -program "dir" -args "c:\" -computerName "SEANC"
.EXAMPLE
    .\remoteExec "C:\Windows\SysWOW64\msiexec.exe" "/i c:\a.msi /passive /log c:\a-install.log" SEANC C:\Windows\Temp\remote.log -verifyPath
.PARAMETER computerName
    The name of the computer on which to create the process.
.PARAMETER program
    The command to run on the remote computer.
.PARAMETER args
    The command arguments.
.PARAMETER log
    The file to which the stderr and stdout generated by the command will be redirected.
    This is a local path on the remote computer.
.PARAMETER verifyPath
    Switch to enforce path verification.
#>
param(
    [parameter(Mandatory=$true)] [string]$program,
    [parameter(Mandatory=$false)][string]$args = "",
    [parameter(Mandatory=$true)] [string]$computerName,
    [parameter(Mandatory=$false)][string]$log = "C:\remoteExec.log",
    [parameter(Mandatory=$false)][switch]$verifyPath = $false
)

if (-not (Test-Connection $computerName -Quiet -Count 1))
{
    return Write-Error "Unable to connect to $computerName."
}

if ($verifyPath -and (-not (Test-Path \\$computerName\$($program.replace(":","$")) -PathType Leaf))) {
    return Write-Error "Path $program does not exist on $computerName."
}

try {
    $remoteWmiProcess = [wmiclass]"\\$computerName\root\cimv2:win32_process"
    $remoteProcess = $remoteWmiProcess.create(
        "cmd.exe /c `"$program $args > $log 2>&1`""
    )
} catch {
    return Write-Error ("Unable to create process through WMI.");
}

if ($remoteProcess.returnValue -ne 0) {
    return Write-Error ("FAILED on $computerName with return code: " + $remoteProcess.returnValue)
} else {
    return ("Successful trigger on $computerName; returned: " + $remoteProcess.returnValue)
}

EDIT: In this example, the script is called remoteExec.ps1 and I use it to create a remote powershell process and run a command (what the asker is attempting to do):

.\remoteExec.ps1 -program "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -verifyPath -computerName "computer1" -args "-command Get-ChildItem C:\"

I could then read the log with:

Get-Content \\computer1\C$\remoteExec.log

Sean C.
  • 954
  • 5
  • 7
  • 1
    While you might not be "really interested", PSH's remoting opens up more options, including not being dependent on DCOM (with all its firewall implications) and be able to perform the same operation on many servers concurrently. – Richard Jul 09 '12 at 13:48
  • Thank you for the rapid response. In fact, I ran this script on the local host and changed the program argument like dir or 'comamnd line' but it was the same output as empty one:Successful trigger on 'machine_name'; returned: 0 – Aimar Jul 09 '12 at 13:50
  • I tried to look into the log file in the remote host and it was like: 'Get-Acl' is not recognized as an internal or external command, operable program or batch file. – Aimar Jul 09 '12 at 14:01
  • Richard; what you say is true, but it also requires that PowerShell is installed on the remote server. In our enterprise, there are many situations where we have Windows Server 2003 and getting PowerShell installed is not an option. We are not allowed to deviate from an approved list of applications in certain environments. I was in no way dissing remoting. – Sean C. Jul 09 '12 at 14:21
  • 1
    Aimar: `.\remoteExec.ps1 -program "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -verifyPath -computerName "computer1"-args "-command Get-ChildItem C:\"` Because the script is calling CMD.EXE, you need to have it open powershell and specify the command you want to run. – Sean C. Jul 09 '12 at 14:28
  • I've added some information to the original answer. – Sean C. Jul 09 '12 at 14:33
  • 1
    Sean, if installing PSH is not option, why not keep things simple and use `psexec`? (Better would be to improve the policy). PS. PSH v2 can be installed on WinXP and 2003 if required and allowed. – Richard Jul 09 '12 at 14:40
  • Hi Richard! I'd love to improve the corporate policy on servers in our production hosting environment. Would you like to come and talk to the management and re-iterate the arguments for adjusting the allowed software policy on legacy servers that are slowly being phased out? PsExec would have been an option; I simply chose to do it using PowerShell. Two different hammers; I decided to make my hammer instead of using a ready-made hammer. – Sean C. Jul 09 '12 at 14:45
  • Very interesting debate there. Just a point, I've been working last year on a psh2 script, and just like you I wasn't allowed to install psh2 on servers, but it's clearly possible with psh2 to attack remotly computers without installing it on the servers. I don't recall my method, but I've got the script that do it, if you want me to give it to you, I'd gladly do it ;) – Anarko_Bizounours Jul 13 '12 at 07:38
0

The short answer is you can't.

Usually, you use Invoke-Command to run a command on a remote computer. That command can be a script block or a PowerShell script. Neither of these approaches can reach out and use variables or functions that exist on your local computer, i.e. you can't load your functions ahead of time to make them accessible on the remote computer.

What you have to do is get them on the remote computer, then load them from there. We create a common share and deploy our PowerShell scripts to that share.

splattered bits
  • 928
  • 3
  • 11
  • 23