Use PowerShell to Execute Commands on Remote Machines
An alternative to using PsExec
March 18, 2009
I wanted to run the ipconfig /all command on all my remote servers and have the results returned to my local machine. I knew that I could use PsExec from the PsTools suite, but I wanted to see how well PowerShell 1.0 worked for this task. (In PowerShell 2.0, you can run PowerShell commands directly against remote computers, but this version is still in the Community Technology Preview—CTP—stage.) After some investigation and trial runs, I created the RemoteProcess.ps1 script in Listing 1.
Listing 1: RemoteProcess.ps1 |
---|
As callout A in Listing 1 shows, you can use Windows Management Instrumentation's (WMI's) Win32_Process class to run a remote process in PowerShell 1.0. Specifically, I use the [wmiclass] type accelerator to connect to WMI's ROOTCIMV2 namespace and access the Win32_Process instance on the computer that's specified in the $s variable. (For information about the WMI type accelerator, see "Type Accelerators: A Useful But Undocumented Feature in PowerShell 1.0.") I then use that class's Create method to start a remote process that runs the ipconfig /all command. The command's results are stored in a text file on the remote server's C drive. This output file's name follows the format $s-ipconfig.txt, where $s is the remote server's name.
Besides storing the ipconfig command's output, I capture the return value of the Win32_Process class's Create method. This method returns a value of 0 when the process is successfully created; any other number indicates an error. The different values are shown in the switch statement in callout B in Listing 1. (You can find more information about them in MSDN's Create Method of the Win32_Process Class web page.) When the Create method returns a value, this switch statement displays the remote server's name followed by the appropriate message based on that value. For example, if the Create method returns a value of 2, the switch statement displays the message Access Denied after the remote server's name.
When the Create method is successful, the ipconfig /all command runs and the results are saved on the remote server's C drive. I didn't want to keep the results on each remote machine, so I used the Move-Item cmdlet in the statement
Move-Item -path \$sc$$s-ipconfig.txt ` $destination -force
to move them to my local machine. However, when I ran the script multiple times to test it, I received an error message along the lines of Move-Item: Cannot find path '\mycomputerc$mycomputer-ipconfig.txt' because it does not exist. Only then did I realize that it takes time to run a Win32 process on remote machine that's across a network—and PowerShell doesn't wait for that process to terminate before it starts executing the next command. So, when PowerShell started executing the Move-Item cmdlet statement, it couldn't find the file because the file didn't exist yet.
To overcome this problem, I used the Microsoft .NET Framework System.Diagnostics.Process class's Start method to execute the Move-Item cmdlet because PowerShell then has to wait for the Win32 process to finish before continuing to the next command. As the code in callout C in Listing 1 shows, I also used the System.Diagnostics.Process class's WaitForExit method to set a maximum wait time. That way, PowerShell won't continue until either the remote process finishes or 3 seconds have elapsed. (For more information about the Start and WaitForExit methods, see the web-exclusive article "Q. When I run a Win32 process from Windows PowerShell, how can I pause PowerShell so that the process can exit before I run other PowerShell commands?")
You can download RemoteProcess.ps1 by clicking the Download the Code Here button at the top of the page. Before you use RemoteProcess.ps1, you need to customize it and create an input file.
To customize RemoteProcess.ps1, open it in a text editor, such as Notepad. In the first line
$destination = \mycomputerd$ipconfig
change \mycomputerd$ipconfig to the location where you want to store the output files. In the second line
$servers = Get-Content D:servers.txt
change D:servers.txt to your input file's pathname.
To create the input file, construct a text file that contains the names of the remote servers on which you want to run the ipconfig /all command. Each server name needs to be on a separate line.
I wrote this script for remote servers running Windows Server 2003. You need to run PowerShell as an administrator for this script to work because there's no way to provide alternative credentials when using the [WMI] type accelerator.
About the Author
You May Also Like