Showing posts with label Don Jones. Show all posts
Showing posts with label Don Jones. Show all posts

Sunday, August 19, 2012

Splat in the Hat

In PowerShell the @ character is called the splat operator.  I'm still trying to figure out why it is called splat.
I'm pretty sure that's not why.  Nevertheless the splat operator has many uses in PowerShell.

In a previous post I demonstrated using the splat operator to create a custom object.

$X = New-Object PSObject -Properties @{
     Name="ObjectName";
     SomeValue=1;
     AnotherValue=10
}

The splat operator is also used to define an array.

$Array = @("one","two","three","four")

PowerShell is forgiving and will let you define an array without the splat operator.

$Array = "one","two","three","four"

But if you need to force the results to be an array you can use the splat operator.  I frequently do this if I'm creating an array of custom objects.  I first define an empty array, then fill it with the objects.

$Procs = @()
foreach ($P in (get-process | get-unique)) {
   $New = New-Object PSObject -Property @{
      Name=$P.ProcessName;
      Company=$P.Company;
      Product=$P.Product;
      Version=$P.ProductVersion
   }
   $Procs += $New
}
$Procs



The splat operator can also be used to force the results of a command into an array.  Enclose the command or cmdlet in parentheses and precede the opening parenthesis with the splat operator.
In the above example the variable $X has a length of 843 because it measures the length of the string returned by the Dir command.  $Y and $Z have a length of 1 because it is the length of the array.  The length of the first and only element of those single element arrays matches the length of the string $X.  I included the variable $Z to show another way of forcing a variable to be an array.

The splat operator is also used to create hash tables.  Hash tables are similar to dictionary objects in VBScript but are much more versatile.  A basic hash table lets you add and remove members in addition to doing searches.  To create the hash table start with the splat, enclose the table in curly braces, and separate items with semi-colons.

$Meals = @{"Breakfast"="Eggs";"Lunch"="Sandwich";"Supper"="Chicken"}


Another use for hash tables (and by association our friend the splat operator) is to define a group of parameters for cmdlets.  In this use the keys are the named parameters and the values are the input for the parameters.

Notice that when the hash table is used to provide parameters to the cmdlet the variable name is preceded with the splat operator instead of the dollar sign.  This tells PowerShell to process the variable as a hash table and use the keys and values as parameters to the cmdlet.

This technique is really useful for making code more readable.  The cmdlet Write-EventLog lets you add records to the Windows event logs.  In the example below I use the source WSH because its events have free form messages which allows me to use Get-WinEvent to confirm the results.

Write-EventLog -LogName Application -Source WSH -EventID 0 -EntryType Information -Message "I added this to the event log from PowerShell"


That works but the Write-EventLog cmdlet gets cumbersome with all of the parameters on a single line.  If I splat the parameters in a hash table the code becomes much easier to read and costs very little extra typing.

$ELParams = @{
    'LogName' = 'Application';
    'Source'  = 'WSH';
    'EventId' = 0;
    'EntryType'= 'Information';
    'Message' = 'I splatted this to the Application log using PowerShell'
}
Write-EventLog @ELParams


Note that while I hard coded all of the parameter names and values in the hash table I could just as easily use variables for any of them.  This allows for some sophisticated processing inside loops and user defined functions.

One final use for the splat operator is the Here-String.  A Here-String is PowerShell's facility for specifying a large block of text that spans multiple lines.  Start the bock of text with the splat and quote @" and end the block with a quote and splat "@.  (On a personal note "Quote and Splat" is the law firm who handled my divorce settlement.)  Inside the Here-String you can specify line breaks and include quotes and other special characters.  The Here-String will display just as you specified.


The splat operator gives us a variety of shortcuts to keep our code efficient and tidy.  But remember that with great power comes great responsibility.  So remember to splat wisely, unlike this juvenile.

Commentors:  Seriously, does anybody know why they call it splat?

Monday, July 2, 2012

The shortest distance

Everybody likes shortcuts.  If you can save yourself some time and effort, why not take the opportunity?

Powershell offers several ways to save yourself some typing and shorten your command lines and scripts.  Plus you almost never blow yourself up.

One way is to string together all of the slicing and dicing you might need to do.  If a method for an object returns an array and I know I need a specific element in that array I can specify that element  right after the method invocation.  And I can call a method right from that array element.  Allow me to demonstrate.

In a previous example I had to parse the DistinguishedName for a user.  I model my test domain based on a former employer who managed users and computers by location.  Delegations were done so support staff at each site could manage their objects.  My OU structure looks like this:

In order to find the site name for an arbitrary user account I can assign a variable at each step to parse the DistinguishedName.  In this example I also show the value of the variables to confirm each step:

But I don't need to use all those variables.  This can be processed in a single line.  Below I build up each step.  The last line shows how I combine all of these steps into a single command:

Another handy shortcut is to enclose a command in parentheses and do the same slicing and dicing.  In the above example I don't even need to assign the Get-ADUser output to a variable.  I can use the command
 (Get-ADUser AlAlpha).DistinguishedName.split(',')[2].substring(3)

Letting PowerShell process things in parentheses has many uses and works for all cmdlets.  You can nest some really crazy crap and it will still work.  In the following example I know I have a user in Atlanta with the last name Alpha and I'm not sure what the account name is but I have to change his first name to Allen.  The first command get-aduser -searchbase "OU=Atlanta,OU=Locations,DC=toddp,DC=local" -filter * | where {$_.surname -eq 'Alpha'} shows how I can do this search.  In the second command I enclose that first command in parentheses and use that as the identity for my Set-ADUser command.  I call Get-ADUser one last time to show the change was made in the GivenName attribute.

Another PowerShell shortcut is to use aliases for the cmdlets. 
Here we see all of the aliases that start with the letter "g".  Two aliases I use frequently are gm for Get-Member and gwmi for Get-WmiObject.


In this example the Get-ADUser output is piped through the Get-Member cmdlet using the alias GM.

You can create your own aliases using the Set-Alias cmdlet.
After using Import-Module (or its alias, ipmo) to import the ActiveDirectory cmdlets I use Set-Alias to create the alias gadu for the Get-ADUser command.  Note that an alias can only be for a cmdlet, not a cmdlet and its parameters.  You can work around this limitation by using a function as shown in the last example for Set-Alias.


There are two other aliases that you may encounter.

There is a default alias for ForEach-Object which is the percent sign (%) and an alias for Where-Object which is the question mark (?).  This will come in handy.

How cool is this?  I pipe Get-Process through the ForEach-Object shortcut to pull out the ProcessName and use the Where-Object shortcut to show just the processes starting with the letter "c".  That's good enough for me.

In Powershell 3 there is an implied ForEach-Object for arrays.  Instead of piping the array through ForEach-Object cmdlet or shortcut you just enclose the object in parentheses and process the properties you need.
These are the processes running on my Windows 8 demo VM.  But if I need to grab just the process name I can use the syntax (Get-Process).ProcessName
Pretty sweet.  There are some other handy shortcuts coming in PowerShell 3.

So we have all these nice shortcuts.  Shouldn't we use them all the time?  The rule of thumb from our friend Don Jones is that if you are writing a quick and dirty command for yourself, then the shortcuts can save you time and effort.  But scripts with shortcuts look more cryptic and are harder to read.  So if you are creating a more invovled script that will be used by someone else you should be considerate and make it more accessible by avoiding shortcuts. 

Knowing these shortcuts and aliases (or at least how to find what is being aliased) helps you be more efficient.  And if you come across a script written by someone else who used them you won't be caught off guard

Monday, April 30, 2012

Pipeline Conga Line

http://28.media.tumblr.com/tumblr_l2k058TExA1qzjpn4o1_500.jpg
Unix, DOS, and PowerShell all have the ability to combine commands so the output from one command becomes the input for the next command.  This is called a pipeline because the commands are connected like sections of a pipe.  All three platforms use the vertical bar as the connector which is why that character is sometimes referred to as the pipe.

Unix and DOS also use the less-than sign < to pipe input from a file to a command, the greater-than sign > to pipe output from a command to a text file, and two greater-than signs >> to append to a file.  All of these pipes are useful and you can sometimes do complex things, but because the Unix and DOS pipes are text based there are limitations on what can be accomplished.

PowerShell has powerful cmdlets for getting data to and from files so it only needs the vertical bar pipe.  Because PowerShell is object oriented and the cmdlets are designed to process objects passed from the pipeline some insanely powerful capabilities are available.  Often you can complete a task with a single command line.

So let's stop talking and start dancing.

Get-GPO -All | where {$_.displayname -match 'XenApp5'} | Set-GPPermissions -TargetName 'XAAdmins' -TargetType Group -PermissionLevel GpoEditDeleteModifySecurity

The administrators of our XenApp farm requested access to edit the group policies that manage the farm.  All of the GPOs include 'XenApp5' in the name.  The output from Get-GPO is piped into the Where-Object command to filter out any GPOs that don't have 'XenApp5' in the name.  The resulting objects are piped into the Set-GPPermissions cmdlet to assign the XAAdmins group the permission to edit the GPOs.  And I complete a complex task in a single line.

Most of your pipeline commands will follow this general pattern.  You will run a command or get some input, filter the pipeline to keep only the stuff you want to work on, then process those objects further.  The filters Where-Object and Select-Object are quite powerful but depending on your source you can speed up your scripts by filtering objects at the source.  Don Jones calls this Filter Left, Format Right.

That article also notes that the pipeline processor assumes that every pipeline ends with Out-Default to dump the results to the screen unless another Out-* command is at the end of the pipe.  PowerShell is using the Extended Type System to convert the objects to text and, unless directed otherwise, to make a best guess at how to format the results.  The Out-* commands produce no objects for any comdlets in the pipeline after them to process.  Therefore the Out-* command is always the last command, even if it is the implied Out-Default.

Where else can we send output?
If you are not using the implied Out-Default you will probably use Out-File, although Out-GridView has some handy capabilities.

What options do we have for formatting the data before we pipe it out somewhere?
Depending on the objects PowerShell will automatically Format-List or Format-Table before doing the implied Out-Default.  We have some handy ConvertTo cmdlets to format the data as HTML, CSV, or XML.  There are also the Export-CSV and Export-CliXML that combine the ConvertTo and Out-File operations into a single cmdlet.

So our pipeline conga line dance steps are:
  1. Get some data objects from a file or cmdlet
  2. Filter the data by limiting the number of objects and/or number of attributes
  3. Process the data by sorting or using the data in another cmdlet
  4. Repeat steps 2 and 3 as needed
  5. Format the data if needed
  6. Output the formatted processed data to the screen or file
Something like:

get-wmiobject win32_service | where {$_.StartMode -eq 'Auto'} | select-object name,displayName,PathName,StartName,Status,State | sort-object StartName,displayName

Use Get-WMIObject to find all of the services that are set to start automatically, select the properties of interest, and sort them so services running under the same account are grouped together.

Get-WMIObject does allow us to filter so we only get the WMI objects we want.  Doing so will make the command run faster because we return less data.  I can also format the output into a table so I get more information on the page.

get-wmiobject win32_service -filter "StartMode='Auto'" | select-object name,displayName,PathName,StartName,Status,State | sort-object StartName,displayName | format-table



If I replace Format-Table with Out-Gridview I get the output in an interactive window that lets me further sort and filter the data.


Or I can replace the Out-GridView with a Format-HTML | Out-File and create an .html web page that I can deliver to my PHB so he can open the report in his web browser.









So don't be a PowerShell wallflower.  Get out there and dance.


Commenters: Have you done any crazy cmdlet stacking on the pipeline?  How many cmdlets have you been able to use in a single pipeline?