Introduction to Powershell – for the sake of argument

Recently I was asked to give an introductory training session on the use of Powershell for administration.

The target audience was all IT professionals and the goal was to make them understand why it’s to their benefit to use powershell.

While I am not going to recreate the presentation here (partially because the finished presentation would be the property of my employer), I do want to outline the presentation for posterity so that I can easily come back to the notes for further argument.


Introduction to Windows Powershell


  • An Object Processing Shell
  • From Technet. Nearly verbatim “”
    • Most shells, including Cmd.exe and the SH, KSH, CSH, and BASH UNIX shells, operate by executing a command or utility in a new process, and presenting the results to the user as text. Over the years, many text processing utilities, such as sed, AWK, and PERL, have evolved to support this interaction.
    • These shells also have commands that are built into the shell and run in the shell process, such as the typeset command in KSH and the dir command in Cmd.exe. In most shells, because there are few built-in commands, many utilities have been created.
    • Windows PowerShell is very different.
    • Windows PowerShell does not process text. Instead, it processes objects based on the .NET Framework platform.
    • Windows PowerShell comes with a large set of built-in commands with a consistent interface.
    • All shell commands use the same command parser, instead of different parsers for each tool. This makes it much easier to learn how to use each command.
  • Cmdlets
    • A cmdlet (pronounced “command-let”) is a single-feature command that manipulates objects in Windows PowerShell. You can recognize cmdlets by their name format — a verb and noun separated by a dash (-), such as Get-Help, Get-Process, and Start-Service.
    • In traditional shells, the commands are executable programs that can be simple or complex and that generally return text to the console or perform some operation. For instance, nslookup.exe performs it’s function and returns the result as text to the console.
    •  In Windows PowerShell, most cmdlets are very simple, and they are designed to be used in combination with other cmdlets. For example, the “get” cmdlets only retrieve data, the “set” cmdlets only establish or change data, the “format” cmdlets only format data, and the “out” cmdlets only direct the output to a specified destination.
    • Each cmdlet has a help file that you can access by typing:
    • get-help <cmdlet-name> -detailed
    • The detailed view of a cmdlet help file normaly includes a description of the cmdlet, the command syntax, descriptions of the parameters, and an example that demonstrate use of the cmdlet.
  • A New Scripting Language
    • Windows PowerShell uses its own language, rather than reusing existing languages, for the following reasons:
      • Windows PowerShell needed a language for managing Microsoft .NET Framework objects.
      • The language needed to provide a consistent environment for using cmdlets.
      • The language needed to support complex tasks, without making simple tasks more complex.
      • The language needed to be consistent with higher-level languages used in .NET Framework programming, such as C#.
  • You Need Powershell for some administrative functions already
    • Exchange Server 2007 (and newer) System Center Operations Manager
    • SQL Server 2008 Management Shell
    • Hyper-V administrative functions
    • Vmware introduced PowerCLI in 4.1 and is increasing reliance on it as a way to enumerate the API to a scripting environment
  • CMD.Exe from Powershell
    • You can run Windows command shell (CLI) programs in Windows PowerShell, and you can start Windows programs that have a graphical user interface, such as Notepad and Calculator, at the Windows Powershell prompt. You can also capture the text that Windows programs generate for conventional shells and use that text in Windows PowerShell.
  • Example
    • For example, if we want to gather the default gateway for all interfaces we could use ipconfig.exe from within powershell and manipulate the returned text as an object.
    •  ipconfig /all | select-string –pattern gateway
  • Note the use of the | (pipe).
    • Because powershell returns objects we can perform operations on those objects without instantiating them first.
    •  We simply ‘pipe’ the results to the new operation.
  • What is happening here is the .exe returns text to the shell, but powershell treats that returned text as an object that it can manipulate.
    • We can see this work by instantiating a variable with the result of ipconfig.exe
    • PS> $newObj = ipconfig /all
  • Now we can view the content of $newObj
  • We can also perform operations against the already instantiated object, the same way that we would with the query into the pipeline


  • Why does this matter ?
    • By returning objects instead of text looped results do not have to be parsed and then inserted into properties of an object.
  • Think about how we define objects in Java.
    • A class needs to be defined with some set of properties and methods that we will use as a container for gathered information.
    • That class needs to be compiled and then instantiated as an object during the execution of the code
    • This is not exactly a fair comparison because Java is not really a scripting language
  • So think about how we do this in VBS
    • A variable needs to be instantiated as an object of some predefined class
    • Assuming that we are instantiating the object at every iteration of a loop


  • An Example
    • Let’s say we want to get the last boot time of a list of computers.
    • We would start by writing a script that uses WMI to get the last boot time of the local machine and then we would insert that script into a list that defines the target machines.
  • In VBS

Option Explicit

‘ Declare variables

Dim blnQuiet

Dim intValidArgs

Dim colItems, objItem, objWMIService

Dim strBoot, strBootDate, strBootDay, strBootHour, strBootMins

Dim strBootMonth, strBootTime, strBootYear, strComputer, strMsg, strQuery


intValidArgs = 0


‘ Check arguments

With WScript.Arguments

Select Case .Unnamed.Count

Case 0

‘ If no hostname is specified then local host is assumed (“.”)

‘ This is the same as in Powershell

Set objWMIService = GetObject( “winmgmts://./root/cimv2” )

strQuery = “Select * from Win32_ComputerSystem”

Set colItems = objWMIService.ExecQuery( strQuery, , 48 )

For Each objItem in colItems

strComputer = objItem.Name


Case 1

‘ Potential argument would be a hostname

strComputer = UCase( Wscript.Arguments(0) )

Case Else


End Select

If .Named.Exists( “Q” ) Then

blnQuiet = True

intValidArgs = intValidArgs + 1

End If

If intValidArgs <> .Named.Count Then Syntax

End With


‘Error Handling

‘ Connect to specified computer

Set objWMIService = GetObject( “winmgmts://” & strComputer & “/root/cimv2” )

‘ Display error number and description if applicable

If Err Then ShowError


Set colItems = objWMIService.ExecQuery( “Select * from Win32_OperatingSystem”, , 48 )

For Each objItem in colItems

If blnquiet Then

strMsg = Left( objItem.LastBootUpTime, 12 )


strBootYear  = Left( objItem.LastBootUpTime, 4 )

strBootMonth = Mid( objItem.LastBootUpTime,  5, 2 )

strBootDay   = Mid( objItem.LastBootUpTime,  7, 2 )

strBootDate  = DateValue( strBootDay & “-” & strBootMonth & “-” & strBootYear )

strBootHour  = Mid( objItem.LastBootUpTime,  9, 2 )

strBootMins  = Mid( objItem.LastBootUpTime, 11, 2 )

strBootTime  = strBootHour & “:” & strBootMins

strBoot = strBootDate & “, ” & strBootTime

strMsg  = “Last boot time of ” & strComputer & “: ” & strBoot

End If



‘ Display results

WScript.Echo strMsg



Sub ShowError()

strMsg = vbCrLf & “Error # ” & Err.Number & vbCrLf _

& Err.Description & vbCrLf & vbCrLf & vbCrLf


End Sub


Sub Syntax( )

strMsg = strMsg _

& “LastBoot.vbs,  Version 2.00” _

& vbCrLf _

& “Display last boot time for any WMI enabled computer” _

& vbCrLf & vbCrLf _

& “Usage:  CSCRIPT  //NoLogo  LASTBOOT.VBS  [ computer_name ]  [ /Q ]” _

& vbCrLf & vbCrLf _

& “Where:  “”computer_name””  is an optional remote computer name” _

& vbCrLf _

& ”                         (default is the local computer name)” _

& vbCrLf _

& ”        /Q               outputs date/time only, in YYYYMMDDhhmm format” _

& vbCrLf _

& ”                         (default is message and DD-MM-YYYY, hh:mm format)” _

& vbCrLf & vbCrLf

WScript.Echo strMsg

WScript.Quit 1

End Sub



  • In Powershell
    • [System.Management.ManagementDateTimeconverter]::ToDateTime((gwmi Win32_operatingSystem).LastBootUpTime)
  • Comparison
    • We are doing the same thing in both cases, but it takes much less effort in Powershell
    • We are even using the same .net objects and providers to get this information and format it appropriately
  • Using Powershell means that it becomes realistic to use a CLI to get information that was previously only available through scripts and reporting
    • In this way we gain flexibility with this information and we can alter what we’re looking for on the fly
    • On the fly scripting in VBS was just not realistic with VBS


  • With respect to the script, we’re aren’t quite at our stated goal yet, but let’s take a look at what we’ve done so far.
    • [System.Management.ManagementDateTimeconverter]::ToDateTime((gwmi Win32_operatingSystem).LastBootUpTime)
  • Get-WmiObject
    • Gwmi  is an alias for the command Get-WmiObject
    • We can see that by using the get-alias command
    • Get-alias GWMI


  • Let’s take a look at the Win32_OperatingSystem Class
  • Return a property of the implied object (gwmi Win32_operatingSystem).LastBootUpTime
  • Here we are returning the property ‘LastBootUpTime’ for the implied object that is of the class Win32_OperatingSystem
  • We can break this out further by casting this object as a variable ($newObj)
    • Note that the time string is not in human-readable format
    • We can call native .net libraries also
  • [System.Management.ManagementDateTimeconverter]::ToDateTime((gwmi Win32_operatingSystem).LastBootUpTime)
  • Here we are using the method ‘ToDateTime’ of the class System.Management.ManagementDateTimeConverter
  • We convert the string returned by WMI to a usable format
  • Definitely simpler
  • So we see that Powershell natively returning objects saves us enough typing that it is reasonable to make use of scripts directly from the CLI
  • We still need to meet our initial objective of getting this information from a list of computers.
  • In this case we will get the list from all of the computer objects in Active Directory with a server operating system
  • Start with one computer
  • Get-qadcomputer jcf-edge
    • Note that there are administrative CMDlets  available in windows now, but that they are not available for WinXP
    • Here we are using Quest’s Active Roles CMDlets, but in native AD administrative CMDlets the command would be get-Adcomputer
  • Decide what property to filter on
    • Get-qadcomputer [servername] | fl
  • Get all Computer objects with that Property
    • Get-QADComputer |where{$ –like “*server*”}


  • What is $_
    • $_ is used in a pipeline to represent the passed object.
    • Recall that $[String] is an instantiated variable
    • By Piping to where{$ –like “*server*”} we are saying all of the things in the left hand side of the pipe where their .name property would be like the string *server*.


  • Add the selection into the for each loop
  • foreach($server in get-qadcomputer | ?{$_.OSName -like “*server*”}){


[System.Management.ManagementDateTimeconverter]::ToDateTime((gwmi -computer $ Win32_operatingSystem).LastBootUpTime)

” ”


  • What about the output?
    • So far we’ve only output this data to the console
    • Remember that the data being returned is an object, but we have 2 separate requests in this string. We should create an array object for each record and then create an array of arrays to be returned.
    • Unlike other scripting languages, we do not need to define the empty arrays ahead of time, so this task is nearly trivial
  • Send the output to a property of $record
    • So that’s nice but where did the data go?
    • For each line that would have returned a result to the console, we added it to a property of an object $record.
  • We had to instantiate the object ahead of time because we wanted to have specifically named properties of the object.
    • Before we looped to the next record we added it to the array object $allRecords
    • We did not need to instantiate $allrecords because it only had one object in each record. That’s because each record is still an array.
    • We instantiated it anyhow so that we could ensure that it was empty if we ran this script again
  • The results are all in $allRecords
  • Array Properties
    • Note that the properties of the underlying records are listed properties of the array
  • Records
    • We can address individual records or their properties
    • Export for later
    • Remember that this is an array of objects. We could see this more clearly if we create an array of all of the servers in the previous slide and check the full list (FL) of properties.  $newlist = get-qadserver | ?{$_.Osname –like “*server*”}  and then fl of one record.


  • We’re not sure what to do with this so let’s send the data to csv and come back to it.
    • $allrecords | Export-Csv c:\LastBoot.csv –NoTypeInformation
    • Then we can get the data back into a new array by using import-csv
    • $newArray = import-csv c:\LastBoot.csv
    • This is just one example, but we can export the data in this array out to a .csv file very easily. Then we can come back to this and import the data to a fresh object.



  • Why you may have some trouble initially
    • Powershell is very cognizant of security right out of the box so this makes executing scripts on the fly tricky.
    • I personally set my execution policy to Unrestricted, which is a misnomer. It means that I can execute unsigned scripts but only after acknowledging a prompt.
      • Set-ExecutionPolicy Unrestricted <Enter>
      • he reason this matters is that it sets the execution policy only for the user profile being loaded with powershell
    • You need to learn how to think about collections
      • foreach($service in get-service){Write-Host $service.DisplayName}



Leave a comment

Filed under Active Directry, Powershell

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s