Tuesday, July 8, 2014

Windows boot time, explored in Powershell

In this post I am going to explore most of the existing ways offered by Windows and Powershell to return the timestamp of the last boot.
 
Let's start with saying that there are multiple ways to retrieve this piece of information and exploring them all will make for what I hope is an interesting trip through some Windows internal mechanisms. Be wary by the way that this subject is very wide, and I have allowed myself some small off-topics that are meant to shed some light on the pieces of code we are now so used to see out there.

Windows is booting, but what time is it?
In old times (more or less one to two decades ago) we could (and still we can) use various legacy tools to fetch a Windows computer boot timestamp.
 
In more recent Windows versions some alternatives to those legacy Tools have appeared and we can today use the information stored:
  • in the Performance counters
  • in the Win32_OperatingSystem WMI instance
  • in the Windows event logs
Each of this approach has pros and cons. Some syntaxes are easier and some are trickier. Going through them all will give you an interesting insight of how you can take advantage of Windows Powershell as your all-in-one administration tool. In any case, please notice that the following examples have been prepared and tested on computers with European settings for the date (dd/MM/yyyy). As a consequence those that are based on text parsing or regular expressions might need some correction if you want to run them on a computer with different regional settings.

Let's start from the beginning: there are three possible legacy executables that you can use the retrieve the last reboot date:
  1. systeminfo.exe in C:\Windows\System32
  2. net.exe in C:\Windows\System32
  3. wmic.exe in C:\Windows\System32\wbem
1. System boot time with SYSTEMINFO
 
Systeminfo.exe is the old good tool every Windows admin used to get a quick look at system information. Used in conjunction with another legacy command, FIND, it allows you to see system boot time.

systeminfo | find "System Boot Time:"
System Boot Time:          07/07/2014, 17:41:11
The returned information is of System.String type.
systeminfo |
     find "System Boot Time:" |
     Get-Member |
     Select-Object Typename -Unique

TypeName                                                                                          
--------
System.String
As usual in Powershell, our aim is to return a re-usable object. In this case, better than a System.String, we would like to have a System.DateTime variable. The transformation can be achieved with an easy one-liner (for an explication of which you can refer to the final part my other blog post:

[datetime]::ParseExact($(systeminfo |
     find "System Boot Time:").substring(27,20),"dd/MM/yyyy, HH:mm:ss",$null)

Monday, July 7, 2014, 17:41:11
The main drawback of using systeminfo is that, since it retrieves the list of all the installed patches, it can take quite a long time to be executed, especially on long living systems, and there is nothing you can do to improve that.
 
2.  System boot time with NET

The second legacy executable is net.exe, which has two possible syntaxes, a long one (net statistics server) and a short one (net stats srv), both returning the same result:

net statistics server
Server Statistics for \\SRV01


Statistics since 07/07/2014 17:41:21


Sessions accepted                  0
Sessions timed-out                 0
Sessions errored-out               0

Kilobytes sent                     16
Kilobytes received                 0

Mean response time (msec)          0

System errors                      0
Permission violations              0
Password violations                0

Files accessed                     21
Communication devices accessed     0
Print jobs spooled                 0

Times buffers exhausted

  Big buffers                      0
  Request buffers                  0

The command completed successfully.
Lot of text in the output, as you can see, and the proceeding to get something meaningful out of it is a bit less comfortable, since some regex pattern matching needs to be done (if you have any kind of question do not hesitate to ask in the comment section below):

[datetime]::ParseExact($($(net stats srv) `
     -match "\d{2}/\d{2}/\d{4}").substring(17,19),"dd/MM/yyyy HH:mm:ss",$null)

Monday, July 7, 2014, 17:41:11

3. System boot time with WMIC
 
Third option, WMIC (which, if don't have it yet, you can get with the WMI Administrative Tools). The only thing you need to know for the moment is that WMIC is an alias based tool which interfaces with WMI (Windows Management Instrumentation). While it has many uses, I find it especially useful for querying certain system parameters, like the type and frequency of the processor, such as in 'wmic cpu get Name'.

To get the system boot up time, the syntax is:
wmic os get lastbootuptime

LastBootUpTime
20140707174111.489051+120
To explain the result of this WMIC example, let me start a brief discussion on the ways WMI stores dates and times, which is always good to know if you are a Windows sysadm. The Common Information Model (CIM) standard, where WMI is sitting on, uses the Universal Time Coordinate (UTC) format. Although UTC dates are non-intuitive at first glance, they are relatively easy to convert to a standard date-time format.

Let's have a look at their format, 20140707174111.489051+120, which is easy for humans to translate to yyyymmddHHMMSS.mmmmmmsUUU (full syntax over here)
In technical terms, this is a variant VT_BSTR returned by WMI as a 27 chars string (where the last two characters are blank in the WMIC output).
wmic os get lastbootuptime | Get-Member | Select-Object typename -Unique

TypeName
--------
System.String
Luckily, to convert this string to a valid Datetime, we can rely on the SWbemDateTime object. This is an object which, starting from Windows XP (way old, isn't it?) is used as a helper to parse CIM datetime values, as in the following example I wrote, where I first use a REGEX expression to skip the undesired text, then I force the remaining data into the Value property:
# Convert from VT_BSTR to System.Datetime
$DateTimeObject = New-Object -ComObject WbemScripting.SWbemDateTime 
$DateTimeObject.Value = $($(wmic os get lastbootuptime) `
     -match "\d{14}\.\d{6}\+\d{3}").TrimEnd() #TrimEnd removes the 2 last blank chars
$DateTimeObject.GetVarDate($DateTimeObject.Value)

Monday, July 7, 2014, 17:41:11
$DateTimeObject.GetVarDate($DateTimeObject.Value) |
     Get-Member |
     Select-Object typename -Unique

TypeName : System.DateTime
This was a general introduction to the three golden ways to retrieve the system boot time on a Windows box using legacy executables and returning the result as a DateTime object.

Of course there are other more Powershell-oriented ways to get to this very same result. Let's take the long windy road to that, by starting to introduce some WMI performance class information.

4. An introduction to WMI performance classes

There are two kinds of WMI performance classes: raw counter classes and cooked counter classes (yes, I didn't made it up: 'cooked' is the technical term for them).
The difference? Well, raw counters are simply raw numbers which have gone through no post-processing. Cooked counters, on the other side, are counters supplied by the Formatted Performance Data Provider, which is in charge of supplying calculated counter data.

As an example, take the two performance classes that store the actual uptime, from which, using a simple subtraction, we can make the system boot date:
Get-WmiObject -List Win32_Perf*System | select Name

Name
----
Win32_PerfFormattedData_PerfOS_System
Win32_PerfRawData_PerfOS_System
Both these classes have the same properties with different values, and this can be confusing. But the raw class has two additional information, which you have to use to get the results you expect (and which are pre-cooked in the formatted class) from the raw data:
  • The first is the Timestamp_Object, which is an object-defined timestamp, defined by the provider, storing the current time measure.
  • The second is the Frequency_Object, which represents the frequency, in ticks per second, of the mentioned Timestamp_Object.
5. System boot time from RAW Performance Counters

Wait, ticks you said? You might be wondering what the heck a tick is, and this allows for a nice short discussion on the internals of Microsoft Windows and, more generally, of how a computer works.
 
Inside any CPU there are processor registers which are in charge of storing data used by arithmetic calculations. One of these, named Time Stamp Counter is a 64-bit register present on all x86 processors since the Pentium (remember?) which counts the number of cycles since reset, known as ticks.

Over time a tick has become a constant which is independent from the processor clock, even do tick count is updated at the System clock interval (every 10msec or 15.6 msec on most systems). The value of this constant is 10 millions per second (as stated over here), which gives a tick a duration of 100 ns.

So, each raw measure of the uptime has this Frequency_Object property constantly set at 10 million ticks per second, which is the frequency at which the Timestamp_Object is recorded.
To calculate the system uptime in seconds from this raw data, the following formula should then logically be used:

UptimeInSec = (Timestamp - SystemUptime) / Frequency

where the SystemUptime is another property containing the timestamp at boot expressed in the same unit as Timestamp_Object.

Let's convert this formula to Powershell:

$TimestampAtBoot = Get-WmiObject Win32_PerfRawData_PerfOS_System |
     Select-Object -ExpandProperty systemuptime
$CurrentTimestamp = Get-WmiObject Win32_PerfRawData_PerfOS_System |
     Select-Object -ExpandProperty Timestamp_Object
$Frequency = Get-WmiObject Win32_PerfRawData_PerfOS_System |
     Select-Object -ExpandProperty Frequency_Object
$UptimeInSec = ($CurrentTimestamp - $TimestampAtBoot)/$Frequency
$UptimeInSec

13180,8610781
Fine. We have been able to calculate our system uptime in seconds starting from truly primary data using a short Powershell script. Now let's try to convert this to something more meaningful and show the system boot time as a DateTime like in the previous examples:

(Get-Date) - (New-TimeSpan -seconds $UptimeInSec)

Monday, July 7, 2014, 17:41:11
Here you have your system boot date and time and the type of the returned object is already of DateTime type, so no parsing required. Great!
 
6. System boot time from [Environment]::TickCount

Another way to get your system boot date is to rely on some information exposed by the .NET Environment class. As explained in MSDN, the system timer we talked above behaves in a way that it increments the value of the property TickCount (which stores in a 32-bit signed integer the number of milliseconds elapsed since the system started) going from zero to Int32.MaxValue (where [int32]::MaxValue is 2147483647) for 24.9 days, then jump to Int32.MinValue (where [int32]::MinValue is -2147483648), which is a negative number, and starts incrementing back to zero for the next 24.9 days.

To see the attributes of the TickCount counter, issue:

[Environment]  |
     Select-Object -ExpandProperty DeclaredProperties |
     Where-Object Name -like TickCount
To get the system boot date using [Environment] we have first to get the time span from the number of milliseconds stored in TickCount, then subtract that time interval from the current date and time:

(Get-Date) - ([timespan]::FromMilliseconds([Math]::Abs([Environment]::TickCount)))

Monday, July 7, 2014, 17:41:11
The function [Math]::Abs() is used to return the absolute value of any number, and it is pretty useful here if your tick count is a negative number (which could be the case on systems up from more than 25 days).

7. System boot time from COOKED Performance Counters

Until now we have explained the differences between raw and cooked performance classes and we have seen how to retrieve the system boot time from the current uptime using the New-TimeSpan cmdlet. Time to see how easy is to get the same information using a cooked performance class in conjunction with a time interval.

The class Win32_PerfFormattedData_PerfOS_System contains a number of performance counters related to the operating system, including SystemUpTime, which tells you how many second the machine has been running, while its raw version Win32_PerfRawData_PerfOS_System stored in Systemuptime just a unit showing the time at boot.

(Get-Date) - (New-TimeSpan -Seconds (Get-WmiObject Win32_PerfFormattedData_PerfOS_System).systemuptime)
Lots of different examples to get the same information, isn't it? Now I am going to show you two more way to get the system uptime. One relies on WMI and the other one on the System eventlogs.

8. System boot time from Win32_OperatingSystem

Let's start from the WMI method. All the lessons we learned above on UTC time formatting and conversion operations from VT_BSTR to DATETIME will be re-used in our next examples and will make you more confident with this kind of Powershell game.

For the WMI part, the only class that we need here is Win32_OperatingSystem. The instance of this class keeps a pretty big bunch of information, such as the Manufacturer of your PC, the name of the registered user, or the OS architecture and language. Furthermore there are a few interesting properties containing precious time-oriented information: CurrentTimeZone, InstallDate, LocalDateTime and, can't believe it, LastBootUpTime. As I explained before, WMI dates are returned as strings and not as datetime objects. These strings are compliant with the CIM standards, so to get a valid re-usable System.DateTime object we have first to make some rework.

Two ways to do that.

The first one is, like I showed in the WMIC example, using the SWbemDateTime helper object:

# Convert from VT_BSTR to System.Datetime
$DateTimeObject = New-Object -ComObject WbemScripting.SWbemDateTime 
$DateTimeObject.Value = (Get-WmiObject Win32_OperatingSystem).LastBootUpTime
$DateTimeObject.GetVarDate($DateTimeObject.Value)
The second method relies on the fact that most WMI objects returned by Powershell have a comfortable "ConvertFromDateTime" method implemented out-of-the-box. Cool.
For example:
Get-WmiObject -Class win32_operatingsystem | Get-Member -MemberType ScriptMethod
will return:
ConvertFromDateTime               ScriptMethod  System.Object ConvertFromDateTime();                                                                               
convertToDateTime                 ScriptMethod  System.Object ConvertToDateTime();
Did I already say: "Cool!"?

Let's take advantage of it then:

$OSInfo = Get-WmiObject Win32_OperatingSystem
$OSInfo.ConvertToDateTime($OSInfo.LastBootupTime)
or, if you are a seasoned old school programmer:

$OSInfo  = Get-WmiObject -query "select * from win32_OperatingSystem"
$OSInfo.ConvertToDateTime($OSInfo.LastBootupTime)

For the most curious, there is also a pretty nice ManagementDateTimeConverter Class, which provides methods to convert CIM datetime to System.DateTime:

$OSInfo = Get-WmiObject Win32_OperatingSystem
[Management.ManagementDateTimeConverter]::ToDateTime($OSInfo.lastbootuptime)
That's all for the WMI part.

9. An introduction to Event Tracing for Windows (ETW) 

Time to pass to exploring the information that can be retrieved from your system event logs and see how they compare, but before we see that, and for better understanding, it's interesting to see how the Microsoft Eventing engine has evolved in the past 10 years.

Under Windows NT4 or Windows XP, the Event Log service recorded computer events in three simple logs, like you can see in the following screenshot: System, Security and Application.


Like most Windows developers know (while many administrators have never heard of it), starting with Windows Vista in 2006, the Windows Event Logs has been rewritten on top of the Event Tracing for Windows (ETW) technology, which is a system and software diagnostic, troubleshooting and performance monitoring component.

This new Event Viewer has been completely re-engineered and its appearance has also changed, but it is still familiar enough to not to feel miles from home:


In the new interface there are still a tree pane and a list of events. You can still access the well-known Application, System, and Security logs under the Windows Logs node.
However:
  • some new nodes have been added, such as the Applications and Services logs which is a new category of event logs aimed at storing events from a single application or component rather than systemwide events.
  • the new ForwardedEvents log has been added to the Windows Logs node.
A lot of other Windows components were since then built on top of it, such as Resource Monitor, which allows sysadmins to drill down computer performance much better they could do with Task Manager on older versions.

Version after version, ETW exposes an increasing number of consistent information providers (through the use of a unified event provider model). Those information providers can be retrieved using logman and counted using the Powershell Count() method:

(c:\Windows\System32\logman.exe query providers).count
  • In Windows 8.1 there are 964 providers
  • In Windows 2012 r2 there are 830 providers
  • In Windows 7 there were 668 providers and 513 in Windows 2008 R2
  • In Windows 2003 R2 no more than 10 providers were exposed
Of course these figures increase when additional features or products are installed (such as Active Directory or, say, Hyper-V).

Unlike its predecessors, Windows (starting from Windows NT 4.0/2000/XP) records the system startup and shutdown times in the event log with the following informational Event IDs: 6005,6006,6008,6009.
For what we are trying to achieve, EventID 6005 is the most meaningful, because it occurs when the Event log service is started, just after the system has been booted and Windows has loaded.

In addition to these event IDs, there are some events from the Microsoft-Windows-Kernel-General event provider (exposed by ETW, which we just discussed) which indicate system startup and shutdown time.
You can find deeper information on this event provider as well as on the mentioned events by issuing the following Powershell commands:

c:\Windows\System32\logman.exe query providers Microsoft-Windows-Kernel-General
which can also be used in the form: 

c:\Windows\System32\logman.exe query providers Kernel-General
and:

(Get-WinEvent -listprovider Microsoft-Windows-Kernel-General).events |
     Select-Object Id, Description |
     Format-Table -AutoSize
As you can see in the output, there are thirteen possible events. For calculation our system last boot date, we can optimistically refer to entries tagged with the EventID 12: "The operating system started at system time %7"

Let's focus then on Event ID 12, source Microsoft-Windows-Kernel-General, and Event ID 6005, source EventLog, to see which one gives us the most reliable information on our system boot time.

In Powershell, as you would expect now that you are an accustomed Powershell user, there are six ways to query the Event Logs:
  • Get-WmiObject -Class Win32_NTLogEvent
  • Get-WmiObject -Query "SELECT * FROM Win32_NTLogEvent"
  • Get-EventLog
  • Get-WinEvent -FilterXml
  • Get-WinEvent -FilterHashtable
  • Get-WinEvent -FilterXPath
Let's have a deeper look at each and every one of these possibilities.
 
10. System boot time from Win32_NTLogEvent

First of all, we also have the option of using WMI against the Win32_NTLogEvent class, as shown here:

$Event12 = Get-WmiObject -Class Win32_NTLogEvent `
     -Filter "LogFile = 'System' AND SourceName='Microsoft-Windows-Kernel-General' AND EventCode=12" |
     Select-Object -First 1
$Event12.ConvertToDateTime($Event12.Timegenerated)
and here:

$Event6005 = Get-WmiObject -Class Win32_NTLogEvent `
     -Filter "LogFile = 'System' AND SourceName='EventLog' AND EventCode=6005" |
     Select-Object -First 1
$Event6005.ConvertToDateTime($Event6005.Timegenerated)
or:

$Event12 = Get-WmiObject -Query "SELECT * FROM Win32_NTLogEvent `
     WHERE (logfile='System') AND (SourceName='Microsoft-Windows-Kernel-General') AND (EventCode='12')" |
     Select-Object -First 1
$Event12.ConvertToDateTime($Event12.Timegenerated)
and:

$Event6005 = Get-WmiObject -Query "SELECT * FROM Win32_NTLogEvent `
     WHERE (logfile='System') AND (SourceName='EventLog') AND  (EventCode='6005')" |
     Select-Object -First 1
$Event6005.ConvertToDateTime($Event6005.Timegenerated)

In both cases, the Win32_NTLogEvent WMI class is used to translate instances from the Windows event log, using the same mechanism explained before for Win32_OperatingSystem.
 
11. System boot time from Get-EventLog

Second option (which, let me say, is so Powershell 1.0, and you'll see why in the performance section at the end of the post) is to use Get-EventLog. The main drawback of using Get-EventLog is that it doesn't support much in the way of filtering. That means you have to retrieve all of the entries, and then use Where-Object to filter. Needless to say, it can be pretty CPU and time consuming in case of large logs and can make your System process peak very high. That's why I always suggest to use this cmdlet in conjunction with at least the -After parameters, to shorten the time window.

12, 6005 | % {
     Get-EventLog -LogName System -After 05/05/2012 |
          Where-Object EventId -eq $_ |
          Select-Object EventId, Source, TimeGenerated -First 1
     }
To extract just the TimeGenerated field for Event IDs 12, use:

((Get-EventLog -logname system) | Where-Object {($_.eventid -eq 12) -and ($_.Source -eq 'Microsoft-Windows-Kernel-General') } )[0] | Select-Object -ExpandProperty TimeGenerated
or, for Event IDs 6005:

((Get-EventLog -logname system) | Where-Object {($_.eventid -eq 6005) -and ($_.Source -eq 'EventLog') } )[0] | Select-Object -ExpandProperty TimeGenerated
Notice in both these examples the different syntax I used to index inside the array of the returned results.
 
12. System boot time from Get-WinEvent
 
Third major option is to rely on the powerful Get-WinEvent cmdlet, which was introduced in Powershell 2.0 in 2009.
It does support three types of built-in filtering (making it lighting fast) and it can query the newer log types introduced since Vista/Win2008, so it should be preferred to Get-EventLog.
These three types of filtering are:
    -FilterHashtable 
        
        Required?                    true
        Position?                    0
        Accept pipeline input?       false
        Parameter set name           HashQuerySet
        Aliases                      None
        Dynamic?                     false
        
    -FilterXPath 
        
        Required?                    false
        Position?                    Named
        Accept pipeline input?       false
        Parameter set name           FileSet, GetProviderSet, GetLogSet
        Aliases                      None
        Dynamic?                     false
        
    -FilterXml <xml>
        
        Required?                    true
        Position?                    0
        Accept pipeline input?       false
        Parameter set name           XmlQuerySet
        Aliases                      None
        Dynamic?                     false
FilterXml takes in a XML-style string of text. This is the example for EventID 6005:

$xml6005='<QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name="EventLog"] and (EventID=6005)]]</Select></Query></QueryList>'
Get-WinEvent -FilterXml $xml6005 -MaxEvents 1 | Select-Object -ExpandProperty TimeCreated
and this is the example for EventID 12:

$xml12='<QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name="Microsoft-Windows-Kernel-General"] and (EventID=12)]]</Select></Query></QueryList>'
Get-WinEvent -FilterXml $xml12 -MaxEvents 1 | Select-Object -ExpandProperty TimeCreated
In both cases the returned result is of System.DateTime type. What else?

FilterHashtable takes in a hashtable containing three properties in our case: LogName, Providername and ID:

Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='Microsoft-Windows-Kernel-General'; ID=12} -MaxEvents 1 | Select-Object -ExpandProperty TimeCreated
Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='EventLog'; ID=6005} -MaxEvents 1 | Select-Object -ExpandProperty TimeCreated
Last option is to use FilterXPath. To make a long story short, XPath is a language used to query specific nodes from an XML document. If you don't use it often, its syntax can look quit scaring. But I am here to help.

For event 12 you have:

(Get-WinEvent -LogName "System" -MaxEvents 1 -FilterXPath "*[System[Provider[@Name='Microsoft-Windows-Kernel-General']][EventID=12]]").TimeCreated
and for event 6005 you have:

(Get-WinEvent -LogName "System" -MaxEvents 1 -FilterXPath "*[System[Provider[@Name='EventLog']][EventID=6005]]").TimeCreated
This was the last example I wanted to use to show you how to find out the system boot up time and date. To resume, there are 23 possible choices, and it could be difficult to choose the right tool for the job. That's why I have decided that this post wouldn't be complete without a performance analysis.
 
13. Performance
 
Performancewise, any of the above solution has its execution time depending on the information provider. That's why I have tested all of them with Measure-Command on a Windows 2012 R2 and on a Windows 8.1.  The resulting duration is expressed in seconds (from faster to slower):

Duration Title                                   Result              ResultType     
-------- -----                                   ------              ----------     
0,0393308 TickCount + TimeSpan + Get-Date         07/07/2014 17:41:11 System.DateTime
0,0417487 WMI query + SWbemDateTime               07/07/2014 17:41:11 System.DateTime
0,0429207 WMI + ManagementDateTimeConverter       07/07/2014 17:41:11 System.DateTime
0,0459907 Net Statistics Server                   07/07/2014 17:41:21 System.DateTime
0,0484394 WMI + SWbemDateTime                     07/07/2014 17:41:11 System.DateTime
0,0512981 Net Stats Srv                           07/07/2014 17:41:21 System.DateTime
0,0775581 WMI Query 6005 + ConvertToDateTime      07/07/2014 17:41:19 System.DateTime
0,0796125 WMI + ConvertToDateTime                 07/07/2014 17:41:11 System.DateTime
0,0860807 WMI filter 6005 + ConvertToDateTime     07/07/2014 17:41:19 System.DateTime
0,1026782 WMI filter 12 + ConvertToDateTime       07/07/2014 17:41:11 System.DateTime
0,1307235 WMIC + SWbemDateTime                    07/07/2014 17:41:11 System.DateTime
0,2467589 Get-WinEvent + FilterXML 12             07/07/2014 17:41:11 System.DateTime
0,2678846 Get-WinEvent + FilterXPath 6005         07/07/2014 17:41:19 System.DateTime
0,2697007 Get-WinEvent + FilterXPath 12           07/07/2014 17:41:11 System.DateTime
0,2825668 Get-WinEvent + FilterHastTable 6005     07/07/2014 17:41:19 System.DateTime
0,2962874 Get-WinEvent + FilterHastTable 12       07/07/2014 17:41:11 System.DateTime
0,2978704 PerfFormattedData + TimeSpan + Get-Date 07/07/2014 17:41:11 System.DateTime
0,3042627 WMI Query 12 + ConvertToDateTime        07/07/2014 17:41:11 System.DateTime
0,4015614 Get-WinEvent + FilterXML 6005           07/07/2014 17:41:19 System.DateTime
2,7686521 PerfRawData + TimeSpan + Get-Date       07/07/2014 17:41:11 System.DateTime
3,5873439 Systeminfo + ParseExact                 07/07/2014 17:41:11 System.DateTime
12,3051242 Get-EventLog 6005 + Where-Object        07/07/2014 17:41:19 System.DateTime
35,5054786 Get-EventLog 12 + Where-Object          07/07/2014 17:41:11 System.DateTime

The result is self-explanatory:
  • Get-EventLog is to be considered deprecated
  • Systeminfo is pretty time consuming, since it fetches all hotfix information
  • All the others are pretty fast
  • [Environment]::TickCount has jaw-dropping performance
14. Precision

Not all the Tools return the same result. There are three main groups:
  1. those that fetch the system boot date from WMI and those that fetch Event 12 as asynchronously provided from the ETW Kernel-General provider and stored in the System eventlog. This is the most accurate information, and, as I was able to verify, returns the exact time your Windows computer bootstraps after POST.
  2. those that fetch Event 6005, which is less accurate, because it returns the date and time the Event Log service has started and that is 8 seconds late on Kernel-provided information.
  3. those that are based on Network Statistics, which are returning the date and time since when the Server service has started. In this case there is a huge 10 seconds delay from what's being returned by Kernel-General.
Here's the complete result ordered by boot time:

Duration Title                                   Result              ResultType     
  -------- -----                                   ------              ----------     
 3,5873439 Systeminfo + ParseExact                 07/07/2014 17:41:11 System.DateTime
35,5054786 Get-EventLog 12 + Where-Object          07/07/2014 17:41:11 System.DateTime
 0,1307235 WMIC + SWbemDateTime                    07/07/2014 17:41:11 System.DateTime
 0,0484394 WMI + SWbemDateTime                     07/07/2014 17:41:11 System.DateTime
 2,7686521 PerfRawData + TimeSpan + Get-Date       07/07/2014 17:41:11 System.DateTime
 0,0393308 TickCount + TimeSpan + Get-Date         07/07/2014 17:41:11 System.DateTime
 0,2978704 PerfFormattedData + TimeSpan + Get-Date 07/07/2014 17:41:11 System.DateTime
 0,0796125 WMI + ConvertToDateTime                 07/07/2014 17:41:11 System.DateTime
 0,0417487 WMI query + SWbemDateTime               07/07/2014 17:41:11 System.DateTime
 0,0429207 WMI + ManagementDateTimeConverter       07/07/2014 17:41:11 System.DateTime
 0,3042627 WMI Query 12 + ConvertToDateTime        07/07/2014 17:41:11 System.DateTime
 0,1026782 WMI filter 12 + ConvertToDateTime       07/07/2014 17:41:11 System.DateTime
 0,2467589 Get-WinEvent + FilterXML 12             07/07/2014 17:41:11 System.DateTime
 0,2962874 Get-WinEvent + FilterHastTable 12       07/07/2014 17:41:11 System.DateTime
 0,2697007 Get-WinEvent + FilterXPath 12           07/07/2014 17:41:11 System.DateTime
 0,0860807 WMI filter 6005 + ConvertToDateTime     07/07/2014 17:41:19 System.DateTime
 0,0775581 WMI Query 6005 + ConvertToDateTime      07/07/2014 17:41:19 System.DateTime
12,3051242 Get-EventLog 6005 + Where-Object        07/07/2014 17:41:19 System.DateTime
 0,4015614 Get-WinEvent + FilterXML 6005           07/07/2014 17:41:19 System.DateTime
 0,2825668 Get-WinEvent + FilterHastTable 6005     07/07/2014 17:41:19 System.DateTime
 0,2678846 Get-WinEvent + FilterXPath 6005         07/07/2014 17:41:19 System.DateTime
 0,0512981 Net Stats Srv                           07/07/2014 17:41:21 System.DateTime
 0,0459907 Net Statistics Server                   07/07/2014 17:41:21 System.DateTime
15. Conclusions

All in all, taking into consideration speed of execution and precision, the syntax to remember is:

(Get-Date) - ([timespan]::FromMilliseconds([Math]::Abs([Environment]::TickCount)))
I hope you have enjoyed this post. If you do, do not hesitate to leave a comment and to share. Also, if you want to suggest any modification, correction or improvement, or should you need any help with the code, feel free to get in touch.

Thursday, July 3, 2014

How to open the Powershell ISE in Windows 8.1

More or less one week ago I have started using the latest Windows 8.1 version on two test laptops . As you can imagine my first reflex was to fire Powershell, which is pretty easy: I brought up the Start Screen and saw that Powershell was well there while the Integrated Scripting Environment (ISE) was missing from the Application menu:


Where is the Powershell ISE gone?
I was kind of surprised but I knew for sure that Windows Powershell 4.0 as well as the ISE are installed by default on the desktop version of the OS:

PS C:\Users\Carlo> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      4.0
WSManStackVersion              3.0
SerializationVersion           1.1.0.1
CLRVersion                     4.0.30319.34014
BuildVersion                   6.3.9600.17090
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0}
PSRemotingProtocolVersion      2.2

I quickly understood that only PowerShell is visible under Apps. The ISE is somewhat hidden inside the Admnistrative Tools. There are three solutions to solve this.

The first one is to launch a classic Powershell than rely on the ISE alias to open the ISE:

PS C:\Users\Carlo> Get-Command ise

CommandType     Name
-----------     ----
Alias           ise -> powershell_ise.exe

The second solution is to bring up the Search screen and type powershell_ise then press Enter:


The third solution is to open the Windows settings (by moving you mouse pointer in the top right corner of your screen), and then select Tiles under the Settings section. To unhide the Administrative Tools, move the slider to the Yes position:


Hope this helps.

Sunday, June 8, 2014

First look at Powershell 5.0

Last time I wrote on this blog, Powershell 4 was king and it boasted more than 1300 cmdlets on its Windows 2012 R2 platform:
PS C:\Users\carlo> Get-Command| measure


Count    : 1301
Average  :
Sum      :
Maximum  :
Minimum  :
Property :
There were more than 60 modules, covering most of the aspects of a server configuration:
Get-Module -ListAvailable

    Directory: C:\Windows\system32\WindowsPowerShell\v1.0\Modules


ModuleType Version    Name                                ExportedCommands                                             
---------- -------    ----                                ----------------                                             
Manifest   2.0.0.0    AppLocker                           {Get-AppLockerFileInformation, Get-Ap
Manifest   2.0.0.0    Appx                                {Add-AppxPackage, Get-AppxPackage, Ge
Manifest   1.0        BestPractices                       {Get-BpaModel, Get-BpaResult, Invoke-
Manifest   1.0.0.0    BitsTransfer                        {Add-BitsFile, Complete-BitsTransfer,
Manifest   1.0.0.0    BranchCache                         {Add-BCDataCacheExtension, Clear-BCCa
Manifest   1.0.0.0    CimCmdlets                          {Get-CimAssociatedInstance, Get-CimCl
Manifest   1.0.0.0    DirectAccessClientComponents        {Disable-DAManualEntryPointSelection,
Script     3.0        Dism                                {Add-AppxProvisionedPackage, Add-Wind
Manifest   1.0.0.0    DnsClient                           {Resolve-DnsName, Clear-DnsClientCach
Manifest   2.0.0.0    International                       {Get-WinDefaultInputMethodOverride, S
Manifest   1.0.0.0    iSCSI                               {Get-IscsiTargetPortal, New-IscsiTarg
Manifest   2.0.0.0    IscsiTarget                         {Add-ClusteriSCSITargetServerRole, Ad
Script     1.0.0.0    ISE                                 {New-IseSnippet, Import-IseSnippet, G
Manifest   1.0.0.0    Kds                                 {Add-KdsRootKey, Get-KdsRootKey, Test
Manifest   3.0.0.0    Microsoft.PowerShell.Diagnostics    {Get-WinEvent, Get-Counter, Import-Co
Manifest   3.0.0.0    Microsoft.PowerShell.Host           {Start-Transcript, Stop-Transcript}
Manifest   3.1.0.0    Microsoft.PowerShell.Management     {Add-Content, Clear-Content, Clear-It
Manifest   3.0.0.0    Microsoft.PowerShell.Security       {Get-Acl, Set-Acl, Get-PfxCertificate
Manifest   3.1.0.0    Microsoft.PowerShell.Utility        {Format-List, Format-Custom, Format-T
Manifest   3.0.0.0    Microsoft.WSMan.Management          {Disable-WSManCredSSP, Enable-WSManCr
Manifest   1.0        MMAgent                             {Disable-MMAgent, Enable-MMAgent, Set
Manifest   1.0.0.0    MsDtc                               {New-DtcDiagnosticTransaction, Comple
Manifest   2.0.0.0    NetAdapter                          {Disable-NetAdapter, Disable-NetAdapt
Manifest   1.0.0.0    NetConnection                       {Get-NetConnectionProfile, Set-NetCon
Manifest   1.0.0.0    NetEventPacketCapture               {New-NetEventSession, Remove-NetEvent
Manifest   2.0.0.0    NetLbfo                             {Add-NetLbfoTeamMember, Add-NetLbfoTe
Manifest   1.0.0.0    NetNat                              {Get-NetNat, Get-NetNatExternalAddres
Manifest   2.0.0.0    NetQos                              {Get-NetQosPolicy, Set-NetQosPolicy, 
Manifest   2.0.0.0    NetSecurity                         {Get-DAPolicyChange, New-NetIPsecAuth
Manifest   1.0.0.0    NetSwitchTeam                       {New-NetSwitchTeam, Remove-NetSwitchT
Manifest   1.0.0.0    NetTCPIP                            {Get-NetIPAddress, Get-NetIPInterface
Manifest   1.0.0.0    NetworkConnectivityStatus           {Get-DAConnectionStatus, Get-NCSIPoli
Manifest   1.0.0.0    NetworkTransition                   {Add-NetIPHttpsCertBinding, Disable-N
Manifest   1.0        NFS                                 {Get-NfsMappedIdentity, Get-NfsNetgro
Manifest   1.0.0.0    PcsvDevice                          {Get-PcsvDevice, Start-PcsvDevice, St
Manifest   1.0.0.0    PKI                                 {Add-CertificateEnrollmentPolicyServe
Manifest   1.1        PrintManagement                     {Add-Printer, Add-PrinterDriver, Add-
Binary     1.0        PSDesiredStateConfiguration         {Set-DscLocalConfigurationManager, St
Script     1.0.0.0    PSDiagnostics                       {Disable-PSTrace, Disable-PSWSManComb
Binary     1.1.0.0    PSScheduledJob                      {New-JobTrigger, Add-JobTrigger, Remo
Manifest   2.0.0.0    PSWorkflow                          {New-PSWorkflowExecutionOption, New-P
Manifest   1.0.0.0    PSWorkflowUtility                   Invoke-AsWorkflow
Manifest   2.0.0.0    RemoteDesktop                       {Get-RDCertificate, Set-RDCertificate
Manifest   1.0.0.0    ScheduledTasks                      {Get-ScheduledTask, Set-ScheduledTask
Manifest   2.0.0.0    SecureBoot                          {Confirm-SecureBootUEFI, Set-SecureBo
Script     1.0.0.0    ServerCore                          {Get-DisplayResolution, Set-DisplayRe
Script     2.0.0.0    ServerManager                       {Get-WindowsFeature, Install-WindowsF
Cim        1.0.0.0    ServerManagerTasks                  {Get-SMCounterSample, Get-SMPerforman
Manifest   2.0.0.0    SmbShare                            {Get-SmbShare, Remove-SmbShare, Set-S
Manifest   2.0.0.0    SmbWitness                          {Get-SmbWitnessClient, Move-SmbWitnes
Manifest   1.0.0.0    SoftwareInventoryLogging            {Get-SilComputer, Get-SilSoftware, Ge
Manifest   1.0.0.0    StartScreen                         {Export-StartLayout, Import-StartLayo
Manifest   2.0.0.0    Storage                             {Add-InitiatorIdToMaskingSet, Add-Par
Manifest   2.0.0.0    TLS                                 {New-TlsSessionTicketKey, Enable-TlsS
Manifest   1.0.0.0    TroubleshootingPack                 {Get-TroubleshootingPack, Invoke-Trou
Manifest   2.0.0.0    TrustedPlatformModule               {Get-Tpm, Initialize-Tpm, Clear-Tpm, 
Manifest   1.0.0.0    UserAccessLogging                   {Enable-Ual, Disable-Ual, Get-Ual, Ge
Manifest   2.0.0.0    VpnClient                           {Add-VpnConnection, Set-VpnConnection
Manifest   1.0.0.0    Wdac                                {Get-OdbcDriver, Set-OdbcDriver, Get-
Manifest   2.0.0.0    Whea                                {Get-WheaMemoryPolicy, Set-WheaMemory
Manifest   1.0.0.0    WindowsDeveloperLicense             {Get-WindowsDeveloperLicense, Show-Wi
Script     1.0        WindowsErrorReporting               {Enable-WindowsErrorReporting, Disabl
Manifest   1.0.0.0    WindowsSearch                       {Get-WindowsSearchSetting, Set-Window
Starting from last May, Microsoft has published a preview release of the fifth version of its Management Framework (which corresponds to KB2894868), which includes Windows PowerShell 5 and brings updates to Desired State Configuration (DSC) as well as three new modules: OneGet, PowerShellGet, and NetworkSwitch.

The Microsoft download page gives you the possibility to download the framework for X86 and x64 computers. Each download is around 5 MBs:
For now these updates install only on Windows 8.1 or Windows 2012 R2, as stated in the release notes.

The MSU to use for Windows 2012 R2 is named WindowsBlue-KB2894868-x64.

Before the installation of the WMF 5.0, note that Windows 2012 R2 comes with the following versions of its components out-of-the-box:
PS C:\Users\carlo> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      4.0
WSManStackVersion              3.0
SerializationVersion           1.1.0.1
CLRVersion                     4.0.30319.34014
BuildVersion                   6.3.9600.16394
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0}
PSRemotingProtocolVersion      2.2
Let's go through the easy upgrade process to Powershell 5.0:
Start the installer...

Wait for it...
Done!
After the installation completes, a restart is required to apply the system changes.

Then when Powershell starts, you can see that you have moved to version 5 just looking at the date as it appears in the logo: if it shows 2014 then you have Powershell 5.0. If it shows 2013 then you have Powershell 4.0.
Also the output of $PSVersionTable shows us new values:
PS C:\Users\carlo> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.0.9740.0
WSManStackVersion              3.0
SerializationVersion           1.1.0.1
CLRVersion                     4.0.30319.34014
BuildVersion                   6.3.9740.0
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion      2.2
With Powershell 5.0 some new cmdlets appear straight away:
PS C:\Users\carlo> Get-Command | measure


Count    : 1331
Average  :
Sum      :
Maximum  :
Minimum  :
Property :
Beside a refresh of Desired State Configuration (DSC) (you can read the complete post by Ravikanth Chaganti on PowershellMagazine), there are 30 new cmdlets, included in three new modules: OneGet, PowershellGet and NetworkSwitch:
PS C:\Users\carlo> gcm -Module NetworkSwitch

CommandType     Name                                               Source
-----------     ----                                               ------
Function        Disable-NetworkSwitchEthernetPort                  NetworkSwitch
Function        Disable-NetworkSwitchFeature                       NetworkSwitch
Function        Disable-NetworkSwitchVlan                          NetworkSwitch
Function        Enable-NetworkSwitchEthernetPort                   NetworkSwitch
Function        Enable-NetworkSwitchFeature                        NetworkSwitch
Function        Enable-NetworkSwitchVlan                           NetworkSwitch
Function        Get-NetworkSwitchEthernetPort                      NetworkSwitch
Function        Get-NetworkSwitchFeature                           NetworkSwitch
Function        Get-NetworkSwitchGlobalData                        NetworkSwitch
Function        Get-NetworkSwitchVlan                              NetworkSwitch
Function        New-NetworkSwitchVlan                              NetworkSwitch
Function        Remove-NetworkSwitchEthernetPortIPAddress          NetworkSwitch
Function        Remove-NetworkSwitchVlan                           NetworkSwitch
Function        Restore-NetworkSwitchConfiguration                 NetworkSwitch
Function        Save-NetworkSwitchConfiguration                    NetworkSwitch
Function        Set-NetworkSwitchEthernetPortIPAddress             NetworkSwitch
Function        Set-NetworkSwitchPortMode                          NetworkSwitch
Function        Set-NetworkSwitchPortProperty                      NetworkSwitch
Function        Set-NetworkSwitchVlanProperty                      NetworkSwitch

PS C:\Users\carlo> gcm -Module OneGet

CommandType     Name                                               Source
-----------     ----                                               ------
Cmdlet          Add-PackageSource                                  OneGet
Cmdlet          Find-Package                                       OneGet
Cmdlet          Get-Package                                        OneGet
Cmdlet          Get-PackageSource                                  OneGet
Cmdlet          Install-Package                                    OneGet
Cmdlet          Remove-PackageSource                               OneGet
Cmdlet          Uninstall-Package                                  OneGet

PS C:\Users\carlo> gcm -Module PowerShellGet

CommandType     Name                                               Source
-----------     ----                                               ------
Function        Find-Module                                        PowerShellGet
Function        Install-Module                                     PowerShellGet
Function        Publish-Module                                     PowerShellGet
Function        Update-Module                                      PowerShellGet
These three new modules confirm Powershell as being the 'glue' which Microsoft wants to push in the limelight as the key tool for a holistic view of the DataCenter, as stated in the DAL:

"The datacenter abstraction layer (DAL) is our work with the industry to provide a common management abstraction for all the resources of a data center to make it simple and easy to adopt and deploy cloud computing. The DAL is not specific to one operating system; it benefits UNIX cloud-computing efforts every bit as much as Windows.

The DAL uses the existing DMTF standards-based management stack to manage all the resources of a data center: physical servers, storage devices, networking devices, hypervisors, operating systems, application frameworks, services, and applications. The DAL is different than other cloud efforts in that it uses - and builds on - proven management stack technologies, instead of inventing new ones."

As a matter of fact, for the moment there is no real tutorial for the usage of these network switch management Cmdlets:
PS C:\Users\carlo> man Get-NetworkSwitchVlan -Online
Get-Help : The online version of this Help topic cannot be displayed because the Internet address (URI) of the Help
topic is not specified in the command code or in the help file for the command.
At line:55 char:7
+       Get-Help @PSBoundParameters | more
+       ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Get-Help], PSInvalidOperationException
    + FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.GetHelpCommand
While this is fully understandable (this is a Preview), I think that this reduces the explorability of those cmdlets and does not encourage IT staff to give them a try for the moment.

What it's clear reading the syntax instructions is that those cmdlets rely on CIM and then to manage the Layer 2 switches you will need to create CIM session connections:
PS C:\Users\carlo> man Get-NetworkSwitchVlan -full

NAME
    Get-NetworkSwitchVlan

SYNTAX
    Get-NetworkSwitchVlan -CimSession  [-Name ]  []

    Get-NetworkSwitchVlan -CimSession  -VlanId   []

    Get-NetworkSwitchVlan -CimSession  -InstanceId   []

    Get-NetworkSwitchVlan -CimSession  -Caption   []

    Get-NetworkSwitchVlan -CimSession  -Description   []
The second Powershell improvement that comes with the WMF 5.0 is the introduction of OneGet module, which can be used to download packages from Chocolatey. The first time you run the Find-Package cmdlet, it asks for the permission to download NuGet:
Just from a first look to the previous screenshot, I can see that this is going to be funny! Just look at the following command:
PS C:\Users\carlo> Find-Package antivirus

Name                             Version          Status           Source
----                             -------          ------           ------
ad-awarefreeantivirus            11.2.5952.0      Available        chocolatey
avastfreeantivirus               9.0.2018         Available        chocolatey
AvgAntivirusBusiness             2013.3349        Available        chocolatey
avgantivirusfree                 14.0.4592        Available        chocolatey
avirafreeantivirus               14.0.3.338       Available        chocolatey
This is simply great! I can choose an antivirus to install and get it on my test machine using Powershell. What else?

Let's proceed with the installation of Avast!. The process is a breeze:
Install-Package avastfreeantivirus
As you can see in Resource Monitor, Powershell itself is downloading the source file and saving it to C:\Users\carlo\AppData\Local\Temp\2\Microsoft.OneGet.Utility\2\chocolatey\avastfreeantivirus
In my case the installation failed because Avast! can't be installed on a Windows Server version. So Find-Package does not shows us only packages which are available for our platform but all of them and it's up to the admin to choose the right ones to install:
Interesting enough, OneGet gives the possibility to install the Powershell Module for Azure. Nice shot!
PS C:\Users\carlo> find-package azure

Name                             Version          Status           Source         Summary
----                             -------          ------           ------         -------
AzureBuildSDKvs2012              2.2.2            Available        chocolatey     Azure
AzureConnectDnsSync              1.0.0            Available        chocolatey
AzureStorageExplorer             5.0.0.0          Available        chocolatey     Azure
Boxstarter.Azure                 2.4.15           Available        chocolatey
CloudBerryExplorer.AzureStorage  2.1.0.37         Available        chocolatey     Cloud
Dogtail.AzureSDK                 11.2.13230.3     Available        chocolatey
Dogtail.AzureSDK.2.1.VisualSt... 11.2.13230.3     Available        chocolatey
Sogeti.WindowsAzure.Plugins.E... 1.0.0            Available        chocolatey     A Win
WindowsAzureLibrariesForNet      2.2.20140119     Available        chocolatey
WindowsAzureLibsForNet           2.2.20140128     Available        chocolatey
WindowsAzurePowershell           0.8.0            Available        chocolatey
Let's see if can make it through this time:
PS C:\Users\carlo> Install-Package windowsazurepowershell

Installing Package 'WindowsAzurePowershell' from untrusted source
WARNING: This package source is not marked as safe. Are you sure you want to install software from 'chocolatey'
[Y] Yes  [N] No  [S] Suspend  [?] Help (default is "Y"):

Name                             Version          Status           Source         Summary
----                             -------          ------           ------         -------
DotNet4.5                        4.5.20120822     Installed        chocolatey     The Microsoft .NET Framework 4.5
Install-Package : Process Failed:msiexec.exe
At line:1 char:1
+ Install-Package windowsazurepowershell
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (Microsoft.Power....InstallPackage:InstallPackage) [Install-Package],
   Exception
    + FullyQualifiedErrorId : errorid,Microsoft.PowerShell.OneGet.CmdLets.InstallPackage
The good thing is that Install-Package detects a dependency from DotNet 4.5 and gets this package too.
The bad thing is the installation fails (probably because OneGet and Chocolatey do not handle well the transition to msiexec), and that the failed package is wrongly shown as properly installed:
PS C:\Users\carlo> Get-Package

Name                             Version          Status           Source
----                             -------          ------           ------
avastfreeantivirus               9.0.2018         Installed        Local File
DotNet4.5                        4.5.20120822     Installed        Local File
WindowsAzurePowershell           0.8.0            Installed        Local File
Let's try again with more standard applications: Notepad++ and Vlc:
PS C:\Users\carlo> Install-Package notepadplusplus,vlc

Installing Package 'notepadplusplus' from untrusted source
WARNING: This package source is not marked as safe. Are you sure you want to install software from 'chocolatey'
[Y] Yes  [N] No  [S] Suspend  [?] Help (default is "Y"):

Name                             Version          Status           Source
----                             -------          ------           ------
notepadplusplus.install          6.6.4            Installed        chocolatey
notepadplusplus                  6.6.4            Installed        chocolatey
vlc                              2.1.3            Installed        chocolatey
This one worked, since I can see Notepad++ and Vlc in the list of the installed Apps. Good.
So, concerning OneGet, I can see that, while experimental, this is pretty wonderful stuff. In the future it would be nice to have an option to update installed packages when a new version becomes available, but, nonetheless, this is a great improvement to Powershell as a management language.

To complete this post, let me mention that the PowerShell Team also added the PowerShellGet module in the May 2014 update to the v5.0 Preview. It comes with an official Microsoft repository for pulling in the latest modules. The first time you use it, it ask for the permission to install NuGet.exe:
PS C:\Users\carlo> find-module

NuGet.exe is required to continue.
PowerShellGet requires NuGet.exe to interact with NuGet based galleries. NuGet.exe must be available in
'C:\Users\carlo\AppData\Local\Microsoft\Windows\PowerShell\PowerShellGet'. For more information about NuGet, see
http://www.nuget.org. Do you want PowerShellGet to download NuGet.exe to
'C:\Users\carlo\AppData\Local\Microsoft\Windows\PowerShell\PowerShellGet' now?
[Y] Yes  [N] No  [S] Suspend  [?] Help (default is "Y"):

Version         Name                                     DateUpdated               Description
-------         ----                                     -----------               -----------
1.1.0.0         AutoVars                                 6/6/2014 11:38:07 PM      Allows for t
2.0             BetterCredentials                        6/6/2014 4:28:03 PM       A (compatibl
5.0             Bing                                     6/6/2014 4:28:03 PM       A few functi
1.0.0.0         CimInventory                             6/6/2014 4:28:03 PM       Module that
6.0             ConversionModule                         6/6/2014 4:28:03 PM       a module tha
1.0             EWS                                      6/8/2014 12:33:11 PM      Module to en
2.1.0.2         FAQ                                      6/6/2014 4:28:03 PM       A Frequently
1.0             GenericMethods                           6/6/2014 4:28:03 PM       The Invoke-G
1.2.0.0         HardwareManagement                       6/6/2014 4:28:03 PM       Out-of-band
1.1             IEFavorites                              6/6/2014 4:28:03 PM       Used to mana
1.0             InlineMailAttachments                    6/6/2014 4:28:03 PM       This module
0.1.0.0         ISEGit                                   6/6/2014 11:48:07 PM      Module to wo
3.0             LocalUserManagement                      6/6/2014 11:28:07 PM      a module tha
1.0.0.5         LockObject                               6/6/2014 4:28:03 PM       Lock-Object
1.0.0.0         MyDefaults                               6/6/2014 11:38:07 PM      Sets and get
1.4             myModule                                 6/6/2014 4:28:03 PM       My PowerShel
1.0.0.2         poke                                     6/6/2014 4:28:03 PM       A PowerShell
1.0             Posh-Shodan                              6/6/2014 4:28:03 PM       Module for i
1.2             Posh-VirusTotal                          6/5/2014 12:28:40 AM      PowerShell m
1.0             PoshInternals                            6/5/2014 12:28:40 AM      Collection o
1.0             PoshNet                                  6/5/2014 4:39:40 PM       Get-DNS is "
2.2.1           PoshWSUS                                 6/5/2014 4:39:40 PM       PowerShell m
3.6             PowerBot                                 6/5/2014 12:28:40 AM      PowerBot: th
0.4             PowerEvents                              6/5/2014 4:34:40 PM       PowerEvents
1.2             PowerShellCookbook                       6/8/2014 1:03:09 AM       Sample scrip
1.2             PowerShellHumanizer                      6/5/2014 4:39:40 PM       PowerShell H
4.0             PowerShellISEModule                      6/6/2014 6:23:03 PM       a module tha
1.1.0.1         PowerShellLogging                        6/5/2014 4:39:40 PM       Captures Pow
1.0             PreferenceVariables                      5/28/2014 5:47:54 PM      The Get-Call
3.1.0.0         Pscx                                     6/4/2014 8:58:40 PM       PowerShell C
1.0.0.8         PSReadline                               6/5/2014 4:39:40 PM       Great comman
2.4             ResolveAlias                             6/5/2014 12:28:40 AM      A function f
1.2.1.0         ScriptBrowser                            6/5/2014 12:28:40 AM      Script Brows
1.0.4.0         ScriptCS                                 6/5/2014 4:39:40 PM       Allows execu
1.0             ScriptTransforms                         5/28/2014 5:47:54 PM      Enables you
1.0.4.36        ScsmPx                                   5/28/2014 5:47:54 PM      The ScsmPx m
1.0.0.1         SNMP                                     6/6/2014 9:03:06 PM       Module to ma
2.0             SQLite                                   6/5/2014 4:39:40 PM       The SQLite P
1.0             StringTokens                             6/5/2014 4:39:40 PM       The Get-Stri
1.0.4.2         StrongNaming                             5/28/2014 5:47:54 PM      The Strong N
1.1             TCPServer                                6/5/2014 12:28:40 AM      Module used
1.0.0.1         TestConnectionAsync                      6/5/2014 4:39:40 PM       Test-Connect
1.1.0.0         TxF                                      6/5/2014 4:39:40 PM       Provides tra
1.0.2.2         TypeAccelerator                          5/28/2014 5:47:54 PM      The TypeAcce
2.0             xActiveDirectory                         6/7/2014 2:08:09 PM       The xActiveD
0.1.0           xAzure                                   6/7/2014 2:13:09 PM       The xAzure m
1.1.5           xAzureVMResources                        6/7/2014 1:08:09 PM       Module with
1.2.1           xComputerManagement                      6/7/2014 2:08:09 PM       The xCompute
1.1.1           xDatabase                                6/7/2014 2:08:09 PM       The xDatabas
1.0             xDhcpServer                              6/7/2014 2:13:09 PM       The xDhcpSer
1.0             xDnsServer                               6/7/2014 2:13:09 PM       The xDnsServ
2.0             xDscDiagnostics                          6/7/2014 2:08:09 PM       Module to he
1.1.1           xDSCResourceDesigner                     6/7/2014 2:08:09 PM       The xDscReso
2.6.0.0         xEXOUserAvailability                     6/7/2014 1:08:09 PM       xEXOUserAvai
1.1.1           xFailOverCluster                         6/7/2014 2:08:09 PM       The xFailOve
2.1.1           xHyper-V                                 6/7/2014 2:08:09 PM       The xHyper-V
0.2.16.1        xJea                                     6/7/2014 12:48:07 AM      Module with
2.1.1           xNetworking                              6/7/2014 2:08:09 PM       The xNetwork
1.0.0           xOneGet                                  6/7/2014 2:13:09 PM       DSC Resource
3.0.0.0         xPSDesiredStateConfiguration             6/7/2014 2:08:09 PM       The xPSDesir
1.0.1           xRemoteDesktopSessionHost                6/7/2014 2:08:09 PM       The xRemoteD
1.0.1           xSmbShare                                6/7/2014 2:08:09 PM       The xSmbShar
1.1.2           xSqlPs                                   6/7/2014 2:08:09 PM       The xSqlPs m
1.0.1           xSystemSecurity                          6/6/2014 12:58:02 PM      The xSystemS
1.3.1           xWebAdministration                       6/7/2014 2:08:09 PM       The xWebAdmi
1.0             xWindowsUpdate                           6/7/2014 2:13:09 PM       Module with
0.0.0.1         xWinEventLog                             6/7/2014 2:13:09 PM       Configure Wi
A new world opens, with so many modules available for use. In a next post I will decisively continue exploring the new functionalities and features of Powershell 5.0. So stay tuned!

Friday, February 21, 2014

Powershell, legacy tools and unmanaged code

Although Powershell in its latest version has literally a huge number of cmdlets (1602 under Windows 2012 R2) and the .Net Framework exposes almost anything a system administrator could dream of, there are times you are stuck and either fall back to what I inappropriately call 'legacy' executables, or call unmanaged APIs from within your code.

For example, a simple task like finding out which Windows process is holding which socket open is almost an impossible task for any beginner to intermediate Powershell developer.

Of course there is a cmdlet to find out the list of the running processes. It's called Get-Process and exposes many process properties: Process Id (PID), Process name, Handles, Non Paged Memory (NPM), Paged Memory (PM), Working Set (WS), Virtual Memory (VM) and many others, but not the number of the corresponding open port.
Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     56       7     1796       7424    53     3.20    292 conhost
    201      11     1532       3612    42     1.98    344 csrss
     89       9     1260       3484    39     0.75    400 csrss
And of course, as the Scripting Guy explains, Powershell 4.0 has a cmdlet, named Get-NetTCPConnection, to show each TCP connection property, such as the local or remote IP address, the local or remote port, and the connection state. But unfortunately the information containing the ID of the process that set up each connection is not fetched.
LocalAddress         LocalPort RemoteAddress     RemotePort State         AppliedSetting
------------         --------- -------------     ---------- -----          --------------
192.168.44.12        58849     192.168.44.14     389        Established    Datacenter
192.168.44.12        58845     192.168.44.15     389        Established    Datacenter
::1                  54765     ::1               135        Established    Datacenter
So, as far as I have found, if we stick to out-of-the-box Powershell cmdlets, there is no way to find the link between a process and a socket.

Looking into .Net, inside the System.Net.NetworkInformation namespace we can find an interesting class to explore: IPGlobalProperties. This class provides information about the network connectivity of the local computer and has:
  • a method GetActiveTcpConnections() that returns a TCPConnectionInformation array holding three properties: LocalEndpoint, RemoteEndPoint and State 
  • a method GetActiveTcpListeners() which returns the IP address of the EndPoint as well as the Port number
Under Powershell you can query them straight away. Notice the we have to use :: to indicate to PowerShell that we want to work with a static method or property, like GetIPGlobalProperties:
[System.Net.NetworkInformation.IPGlobalProperties] | Get-Member -static

   TypeName: System.Net.NetworkInformation.IPGlobalProperties

Name                  MemberType Definition
----                  ---------- ----------
Equals                Method   static bool Equals(System.Object objA, System.Object objB)
GetIPGlobalProperties Method   static System.Net.Netwo...ion.IPGlobalProperties GetIPGlobalProperties()
ReferenceEquals       Method   static bool ReferenceEquals(System.Object objA, System.Object objB)

[System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties().GetActiveTcpConnections()

[System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties().GetActiveTcpListeners()
Unfortunately the PID of the process opening the socket is still missing. Too bad.

At this point, there are two options left. The first one is to fall back to using good old Netstat.exe and parse the result as Shay Levy did in an old post. You can check his function on PoshCode.

Parsing the output of netstat is not an easy task, and Shay does a brilliant job of splitting the text output and objectify it while adding the result of Get-Process to each connection, so that the PID is shown. The resulting PSObject can be filtered, sorted and formatted to best suit our needs. The function code is explained on his blog, so I won't delve more in it. I just invite you to have a look at it since its a great workaround (if not a full extent solution).

But how comes that we can't retrieve the same information netstat does when used with the -ano parameters? Well, the answer is simple: on a Windows Systems there is managed code and unmanaged code.
Output of netstat -ano, with PID
To make it simple (I am not a developer), Managed Code is code that runs within the .Net Framework's Common Language Runtime (CLR) and benefits from the services provided by the CLR itself. On the contrary, Unmanaged Code is code that runs outside the CLR: COM components, ActiveX components, and Win32 API functions are all examples of Unmanaged Code.

One of these functions, GetExtendedTCpTable, which exists inside the Internet Protocol Helper (IP Helper) API, is the one that Netstat probably use to find out the hidden link between processes and sockets.

These Win32 API functions can't be accessed directly from inside Powershell because there is no wrapper for them in the .Net Framework.

Fortunately there is the possibility to call Win32 and other unmanaged APIs from managed code (like C#) by using a Platform Invoke (P/Invoke), which is the operation the Common Language Runtime does when it finds and loads the required Win32 API DLL in memory, then invokes the desired function.

As Lee Holmes explains on his blog, there are a few ways to access a Win32 API from Powershell. One of them consists in using the Add-Type cmdlet, which takes in the C# signature of a function (GetExtendedTCpTable in our case, whose signature is here), and building a new class on it.

This is a pretty complex process, which goes well beyond the perimeter of this system administration blog, but I want all the same share the link to Justin blog who did an excellent job of importing the iphlpapi.dll and writing a Get-Netstat wrapper function.
Add-Type cmdlet and C# signature
Both Shay's and Justin's solution to the netstat problem are good and very educational and both have pros and cons.

Accessing a function inside an unmanaged API is a rewarding but dangerous task, since you are exiting the comfort zone your Powershell interpreter provides, so be careful, since you shouldn't be doing that unless you have very good Windows Internals and C# skills.

Shay's approach is brilliant, since it shows how strong Powershell is in handling raw text and beautifying it to a manageable object. I liked in particular the way he checks for a string being an IPV6 address inside an IF block:
if (($la = $item[1] -as [ipaddress]).AddressFamily -eq ‘InterNetworkV6′)
        {
 }
I really like Powershell, because I never stop learning. Stay tuned for more!
Related Posts Plugin for WordPress, Blogger...