Tag Archives: Powershell

JSON for PowerShell Part 1

JSON, otherwise known as “Javascript object notation” is a fundamental way of representing key/value pairs. In database terms this translates as a field name (the key) and the field contents (the value).

Let’s show a single contact record in JSON, with two phone numbers, work and home.

{    
"FName":"Myron", 
"Lname":"Kapoodle",
"Address1":"123 Anywhere Lane",
 "Address2":"Apartment 404",
 "City":"BigTown",
  "State":"AZ",
    "Zip":"12345",
    "PhoneNumbers":[       
 {"type":"home","number":"920 234-3424" },
 {"type":"work","number":"920 535-2312"  }
   ]
}

A couple of characteristics:

The complete JSON structure is enclosed in its own curly brackets
If there is more than one record, then the whole structure is contained with square brackets, and the individual records are separated by a comma. This applies to nested records too, as in the case of the two phone numbers for the individual.

Below is the notation for two contact records. In this case, the whole structure is enclosed in square brackets. The individual contact records are enclosed in curly brackets and the two records are separated by a comma.

[    
{"FName":"Myron",
 "Lname":"Kapoodle",
 "Address1":"123 Anywhere Lane",
 "Address2":"Apartment 404",
 "City":"BigTown",
 "State":"AZ",
"Zip":"12345",
"PhoneNumbers":[ {"type":
"home","number":"920 234 3424"},
"type":"work","number":"920 535-2312"}]
}
,
{
"FName":"Myra",
Lname":"Kapoodlova",
"Address1":"123 Anywhere Lane",
"Address2":"Apartment 404",
"City":"BigTown",
"State":"AZ",
"Zip":"12345",
"PhoneNumbers":[ {"typee":
"home","number":"920 234-3424"},
{"type":"work","number":"920 535-2312"}]
}
]

When writing JSON, its helpful to have an automatic checker for syntax. One such is JSONLint at http://jsonlint.com. In Visual Studio Code you can use the Shift-Alt-F within the editor to nicely format JSON code.

The rules of JSON are summarized at JSONLint, but basically include:

  • Keys are enclosed in double quotes.
  • All data elements that are not a boolean or integers are enclosed in double-quotes.
  • Individual entities (i.e. records) are separated by a comma
  • Each key/value pair is separated by a comma
  • Each entity is enclosed by curly brackets.

Since I’m editing JSON in Visual Studio Code, there is more how VSC handles JSON in the Microsoft docs at JSON editing in Visual Studio Code.

Powershell has a two commands that deal directly with JSON.

PS M:\PSFolder> get-command *json*     

CommandType     Name                    Version    Source
-----------     ----                    -------    ------
Cmdlet          ConvertFrom-Json        3.1.0.0    Microsoft.PowerShell.Utility
Cmdlet          ConvertTo-Json          3.1.0.0    Microsoft.PowerShell.Utility


These convert Powershell objects to and from JSON notation. More on that in Part 2.

Powershell: Get Computer Configuration

I’m a great fan of the Belarc Advisor. This system profiler gives you an extensive inventory of just about anything you need to know about your computer. One exception is the kind of memory that is installed. (It will tell you how much is installed, and how many free slots you may have if any). The free version of Belarc is for personal use only; use on a corporate, educational or corporate network requires a license. So, it may be of interest to look at Powershell’s capability for returning information about computers.

My use-case is memory information.  I’ve got a slow machine that I think may be slow due to the fact that it only has limited memory. Of course, you can find the amount of installed memory of a machine using the Control Panel ->System applet.    Screenshot_060915_090137_AM

The down and dirty about the memory can be obtained by querying the Windows Management Instrumentation (WMI), a database of objects which are related to the Windows operating system. To restrict this to a memory query, you simply specify memory object.

Get-WMIObject CIM_PhysicalMemory

To make a long story short, this call will provide a host of information abou the physical memory chips that are installed in the machine. You can restrict the information by choosing which of the fields you actually want to see, by putting the call in the form of a SQL query.

This call duplicates the line above….returning everything available about the installed memory.

GetWMIObject -query "Select * from  CIM_PhysicalMemory"

This call returns a subset of the memory information. First the select statement gets all of the memory attributes (fields), and then the output is piped to the Format-Table cmdlet, specifying just the fields we want to appear in the table.  The -auto attribute will format field widths of the table, so the information for each record appears on one line.

Get-WmiObject -query "Select * from CIM_PhysicalMemory" | 
Format-Table name, capacity, datawidth,speed, manufacturer, devicelocator, partnumber -auto

Screenshot_060915_094312_AM

Caveat: Everything above was run on my Windows 7 machine. I also tried running this on the Windows 10 preview running in a VirtualBox VM, and didn’t get any response. I don’t know if this is related to Windows 10,  the fact that it is still a preview…  or that it is running in VirtualBox.

References & Thanks:
http://www.computerperformance.co.uk/powershell/powershell_wmi_memory.htm
https://msdn.microsoft.com/en-us/library/aa394347%28v=vs.85%29.aspx

File Management in Powershell

I’m looking to do the following in Powershell

1. Is a file that exists in a source folder more recent than a file in a target folder?
2. If it the file is more recent…copy it to the target folder, but before copying…
3. Back up the current version in the target folder, by appending a date to the file name.

Does a file exist?

test-path <filename>
This returns TRUE or FALSE if the file exists. If the file is in the current folder, then you can just list the file name, otherwise, the full path has to be included.

What is the file’s date and time?

The file date and time are properties of the file name, obtained via the Get-Item cmdlet.  We can assign these to a variable. Here I will compare file dates between two files that have the same name, but reside in different folders, one located along the default path, and one located on drive W.

$Sourcedate = (Get-Item w:myfile.csv).LastWriteTime
$TargetDate = (Get-Item myfile.csv).LastWriteTime
If ($Sourcedate = $Targetdate) {"True"}
If ($Sourcedate -lt $Targetdate) {"True"}
If ($Sourcedate -gt $Targetdate) {"True"}

Note (rather irritatingly….) that the usual comparison operators are different in Powershell.

=  -eq Equal
<> -ne Not Equal 
>= -ge Greater than or equal 
>  -gt Greater than
<  -lt Less than 
<= -le Less than or equal

 

Back up the target file

Having determined that the source file is newer than the target file, we now want to back up the target file, incorporating the date within the renamed filename.

myfile.csv
myfile0150505.csv
myfile.csv_5150505

Since we’ve used the functions for this in another post someplace,..

$ShortDate= $SourceDate | Get-Date -UFormat %Y%m%d

This returns the date in a short form:

20150505

Now append the date to the filename

$newfilename="myfile_"+$ShortDate+".csv"
Rename-Item myfile.csv $newfilename

This results in a file name of:

myfile_20150505.csv

Now we can do the copy from the source folder to the target.

Copy-Item  w:myfile.csv

 

 

 

 

Powershell: Search and Replace in text files

Searching and replacing within Powershell, seems a little awkward.

To do a search and replace, use the –replace parameter of ForEach-Object, with two attributes, the first being the thing to search for and the second being the replacement.

Get-Content -path ./NGA_Tracking.csv | ForEach-Object {$_ -replace ‘Tracking’, ‘TrackNum’}

You can do a series of these in a single set of piped commands….

Get-Content -path ./NGA_Tracking.csv | ForEach-Object { $_ -replace ‘Tracking’, ‘TrackNum’ `
    -replace ‘X’, ‘Y’ `
    -replace ‘A’, ‘B’ } |
Set-Content $mynewfile

In the case of a text file, each object being modified in the For-Each loop is a string terminated by a newline.

The good news is that there is full support for regular expressions, so if you know how to deal with those, they can be incorporated within the -replace mechanism.

I guess I’m still looking for something a bit more friendly for casual use, Wouldn’t it be nice, for instance to have something like Get-Content $myfile -replace ‘X’,’Y’.   Well, we actually can do this:

(Get-Content ./NGA_Tracking -raw) -replace ‘Tracking’, ‘TrackNum’ | out-file ./NGA_Tracking -Encoding utf8 

If you don’t put the parentheses around Get-Content $filename -raw you generate an error. The parentheses read the entire file into memory as an object, which can then take the -replace parameter. The -Encoding parameter is there to make sure the characters are all read correctly in subsequent uses of the file. (took lots of experimentation and blood on the floor, but that it what worked.)

The effect is the same, when the file name is replaced.

$Myfile = ‘./NGA_Tracking.csv’
(Get-Content $MyFile)  -replace ‘Tracking’, ‘TrackNum’ | Out-File $MyFile -Encoding utf8

But, if you create an object then it works.

$Myfile = Get-Content ‘./NGA_Tracking.csv’
$Myfile -replace ‘Tracking, ‘Tracknum’ 

Text to HTML

As we start fiddling with text files, the question comes up regarding the creation of html files.  There is a Convertto-HTML commandlet, which, on the face of it looks pretty rudimentary. But this TechNet article explains how to enhance things using styles.

Brightpearl API: Add UPS Tracking Numbers

We have now been using our web store for about a month, and for the most part things have been going pretty smoothly. One issue has been sending orders to our warehouse, and we’ve got a pretty good Powershell script that creates a comma delimited text file (.csv) of order numbers and address information from queries to the Brightpearl API. This file is sent daily to the warehouse via FTP, and warehouse staff  import the orders into their UPS Worldship program.

Screenshot_042715_044611_PM

The second half of this saga is to obtain the UPS Tracking number for each shipment. Once the shipment has been processed in UPS Worldship, a tracking number is generated and stored in the UPS Worldship record for that shipment. Worldship has an export function which will add the tracking number to a .csv file of order numbers and tracking numbers that we can use to update the order record in Brightpearl. The structure of this file, (which is completely customizable) is:

Order Number – In our case it is the Brightpearl sales order number
Tracking Number – from UPS. These look like “1Z 041 388 03 8331 4101”
Expected Delivery Date.
The .csv file looks like this. (UPS loves long field names).

ShipToCustomerID,ShipmentInformationLeadTrackingNumber,ShipmentInformationDeliveryDateTransitTime
"100064","1Z0413880373533722","20150429"
"100020","1Z0413880373302132","20150504"
"100068","1Z0413880373810940","20150430"
"100074","1Z0413880374436157","20150430"

The next step is to walk through the .csv file, find an order number, and update the custom field PCF_TRACKING in Brightpearl to contain the tracking number. Here is the Powershell call to update a single record:

PS>$BPOrders=Invoke-RestMethod `
 -Uri http://ws-use.brightpearl.com/public-api/myBPAccount/order-service/order/100541/custom-field `
 -Body $body `
 -Headers $headers `
 -Method Patch

There are a couple points of interest here. For the most part it is “standard” Powershell syntax for the Invoke-RestMethod.
1. We invoke this by assigning the results of the API call to $BPOrders
2. The call has several lines; the line continuation character is a accent aigu or back-tick.
3. Note that this query uses a $headers variable which includes the two validation properties for the Brightpearl query: , the name of the application and the security token for the application. These are stored as a hashtable.

PS>$headers
brightpearl-app-ref myappreference
brightpearl-staff-token mystaff-tokenXYZ123

More on obtaining the authcode here.

4. The $body variable is also created as a hashtable, but then converted to JSON, and placed between square brackets. This variable contains three parameters, the operation that you are performing on the record, the field that you want to modify, and the value that you want to put in the field. The syntax below simply says, “Replace the contents of the /PCF_TRACKING field with the value of 12345”.

$body=[ordered]@{"op"="replace";"path"="/PCF_TRACKING";"value"="12345"}
$body=($body | ConvertTo-JSON)
$body=("["+$body+"]")

The result is:

PS>$body
[{
"op": "replace",
"path": "/PCF_TRACKING",
"value": "12345"
}]

5. The -Method parameter is a “Patch”. This allows you to replace the contents of a single field in a record rather than replace an entire record as happens when you use PUT.

6. Finally note in the Invoke-RestMethod call, the URI contains “custom-field”. This is a literal, it isn’t the name of your custom field. The name of the custom field is contained in the body.I In the example above, it is “/PCF_TRACKING”

The above API call will replace the contents of a single field in a single record. The next step is to be able to loop through the .csv file, and for each record, find the corresponding record within the Brightpearl database, and update its Tracking number field.

Oh, one more thing, the results of the operation are contained in $BPOrders. The API actually returns the contents of ALL custom fields. You can choose which ones you want to see using dot notation.


PS>$BPOrders.response.PCF_TRACKING
12345

Quick Look at Windows 10

Well, I was going to say that the Windows 10 technical preview solves many of the problems that Windows 8 has, including the lack of a start button.

I installed this in a virtual machine both to take a look at 10, but especially to take advantage of updates to Windows PowerShell, which are available only with Windows 8 or later. I’m happy to say the start button is there.

The start button leads to the tiles left over from Windows 8. Maybe this can be re-configured to avoid the tiles? Didn’t Microsoft get the memo that people working on business-class desktop computers DON’T WANT TILES!  At least not the default ones, with videos, the stock market, etc.

OK so you can right-click and delete a tile.  I can imagine configuring this as a standard operation when deploying a new desktop computer to a co-worker.

If you look in the document explorer the new icons are even more cartoon-like than than before. It makes me nostalgic for the old “cartoon-like” icons of Windows XP.

  Sigh.

PowerShell: Simple GUI Message Boxes

One way to build simple message boxes in PowerShell is to “borrow” from .NET COM objects. Basically you initialize the object…(a.k.a. load the code for generating a messagebox), and then you create instances of the object which are the actual message boxes. These messagebox code may  be familiar to anyone who has programmed in vbScript, or any of the Microsoft programming languages such as Visual Basic or C#.  The boxes can look pretty good, even though the coding is a bit arcane.

To get started, initialize the messagebox object. You only need to do this once per session.

PS> $wshell=New-Object -comObject Wscript.Shell

Now you can make message boxes until the cows come home.

PS> $wshell.Popup(“Hi…This is a lovely messagebox”,0,”My Window Title”)

messagebox1

You can have multi-line boxes by including the newline (back-tick+’n’) to separate lines.

PS> $wshell.Popup(“Hi! A list of shipping addresses will appear in the next window.`n

If you need to edit the addresses then… `n

  1. Go back to BrightPearl`n
  2. Make the address changes.`n
  3. Run this program again.”,0,”Send To Warehouse”)

MessageBox2

The Buttons

You can have a buttons which return an integer based on which button is pressed.

In PowerShell, the button combinations are designated by integers:

0: OK
1: OK, Cancel
2: Abort, Retry, Ignore
3: Yes, No, Cancel,
4: Yes, No,
5: Retry, Cancel

The parameters for showing a window are:

<messagetext>, <duration>, <window title>, <button integers>

PS> $wshell.Popup(“Hi…This is a lovely messagebox”,0,”My Window Title”,1)

The first parameter is the message that you want to appear in the box. This is a string.

The second parameter “duration”, is an integer that specifies how long you want the messagebox to stay open if there is no activity from the user. If you specify ‘0’, then the box stays open forever. Note that your script is completely paused when this is the case. If you use a positive integer, that is the length in seconds that the messagebox stays open.

The third parameter is a string that is the title of the box. This appears in the top of the window frame.

In the example above, the messagebox call specifies “1” for the OK and Cancel button combination. When the user clicks on a button the messagebox closes, and returns an integer, in this case, 1 for OK or 2 for Cancel. The integers are displayed on the PowerShell command line.  (Technically, the integers are returned to the pipeline).

Note that these returned integers are have nothing to do with the integer used to determine which buttons are displayed. (Why do I have to remind myself of this?) 

Button Return Values

OK Cancel Abort Retry Ignore Yes No
1 2 3 4 5 6 7

Instead of having the result go to the pipeline, you can store it in a variable for further use. Capture the result variable by assigning the message box to a variable.

 PS> $result=$wshell.Popup(“Hi…This is a lovely messagebox”,0,”My Window Title”,1)

PS> $result

The Icons

Messageboxes can also have an icon. There are four to choose from, and each has a designated integer.

Stop 16
Question 32
Exclamation 48
Information 64

Now, here’s the arcane part.  To display an icon,  you take the icon’s numeric value and add it to the numeric value of the button numeric value.  So, for example to place a Stop icon on our sample message box with OK and Cancel buttons  we add 16 to 1. Its not like they couldn’t provide another parameter?

PS> $result=$wshell.Popup(“Hi…This is a lovely messagebox”,0,”My Window Title”,65)

Finally:

If you get an error message, when putting up a messagebox, like the one below…   it means that you didn’t run the New-Object cmdlet one time in your PowerShell session, prior to making a call to put up a messagebox.

PS> $wshell=New-Object -comObject Wscript.Shell

Having run that once, you can create as many messageboxes as you need within that particular PowerShell session.

You cannot call a method on a null-valued expression.
At line:1 char:1
+ $result=$wshell.Popup("Hi…This is a lovely messagebox",0,"My Window Title",65)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

MessageboxFinal

Powershell: Basic Address List Processing

One of the great things that was always a little bit fun (well, for some us… what passes for fun) is processing lists using Unix/Linux shell scripts and tools. When you are in practice, you can perform miracles; leap tall buildings in a single bound. Let’s see what we can do with a file using Powershell commands.

I received a file of “lapsed” donors. These are donors to our organization that gave to one of our campaigns in the past, but haven’t given recently. We know they were our friends in the past, and we think they still are, so we’d like to contact them for a one-time mailing, and/or add them to our master mailing lists.

The file is named “lapsed.csv”. The csv extension suggests this is a text file with a “comma separated values. And indeed, if I look at this file, at the powershell prompt, it is easily visible.

PS C:UsersLarrypowershell> cat lapsed.csv

This shows a comma delimited file with a header line:

Addressee,first,spouse,Organization,Street,City,State,ZIP
Joe Blow, Joe,,,123 W 57TH St Apt 123,New York,NY,10019
Jill Smith,Jill,Howard Services,123 Poor Farm Rd,Colchester,VT,05446

Our standard is:

Org, fname, lname, address1, address2, city, state, zip

So, among other things, we’re going to want to change the order of the information in the fields, as well as the field names.

First thing to do is to import the file into a single PowerShell variable, and then see what we’ve got.

PS>$lapsed= Import-CSV lapsed.csv
PS>$lapsed

Addressee : Joe Blow
 first : Joe
 spouse :
 Organization : Paul C Bunn Elementary
 Street : 123 W 57TH St Apt 123
 City : New York
 State : NY
 ZIP : 10019
Addressee : Jill Smith
 first : Jill
 spouse :
 Organization : Howard Services
 Street : 123 Poor Farm Rd
 City : Colchester
 State : VT
 ZIP : 05446

Using import-CSV, the file is converted into a series of custom objects with members that correspond to the existing field names …so further manipulations can be done using object manipulations, instead of just a bunch of searching and replacing. (Well that’s the theory anyway).

We can find out the number of records we have by looking at the count attribute.
 PS>$lapsed.count
 553

And we can see the “members” or field names of each record by using Get-Member

PS>$Lapsed |Get-Member

TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
 ---- ---------- ----------
 Equals Method bool Equals(System.Object obj)
 GetHashCode Method int GetHashCode()
 GetType Method type GetType()
 ToString Method string ToString()
 Addressee NoteProperty System.String Addressee=Joe Blow
 City NoteProperty System.String City=New York
 first NoteProperty System.String first=Joe
 Organization NoteProperty System.String Organization=Paul C Bunn Elementary
 spouse NoteProperty System.String spouse=
 State NoteProperty System.String State=NY
 Street NoteProperty System.String Street=123 W 57TH St Apt 123
 ZIP NoteProperty System.String ZIP=10019

Ok…we knew the field names before by just looking at the raw .CSV file. But now we have seen how the Import-CSV command converts the .CSV file to an array of objects with a type of PSCustomObject.  Since each address record is an object, the way we manipulate it is to use object methods.

1. Add a lname field for the last name
PS>$lapsed | Add-Member -Name “lname”

That takes the $lapsed table and pipes it to the Add-Member cmdlet. This adds the the member to EACH object in the table, rather than adding it to the table itself.

2. Add a fname and lname fields for the first and last name
PS>$lapsed | Add-Member -Name “fname” -MemberType NoteProperty -Value “”

PS>$lapsed | Add-Member -Name “lname” -MemberType NoteProperty -Value “”

Now the fields look like this:
Addressee : Joe Blow
 first : Joe
 spouse :
 Organization : Paul C Bunn Elementary
 Street : 123 W 57TH St Apt 123
 City : New York
 State : NY
 ZIP :
 lname : ""
 fname : ""

3. Copy data from the first name and organization fields to their new fields.
PS>$lapsed | ForEach-Object ($_.fname) {$_.fname=$_.first}
PS>$lapsed | ForEach-Object ($_.org) {$_.org=$_.Organization}
This leaves us with a record looking like this.

Addressee : Joe Blow
 first : Joe
 spouse :
 Organization : Paul C Bunn Elementary
 Street : 123 W 57TH St Apt 123
 City : New York
 State : NY
 ZIP : 10019
 lname :
 fname : Joe
 org : Paul C Bunn Elementary

6. Having copied the data from the old fields to the new ones, we can delete the old fields.
There isn’t a cmdlet to remove an object member, so the you have to use a different nomenclature. (Note To Self… opportunity to make a custom cmdlet?)

PS> $lapsed | ForEach-Object ($_){$_.PsObject.Members.Remove(‘Organization’)}
PS>$lapsed | ForEach-Object ($_){$_.PsObject.Members.Remove(‘first’)}
PS>$lapsed | ForEach-Object ($_){$_.PsObject.Members.Remove(‘spouse’)}

Now a typical record is starting to look much more like what we want it to look like.

 Addressee : Joe Blow
 Street : 123 W 57TH St Apt 123
 City : New York
 State : NY
 ZIP : 10019
 lname :
 fname : Joe
 org : Paul C Bunn Elementary

7. We still need to pick out the last name from the Addressee field.
There might be a couple approaches to this using regular text search methods:
a. Given a string “Joe Blow”, we could find the first blank character, and then take anything to the right of if as our last name.
b. We could start at the right hand side and count backwards until we get to a space.
c. If there are word functions, we can choose the right-most word in the string.
d. Use the split function. This is what we’ll use.

PS> $lapsed | ForEach-Object ($_){$_.lname=$_.Addressee.split()[-1]}

8. Finally, we can eliminate the “Addressee” field
PS> $lapsed | ForEach-Object ($_){$_.PsObject.Members.Remove(‘Addressee’)}

Street : 123 W 57TH St Apt 123
 City : New York
 State : NY
 ZIP : 10019
 lname : Blow
 fname : Joe
 org : Paul C Bunn Elementary

9. Time to export back to a CSV file.
PS> $lapsed | Export-Csv -Confirm -Path “C:UsersLarryPowershellulapsed.csv” -NoTypeInformation

10. Almost done. The one frost is that the field order isn’t exactly as I’d like. This can be fixed with the Select-Object cmdlet.

PS > $lapsed | Select-Object -Property fname,lname,org,street,city,state,zip |
Export-CSV -Path “C:UsersLarryPowershellulapsed.csv” -NoTypeInformation

Recall when typing a string of commands with a pipeline, the pipe delimter will also act as a “newline”, so you can break the command up over the course of a couple of lines and have the full pipeline execute as one command.

Notes:

As they say on public television: “Many thanks to the following:”

“jrv” on Microsoft Technet
http://goo.gl/1Kyw07

“Root Loop” on StackOverflow
http://stackoverflow.com/questions/22029944/batch-or-powershell-how-to-get-the-last-word-from-string

More Info:
http://windowsitpro.com/powershell/csv-excel-or-sql-it-doesnt-matter-powershell

More about the split function in Powershell help
PS> Get-Help about_Split

PowerShell Functions II – Output function results as a hash table

In a previous post I wrote a butt-simple function with three parameters, called it a couple ways, and talked about how the function returns its data either as a single string or as an array of variables. Before leaving this, I’m going to experiment about returning data as a hash table.

function Get-ReallySimple($fname,$lname,$age) {

$OutTable=@{"FirstName"=$fname;"LastName"=$lname;"Age"=$age}
return $OutTable

}

This prints out nicely, except it is in the wrong order.

 PS>Get-ReallySimple Joe Dokes 32

 Name                           Value
----                            -----
Age                            32
FirstName                      Joe
LastName                       Dokes

By adding the ordered keyword in front of the hash table definition, we can get the hash table to print in the order in which we asked for the function’s input paramenters.

function Get-ReallySimple($fname,$lname,$age) {

$OutTable= [ordered] @{"FirstName"=$fname;"LastName"=$lname;"Age"=$age}
return $OutTable

}

PS>Get-ReallySimple Joe Dokes 32

Name                           Value
—-                                —–
FirstName                     Joe
LastName                     Dokes
Age                               32

Since the result is a hash table we can also return a portion of the table using dot notation. We surround the functional call with parentheses to force the call to be evaluated first.

PS>(Get-ReallySimple Joe Dokes 32).FirstName

Joe

Of course instead of the the parentheses we probably should assign the call to a variable; and then dot notate that.

$Zilch=Get-ReallySimple Joe Dokes 32

$Zilch.FirstName
Joe

There is  an Output Type attribute that can be applied to a function, but this appears to be cosmetic. It doesn’t enforce or do any error checking.  If your function returns a string, and Output type says “hashtable”, nothing happens; the function still return the string.  According to the help documentation the purpose of the Output Type attribute is to provide documentation; but when I queried with Get-Command Get-ReallySimple,  it returned nothing for the output.

Windows Remoting

Remoting allows you to log into the command line of the target machine. It is similar to the SSH command on Linux, in that it provides access only to the command line.

It has to be set up on beforehand by using PowerShell with Admin privileges on both the controlled and controlling machine.

Note:  You will eventually need to the name of your machine. You can find this out with the following command:

Get-ChildItem env:ComputerName

Here are the steps to set it up:  At the PowerShell prompt on each machine

1. Turn on PSRemoting.

PS> Enable-PSRemoting  -Force  # The Force parameter eliminates a lot of annoying questions.

2. For machines that are not on a domain-based nework, you need to configure the trusted hosts lists on each machine.

PS> Set-Item wsman:localhostclienttrustedhosts *

This allows any computer to connect to this machine.  If you want to restrict the trusted hosts, you can use a comma-separated list of IP addresses, or computer names.

3. Restart the WinRM service

PS> Restart-Service WinRM

Repeat the three steps above for any machine that you wish to access via Remoting.

To test the connection:

Note:  You will eventually need to the name of your machine. You can find this out with the following command:  Get-ChildItem env:ComputerName

Test-WsMan <computename>

If the connection is made you’ll get a reply…if not then the connection will appear to hang at the prompt.

Now for the real deal:

To execute a single command from the source machine on the target machine.

Invoke-Command -ComputerName <computername> -ScriptBlock {command } -credential <username>

The credential paraeter will put up a name-password gui box to get your user credentials.

You can start an entire remote session with a similar command

Enter-PSSession -ComputerName <computer> – Credential <username>

This will give you a command line prefaced with the computer name.

[Win7VM] PS >

You can run commands that reside only on the remote computer….or run PS commands on your own computer that execute on the remote computer.

To return to your own machine:

[Win7VM] PS> Exit

Because Remoting runs as a service, you don’t have to have PowerShell open on the remote machine to be able to connect to it.

If you set this up first using a Virtualbox virtual machine, you can see how both ends of the connection work without inconveniencing a user.