PowerShell Live - being retired

Please go to http://www.shelltools.net for more information

Welcome to PowerShell Live - being retired Sign in | Join | Help
in
Home Main Site Blogs Forums Videos Chat Customer Support

Karl Prosser on Powershell

Starting processes on remote machines

"192.168.1.100","machine2" | foreach-object {([wmiClass]"\\$_\ROOT\CIMV2:win32_process").Create("calc")}

So what does this do?

Well first of all it takes in a list of machines, in this case 2, one specified by name, and one by IP, then pipes that into a foreach "loop" and from there we leverage WMI to reach out to another machine and create an instance of a WMI class called win32_process. With that class we can then do various tasks, call various methods, In this case Create, which starts a calc.exe on each of those computers. (Note that on remote computers the calc.exe won't be visible, just will be in your task manager, however its an easy example, and when you pass in 127.0.0.1 or localhost as the machines for testing, then it will popup on your own machine).

High Level Breakdown

  1. Array to Pipeline. -  So lets break it down into the powershell features that are used to do this. The first is the pipeline. We have an array of things that are getting passed. These things happen to be strings of machine names that we explicitly declare. we could have these. This array gets stripped by powershell and passed along the pipeline to the next command there.
  2. Foreach-Object CmdLet. - This looks like a normal foreach loop, and effectively behaves like one, but is actually a Cmdlet. So for every item that comes down the pipeline. whatever that is in the {scriptblock} that follows gets run.
  3. [WmiClass] cast. - [typename] is how you cast in powershell. So here we are casting the following string to create a WmiClass object. This is a special cast, in comparison to simple casts of [int], [string] , in that the [WmiClass] in powershell is an adapter.
  4. $_ within String. - $_ is a special variable meaning the current item in the pipeline. When you put variables within a string they get expanded. In this case its the name of the machine.
  5. Create('Calc') method call. - Calls a method on the Wmi object that creates a process on the remote computer.

Detailed Breakdown

  1. Array to Pipeline. - LEVEL 1
    After Commands and Parameters, the behaviour of the pipeline is the next thing that a beginner needs to understand with powershell. Its a large topic and quite involved so I will only give a slight overview here. But basically the pipeline strips arrays and collections of objects and sends the objects through the pipeline one at a time. Think of a literal real world pipe with different markers along the way. You put an object in the pipe then roll it along, and it goes through each marker and continues further. In the same way if you run:

    "hello", "there", "sir" | A | B | C


    "Hello" gets piped through to A, then to B then to C then out the others ide (usually transformed to text on your computer screen), Then after that the next item in the array - "there" gets piped through A first, then B and C, and so on.
     
  2. Foreach-Object CmdLet.  LEVEL 1
    Gotcha Warning
    Foreach-Object is actually a cmdlet, and to a traditional programmer the syntax looks quite normal so most people miss that fact that its not a language keyword here, but a cmdlet that is processing the pipeline like any internal or third party Cmdlet. Also another gotcha is that the word foreach is an alias to foreach-object and also there is an actual Powershell Language foreach feature which works different, more akin to foreach in Visual Basic, which doesn't work on the pipeline. Keep this in the back of your mind if sometime you are finding yourself dealing with strange errors and behaviours when you are using foreach.
    Gotcha Warning
    Often in scripts you will see the alias %. % is simply an alias for foreach-object. I use this all the time because foreach is one of the most common commands you write in powershell and % is pithy, but will confuse and scare away beginners as it makes the language look more like a cryptic code, than source code, but bear with it, in no time % will come naturally to you.

    1,2 | foreach {"hello $_" }

    In the above example the output will return two items - "hello 1" and "hello 2". Once we understand the pipeline and that % is an alias for foreach-object, its pretty obvious. A programmer with a C-based language will feel quite at home with the curcly bracket style thinking its strongly typed with the language keyword foreach. But really foreach is a Cmdlet - In Dos terms think command line program like ipconfig. {"hello $_"} is actually a parameter. Here we don't specify the name of the parameter because powershell can assume Cmdlet parameters by position but we can actually declare it explicitly like below.

    1,2 | foreach-object -process{"hello $_" }

    The code above now is looking less like a traditional programming language, but a commandline in a traditional shell, and powershell is indeed a shell. The above syntax really helps drive this point home. run:

    get-Help foreach-Object -full

    to learn all about foreach-object and its parameters. The -Process parameter runs the associated scriptblock for EVERY ITEM in the pipeline. The -Begin runs its scriptblock before foreach-object first gets its first object, and -End after the pipeline has finished giving it objects to process.

    Compare our previous example to the example below which uses the LANGUAGE foreach command.

    foreach($i in 1,2)
    {
     
    "hello $i"
    } 

    Notice here that what is in { } is not a scriptblock parameter like with foreach-object but rather a language scope strongly bound to the foreach language keyword as in a traditional programming language. Another thing to note the language imperatives such as foreach, for, do while are not inheriantly compatible with the pipeline
  3. [WmiClass] cast. - LEVEL 3.
    With powershell you can cast various types as other types along the same lines as in any other dotnet language. Here is a couple of examples:[int]"2"
    [string]2
    However being a dynamic language it also gives you grace and does a conversion for many of the basic types so you can do the likes of:

    [datetime]"2001-01-01"

    rather than having to do something like :

    [datetime]::Parse(
    "2001-01-01")

    however unlike wmiclass a datetime in powershell is still the same as a dotnet datetime object, with the same properties and methods, even though it was created through a cast that in reality did a conversion from another type rather than just a plain cast. Powershell (and you yourself) can even extend ANY class, spotwelding on properties and methods of your liking, but types like [WmiClass] go a step further than that. They are called adapters. They adapt the underlying class to have different methods and properties to make it more useful and natural to use in powershell. A good example of this is the xml class in powershell. Underlying the xml class is a dotnet System.Xml.XmlDocument type. Lets have an example.

    [xml]
    "<A><B>test</B></A>"

    With this example here you see the first thing it is doing is a CONVERSION. converting the string containing the XML into an XmlDocument object. If that string wasn't valid XML you would get an error. Now that we have an XmlDocument we can access most of the usual properties and methods of an XmlDocument, however as powershell has adapted this class, we can do some more useful things. Powershell has added properties based on the contents of this specific instance. I.e. the A and B nodes. so if i type this:

    $myxml
    = [xml]"<A><B>test</B></A>"
    $myxml.A
    $myxml.A.B

    The first result returns the XmlNode of B(also adapted), while the last line returns the string content of "test". In a similar way with [WmiClass] powershell will adapt and add the releveant properties and methods of the WMI object you are accessing. These properties are going to be different for a Process than for a Hard Drive or Registry Entry. So given that file://mycomputer/ROOT/CIMV2:win32_process is a valid Wmi path.

    [wmiClass]
    "\\mycomputer\ROOT\CIMV2:win32_process"

    creates a wmiclass for the specified remote computer, and adapts methods and properties that are relevant for a process to it.

  4. $_ within String. - LEVEL 1

    Users of  PERL will be most at home with this. $_ is the default variable. The easiest way to thing about it is that it contains the object that is currently coming through the pipeline. The next thing to note in powershell is that when it processes strings it will automatically try to expand variables that are within double quoted strings, but it will not expand those within single quoted strings.

    $a = "test"
    "hello $a"
    'hello $a'

    So in the first example, with the double quotes it gets expanded to "hello test", and the 2nd example doesn't get exampled so it remains the literal "hello $a". in the same manner:

    "\\$_\ROOT\CIMV2:win32_process"
    the $_ gets expanded each time this is run to whatever is coming through the pipeline. so the first time it gets expanded to:
    "\\192.168.1.100\ROOT\CIMV2:win32_process"


    While the second time around in the foreach-object it gets expanded to:
    "\\machine2\ROOT\CIMV2:win32_process"


    Both producing valid strings when it gets converted to a WmiClass with the cast:

    [wmiClass]"\\$_\ROOT\CIMV2:win32_process"
     

  5. Create('Calc') method call. - LEVEL 2

    So now we have a valid WmiClass which is using a WMI win32_process class against a remote system. We can now use methods against it and see its properties. Here our goal is to start a process on the remote machine. try the following:

    $mywmi = [wmiClass]"\\localhost\ROOT\CIMV2:win32_process"
    $mywmi | get-Member

    Here we are piping the object we just created to get-member to see what is availible, property and method wise. Apart from the properties common to all WMI objects (they are adapted here started with a double underscore such as __SUPERCLASS)  you can see the properties and methods that are adapted SPECIFICALLY to a WMI win32_process. Amoung those is the one we want - Create. from Get-member you can also see the syntax that is expected and with the following command you can test it on your own box.

    $mywmi.Create('notepad.exe')

Summary

This oneliner article has been a lot longer than one liner, and probably longer than most will be in the series, however I wanted to give a good overview of some key concepts of powershell for beginners. I would also like to thank Xaegr who is a regular in our #powershell chat room for suggesting this particular theme for the oneliner.

-Karl Prosser

Published Wednesday, March 28, 2007 10:21 PM by karl
Filed under:

Comments

 

tobias said:

Starting CALC.EXE on a remote computer will work but the process will be invisible to the user. It is launched in the context of the caller, so you would need to open task manager to verify that calc.exe indeed was started.

The examples show clearly that you can easily invoke any WMI method on remote systems. Other examples would include starting, stopping or disabling of services or shutting down computers.

March 29, 2007 1:21 AM
Anonymous comments are disabled

About karl

I am one of the original creators of Powershell Analyzer, and the cofounder of Powershell Live.