Monday, May 25, 2015

Moving from ping, netstat and ipconfig to Powershell

Ping, Netstat and Ipconfig are the three major legacy commands that every Windows system administrator out there has been using for decades and that, I bet, most of you are still using pretty often notwithstanding the large spread of Powershell. Well, I am not telling you to switch to Powershell today for performing basic tasks like pinging hosts or clearing your DNS cache. I am just going to show you the cmdlets that in Powershell do these tasks. And, knowing the fact that Powershell is an object-based language with a strong pipeline support, you imagine that using the Powershell way is a better option and gives you a flexible syntax as wel as re-usable results.

PING

Let's start from the first legacy command. While working in a research laboratory, back in 1983, in just one evening, Mike Muuss wrote the ancestor of the small utility that everyone of us knows as being the first tool to use to check network connectivity: ping.exe.

Microsoft came up a great amount of years later with a new cmdlet that leverages ICMP for its second version of Powershell: Test-Connection.

Test-Connection is based on the Win32_PingStatus class and since Powershell 3.0 supports a -Source parameter, which makes the use of this cmdlet particularly interesting over the use of old style pings. In fact, the -Source parameter allows you to specify the names of the computer where the ping originates from:

Test-Connection -Source Src01, Src02, Src03 -ComputerName Dst01
Also, Test-Connection allows you to ping multiple hosts at once:

Test-Connection -ComputerName Dst01, Dst02, Dst03
Using Test-Connection instead of ping allows you to take advantage of the $? variable to perform conditional actions:

if(Test-Connection nonexistingserver){'succeded'}else{'Failed'}
Test-Connection : Testing connection to computer 'nonexistingserver' failed: No such host is known
At line:1 char:4
+ if(Test-Connection nonexistingserver){'succeded'}else{'Failed'}
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : ResourceUnavailable: (nonexistingserver:String) [Test-Connection], PingException
+ FullyQualifiedErrorId : TestConnectionException,Microsoft.PowerShell.Commands.TestConnectionCommand
if(Test-Connection -ComputerName Server01 -Quiet){New-PSSession Server01}
This variable is not available for legacy commands (as I explained in this previous post), so the value of $? stays $True when you use legacy ping, and this is a limiting factor in using ping.exe in conditional statements:

if(ping nonexistingserver){'succeded'}else{'Failed'}
succeded
So now, if you are decided to replace the ping command with Test-Connection, you can simply create an alias named 'Ping' and you're set:

Set-Alias ping Test-Connection
or if you want a quicker way, just name the alias 'p':

Set-Alias p Test-Connection
IPCONFIG

The second command I want to talk of is ipconfig, which is the command used primarly to output your computer network configuration. This command was introduced 20 years ago, in 1995-1996, to Windows 95, at a time when putting the IP stack on everyone's PC was the main task of administrators and this involved typing arcane IPv4 numbers into the now forgotten winipcfg, which was in turn created for Windows 3.1 years before (remember the Wolverine stack?).

Ipconfig has four major switches as old as dinosaurs: /all, /flushdns, /displaydns and /registerdns. All of them have corresponding Powershell cmdlets, which I must admit makes them hard to remember. In any case working with network adapters has taken a great swing starting with Windows 8 and Windows 2012, when a new wave of network-oriented cmdlets belonging to the NetTCPIP module has appeared along with the underlying CIM namespaces and classes.

Ipconfig /all can be replaced by Get-NetIPConfiguration. Tough their output is more or less the same, a few consideration can be made about the improvement the Powershell cmdlet has brought to its predecessor. First of all, Get-NetIPConfiguration has switches for querying remote hosts: -Computername and -Cimsession.

You could for instance create a CIM session to pass to Get-NetIPConfiguration, even do for the moment the -CimSession switch of this cmdlet does not accept an array of computers:

$cimsession = New-CIMsession -Computername srv01

Get-NetIPConfiguration -CimSession $cimsession
Now, since Get-NetIPConfiguration returns an object, you could retrieve just one or more properties:

$ipconf = Get-NetIPConfiguration
$ipconf.DNSServer

InterfaceAlias               Interface Address ServerAddresses
                             Index     Family
--------------               --------- ------- ---------------
Wi-Fi                                4 IPv6    {}
Wi-Fi                                4 IPv4    {192.168.1.1, 192.168.1.1}
Bluetooth Network Connection         8 IPv6    {fec0:0:0:ffff::1, fec0:0:0:ffff:...
Bluetooth Network Connection         8 IPv4    {}
Ethernet                             3 IPv6    {}
Ethernet                             3 IPv4    {192.168.1.254}
Powerful, isn't it?

Now, for the lazy admin, know that the NetTCPIP module exports an alias for Get-NetIPConfiguration: gip. And since gip is way shorter than ipconfig, you have no reasons left to stick with the old command.

Ipconfig /flushdns can be replaced by Clear-DnsClientCache. This cmdlet purges the cache the client-side DNS cache that Windows stores on your computers.

The equivalent of Ipconfig /RegisterDns is Register-DnsClient. As its predecessor, Register-DnsClient invokes un update of the DNS names on all the interfaces of your computer.

NETSTAT

The last command I'd like to talk about today is the old glorious netstat. I am a huge fan of this command since it saved me in many cases when working on network connectivity issues with firewalls and application ports. Nonetheless the time has arrived to explore the Powershell way of implementing the same functions.

Netstat -a, which is used to display connections and listening ports, has evolved toward Get-NetTCPConnection since Windows 2012 and Windows 8. The output is an object with six default fields:

Get-NetTCPConnection

LocalAddress                        LocalPort RemoteAddress                       RemotePort State       AppliedSetting
------------                        --------- -------------                       ---------- -----       --------------
::                                  49157     ::                                  0          Listen
::                                  49156     ::                                  0          Listen
::                                  49155     ::                                  0          Listen
::                                  49154     ::                                  0          Listen
::                                  49153     ::                                  0          Listen
::                                  49152     ::                                  0          Listen
::                                  445       ::                                  0          Listen
::                                  135       ::                                  0          Listen
192.168.1.32                        49465     204.79.197.213                      443        Established Internet
0.0.0.0                             49157     0.0.0.0                             0          Listen
0.0.0.0                             49156     0.0.0.0                             0          Listen
0.0.0.0                             49155     0.0.0.0                             0          Listen
0.0.0.0                             49154     0.0.0.0                             0          Listen
0.0.0.0                             49153     0.0.0.0                             0          Listen
0.0.0.0                             49152     0.0.0.0                             0          Listen
0.0.0.0                             135       0.0.0.0                             0          Listen
The greatest strength of Get-NetTCPConnection over netstat is the possibility to filter on connection state on the fly:

Get-NetTCPConnection -State Established

LocalAddress     LocalPort RemoteAddress        RemotePort State       AppliedSetting
------------     --------- -------------        ---------- -----       --------------
192.168.1.32     49465     204.79.197.213       443        Established Internet
This means that you can easily filter down all connections which are in the state SYN_SENT, that is connections that are being blocked by a security firewall:

Get-NetTCPConnection -State SynSent
Get-NetTCPConnection : No MSFT_NetTCPConnection objects found with property 'State' equal to 'SynSent'.  Verify the
value of the property and retry.
At line:1 char:1
+ Get-NetTCPConnection -State SynSent
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (SynSent:State) [Get-NetTCPConnection], CimJobException
    + FullyQualifiedErrorId : CmdletizationQuery_NotFound_State,Get-NetTCPConnection
In the previous example, no connection is being blocked, so the big red error shown must be interpreted as a good news. Irony of IT.

For completeness, the possible connection states accepted by Get-NetTCPConnection are: Closed, Listen, SynSent, SynReceived, Established, FinWait1, FinWait2, CloseWait, Closing, LastAck, TimeWait and DeleteTCB.

There is another truly important switch for netstat that we are going to discuss to complete this blog post: -r. Using netstat with -r prints the routing table of your computer. The output you get is historically the same that you can get by using 'route print':

route print
===========================================================================
Interface List
  8...e4 d5 3d fd 15 99 ......Bluetooth Device (Personal Area Network)
  4...64 27 37 32 22 d1 ......Broadcom 4313GN 802.11b/g/n 1x1 Wi-Fi Adapter
  3...ec 9a 74 56 e8 89 ......Realtek PCIe GBE Family Controller
  1...........................Software Loopback Interface 1
  6...00 00 00 00 00 00 00 e0 Teredo Tunneling Pseudo-Interface
===========================================================================

IPv4 Route Table
===========================================================================
Active Routes:
Network Destination        Netmask          Gateway       Interface  Metric
        127.0.0.0        255.0.0.0         On-link         127.0.0.1    306
        127.0.0.1  255.255.255.255         On-link         127.0.0.1    306
  127.255.255.255  255.255.255.255         On-link         127.0.0.1    306
        224.0.0.0        240.0.0.0         On-link         127.0.0.1    306
  255.255.255.255  255.255.255.255         On-link         127.0.0.1    306
===========================================================================
Persistent Routes:
  None

IPv6 Route Table
===========================================================================
Active Routes:
 If Metric Network Destination      Gateway
  1    306 ::1/128                  On-link
  1    306 ff00::/8                 On-link
===========================================================================
Persistent Routes:
  None
In Powershell the cmdlet to use to get this piece of information is Get-NetRoute. In the case of Get-NetRoute, the interesting feature to highlight is of course the possibility to sort routes by metric:

Get-NetRoute | Sort-Object RouteMetric

ifIndex DestinationPrefix             NextHop     RouteMetric  PolicyStore
------- -----------------             -------     -----------  -----------
4       0.0.0.0/0                     192.168.1.1 0     ActiveStore
8       fe80::c5ee:f21f:1980:6b41/128 ::          256   ActiveStore
4       fe80::a820:ec8e:e0c5:b536/128 ::          256   ActiveStore
3       fe80::44ad:3bde:8351:1b7/128  ::          256   ActiveStore
1       ff00::/8                      ::          256   ActiveStore
4       ff00::/8                      ::          256   ActiveStore
8       ff00::/8                      ::          256   ActiveStore
3       ff00::/8                      ::          256   ActiveStore
6       2001::/32                     ::          256   ActiveStore
3       fe80::/64                     ::          256   ActiveStore
6       ::/0                          ::          256   ActiveStore
1       ::1/128                       ::          256   ActiveStore
6       fe80::/64                     ::          256   ActiveStore
6       fe80::ffff:ffff:fffe/128      ::          256   ActiveStore
8       fe80::/64                     ::          256   ActiveStore
4       fe80::/64                     ::          256   ActiveStore
4       224.0.0.0/4                   0.0.0.0     256   ActiveStore
8       224.0.0.0/4                   0.0.0.0     256   ActiveStore
3       224.0.0.0/4                   0.0.0.0     256   ActiveStore
1       255.255.255.255/32            0.0.0.0     256   ActiveStore
4       255.255.255.255/32            0.0.0.0     256   ActiveStore
8       255.255.255.255/32            0.0.0.0     256   ActiveStore
3       255.255.255.255/32            0.0.0.0     256   ActiveStore
1       224.0.0.0/4                   0.0.0.0     256   ActiveStore
1       127.0.0.1/32                  0.0.0.0     256   ActiveStore
1       127.0.0.0/8                   0.0.0.0     256   ActiveStore
6       ff00::/8                      ::          256   ActiveStore
1       127.255.255.255/32            0.0.0.0     256   ActiveStore
4       192.168.1.255/32              0.0.0.0     256   ActiveStore
4       192.168.1.32/32               0.0.0.0     256   ActiveStore
4       192.168.1.0/24                0.0.0.0     256    ActiveStore
I hope you learned something. It's time to reimagine ipconfig, ping and netstat, guys. Time to move to Powershell and become a faster, more powerful admin. A zillion of cmdlets (network-oriented or not) are waiting for you.

The pipeline is yours.

Friday, May 22, 2015

Powershell and exit status for cmdlets and commands

In Unix and Linux systems you can use the $? variable to find out the exit status of a command. To make a long story short, when you type commands in a UNIX shell, $? always expands to the status of the most recently executed foreground command or pipeline.

Now, since the original Powershell is based on the POSIX standard (back in the eighties, guys!), which tries to standardize commands from the UNIX Korn Shell, it's somewhat natural that Powershell borrowed this $? variable from its predecessor.

Let me give a few basic tips on the use of it.

The $? is a boolean variable documented in about_Automatic_Variables, and can by design hold only two values: $True or $False.

Get-ChildItem C:\Windows\System32\drivers\etc

    Directory: C:\Windows\System32\drivers\etc

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        10/06/2009     23:00        824 hosts
-a---        10/06/2009     23:00       3683 lmhosts.sam
-a---        10/06/2009     23:00        407 networks
-a---        10/06/2009     23:00       1358 protocol
-a---        10/06/2009     23:00      17463 services

$?
True
Test-Connection nohost -Count 1
Test-Connection : Testing connection to computer 'nohost' failed: The requested name is valid, but no data of the
requested type was found
At line:1 char:1
+ Test-Connection nohost -Count 1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (nohost:String) [Test-Connection], PingException
    + FullyQualifiedErrorId : TestConnectionException,Microsoft.PowerShell.Commands.TestConnectionCommand

$?
False
Here's how to check the variable type:

$?.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Boolean                                  System.ValueType
So, while in UNIX $? returned 0 or 1, in Powershell it returns $True or $False.

There is another important variable named $LastExitCode which returns the exit command of the last Windows command run:

cmd /C exit 0

$LASTEXITCODE
0
Remember though, $LastExitCode doesn't work with Powershell commands. For instance if we check the value of the variable after a failed Powershell cmdlet, its value stays 0, which means succesful:

Test-Connection nohost -Count 1

Test-Connection : Testing connection to computer 'nohost' failed: The requested name is valid, but no data of the
requested type was found
At line:1 char:1
+ Test-Connection nohost -Count 1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (nohost:String) [Test-Connection], PingException
    + FullyQualifiedErrorId : TestConnectionException,Microsoft.PowerShell.Commands.TestConnectionCommand

$LASTEXITCODE
0
On the other side, when we check it after a failing legacy or external command, it becomes effective:
ping nohost

Ping request could not find host nohost. Please check the name and try again.

$LASTEXITCODE
1
Also, $LastExitCode isn't populated until you execute a Powershell task that returns an exit code. If you open a fresh Powershell console and search for $LastExitCode... you won't find it:

$LASTEXITCODE -eq $null

True
Nor you will find if you run a Powershell cmdlet:

gci c:\nonexistingfolder

gci : Cannot find path 'C:\nonexistingfolde
At line:1 char:1
+ gci c:\nonexistingfolder
+ ~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFoun
    + FullyQualifiedErrorId : PathNotFound,

$LASTEXITCODE -eq $null
True
The variable gets populated only when you run an external executable that returns an exit code, like the ping command:

ping nohost

Ping request could not find host nohost. Please check the name and try again.

$LASTEXITCODE -eq $null
False
I hope this brief description of the the two $? and $LASTEXITCODE variable was clear enough. If you have any specific question do not hesitate to leave it in a comment.

If you liked ths quick post, be kind, share. Alot more to come.

Thursday, April 30, 2015

How to pin Powershell to the taskbar with... Powershell

Last month I have been involved in a large Windows deployment project. One of the requirements was to be able to deliver Windows 7 computers with the Windows Powershell icon pinned to the taskbar.
 
That was not a difficult problem to solve, and, even if Windows 7 is now old stuff (especially after Windows 8.1 came out both on computers and Windows Phones) this project was nevertheless a good occasion to learn some Powershell basics that I feel like sharing with you here today.
 
The first thing to understand is that the Windows Taskbar is a user-oriented feature that Microsoft doesn't want system administrators or program installers to modify in place of the end-user. In other words, since most users don't want programs putting junk in their taskbar, Windows doesn't support you doing as such. That's the historical reason why I suppose there is no existing API to my knowledge to modify the Windows Taskbar.

Happily enough, the Shell.Application COM object comes to the rescue. Ok, I agree that's a bit old method of interacting with the Desktop by mimicking mouse-clicking, and some of you will point out that this is so VBScript. This is nonetheless the only possible way to achieve what we have been tasked for. And, for those that are curious, it's good to know that all the code I'll show below works against Windows 8 systems. What else?

Let's begin. The Shell.Application COM object is a powerful object that allow interaction with the Windows Shell. It provides a set of automation objects that can be used to access many of the Shell's features and dialog boxes.

For our task, we are going to use PowerShell to create this Shell.Application object and through it we will manipulate the Explorer programmatically.

All COM objects are created through the command: New-Object -ComObject. There really are a lot of options and possibilities for New-Object -ComObject. You could for instance open Outlook (with Outlook.Application), or Word (with Word.Application), or Excel (with Excel.Application). For our purpose we specifically need a Shell.Application type of object. Let's see how that's done.

$Shell = New-Object -ComObject Shell.Application
Once you have established this kind of connection with the Windows Shell, you can perfom most of the interaction with Windows Explorer as you'd do with your keyboard and mouse.

You could for instance open the Windows Explorer and browse the C:\ folder:

$Shell.open("C:\")
Or you could open the Windows Help:

$Shell.Help()


As you should know, Open() and Help() are methods of the Shell object. There are other useful methods, which can be investigated with Get-Member:

$Shell | Get-Member

   TypeName: System.__ComObject#{866738b9-6cf2-4de8-8767-f794ebe74f4e}

Name                 MemberType Definition
----                 ---------- ----------
AddToRecent          Method     void AddToRecent (Variant, string)
BrowseForFolder      Method     Folder BrowseForFolder (int, string, int, Variant)
CanStartStopService  Method     Variant CanStartStopService (string)
CascadeWindows       Method     void CascadeWindows ()
ControlPanelItem     Method     void ControlPanelItem (string)
EjectPC              Method     void EjectPC ()
Explore              Method     void Explore (Variant)
ExplorerPolicy       Method     Variant ExplorerPolicy (string)
FileRun              Method     void FileRun ()
FindComputer         Method     void FindComputer ()
FindFiles            Method     void FindFiles ()
FindPrinter          Method     void FindPrinter (string, string, string)
GetSetting           Method     bool GetSetting (int)
GetSystemInformation Method     Variant GetSystemInformation (string)
Help                 Method     void Help ()
IsRestricted         Method     int IsRestricted (string, string)
IsServiceRunning     Method     Variant IsServiceRunning (string)
MinimizeAll          Method     void MinimizeAll ()
NameSpace            Method     Folder NameSpace (Variant)
Open                 Method     void Open (Variant)
RefreshMenu          Method     void RefreshMenu ()
ServiceStart         Method     Variant ServiceStart (string, Variant)
ServiceStop          Method     Variant ServiceStop (string, Variant)
SetTime              Method     void SetTime ()
ShellExecute         Method     void ShellExecute (string, Variant, Variant, Variant, Variant)
ShowBrowserBar       Method     Variant ShowBrowserBar (string, Variant)
ShutdownWindows      Method     void ShutdownWindows ()
Suspend              Method     void Suspend ()
TileHorizontally     Method     void TileHorizontally ()
TileVertically       Method     void TileVertically ()
ToggleDesktop        Method     void ToggleDesktop ()
TrayProperties       Method     void TrayProperties ()
UndoMinimizeALL      Method     void UndoMinimizeALL ()
Windows              Method     IDispatch Windows ()
WindowsSecurity      Method     void WindowsSecurity ()
WindowSwitcher       Method     void WindowSwitcher ()
Application          Property   IDispatch Application () {get}
Parent               Property   IDispatch Parent () {get}
Between them you can see methods like ToggleDesktop(), which corresponds to pressing Win+D, or MinimizeAll(), which corresponds to Win+M. In our case we want first use the NameSpace method to create and return a Folder object for the folder where Windows Powershell resides: C:\Windows\System32\WindowsPowershell\v1.0\
 
The NameSpace method accepts as a parameter the path of the folder you work on as well as one of the ShellSpecialFolderConstants values. Here some examples:

(new-object -comobject shell.application).NameSpace(0x21).Self.Path
C:\Users\administrator\AppData\Roaming\Microsoft\Windows\Cookies
or

(new-object -comobject shell.application).NameSpace(0x25).Self.Path
C:\Windows\System32
In the previous example 0x25 is an hex code referring to the Windows System folder, usually c:\Windows\System32.

Unfortunately for the moment there is no special folder hex code for the Windows Powershell folder as far as I know. So the full path must be hard-coded in the string:

(New-Object -ComObject shell.application).Namespace('C:\Windows\System32\WindowsPowershell\v1.0\')
Let's see what the available methods are for the returned Folder object:

(New-Object -ComObject shell.application).Namespace('C:\Windows\System32\WindowsPowershell\v1.0\') | Get-Member

   TypeName: System.__ComObject#{a7ae5f64-c4d7-4d7f-9307-4d24ee54b841}

Name                       MemberType Definition
----                       ---------- ----------
CopyHere                   Method     void CopyHere (Variant, Variant)
DismissedWebViewBarricade  Method     void DismissedWebViewBarricade ()
GetDetailsOf               Method     string GetDetailsOf (Variant, int)
Items                      Method     FolderItems Items ()
MoveHere                   Method     void MoveHere (Variant, Variant)
NewFolder                  Method     void NewFolder (string, Variant)
ParseName                  Method     FolderItem ParseName (string)
Synchronize                Method     void Synchronize ()
Application                Property   IDispatch Application () {get}
HaveToShowWebViewBarricade Property   bool HaveToShowWebViewBarricade () {get}
OfflineStatus              Property   int OfflineStatus () {get}
Parent                     Property   IDispatch Parent () {get}
ParentFolder               Property   Folder ParentFolder () {get}
Self                       Property   FolderItem Self () {get}
ShowWebViewBarricade       Property   bool ShowWebViewBarricade () {get} {set}
Title                      Property   string Title () {get}
Before any of these methods can be executed, we must use ParseName to create an object that represents an item inside this folder.

(New-Object -ComObject shell.application).Namespace('C:\Windows\System32\WindowsPowershell\v1.0\').parsename('powershell.exe')

Application  : System.__ComObject
Parent       : System.__ComObject
Name         : powershell.exe
Path         : C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
GetLink      :
GetFolder    :
IsLink       : False
IsFolder     : False
IsFileSystem : True
IsBrowsable  : False
ModifyDate   : 27/09/2013 04:13:50
Size         : 471040
Type         : Application
The returned object has four methods:

ExtendedProperty Method     Variant ExtendedProperty (string)
InvokeVerb       Method     void InvokeVerb (Variant)
InvokeVerbEx     Method     void InvokeVerbEx (Variant, Variant)
Verbs            Method     FolderItemVerbs Verbs ()
As you can see there are two ways to call linked verbs: InvokeVerb() and Verbs().

InvokeVerb() is the direct method to work on a FolderItem object. To pin or unpin items to the taskbar it takes as value the verbs as they are described under HKEY_CLASSES_ROOT\CLSID\{90AA3A4E-1CBA-4233-B8BB-535773D48449}.
That's because the action of pinning and unpinning items is managed by the 'Pin to Taskbar' handler which has the very secret CLSID {90AA3A4E-1CBA-4233-B8BB-535773D48449}. The two shown verbs are:

taskbarpin;taskbarunpin


So the code to pin or unpin is the following:

(New-Object -ComObject shell.application).Namespace('C:\Windows\System32\WindowsPowershell\v1.0\').parsename('powershell.exe').invokeverb('TaskbarPin')

(New-Object -ComObject shell.application).Namespace('C:\Windows\System32\WindowsPowershell\v1.0\').parsename('powershell.exe').invokeverb('TaskbarUnPin')
The alternative is to use the Verbs() method. The listed actions match what you would get by right clicking on an item in Windows Explorer:

(New-Object -ComObject shell.application).Namespace('C:\Windows\System32\WindowsPowershell\v1.0\').parsename('powershell.exe').verbs() | select Name

Name
----
&Open
Run as &administrator

Scan for Viruses...
Pin to Tas&kbar
Pin to Start Men&u
Restore previous &versions

Cu&t
&Copy
Create &shortcut
&Delete
Rena&me
P&roperties
Please note that the ampersand (&) that appears in some verbs is not a typo. Some verbs actually have the ampersand because it's used for the hot key of the context menu.

The oneliner here is longer than the one that leverages InvokeVerb():

((new-object -com "Shell.Application").Namespace('C:\Windows\System32\WindowsPowershell\v1.0\').Parsename('powershell.exe').Verbs() | ? {$_.Name -eq 'Pin to Tas&kbar'}).doit()
To make it more readable and understandable, let's split it up:

$shell = new-object -com "Shell.Application"
$folder = $shell.Namespace('C:\Windows\System32\WindowsPowershell\v1.0\')
$item = $folder.Parsename('powershell.exe')
$verb = $item.Verbs() | ? {$_.Name -eq 'Pin to Tas&kbar'}
if ($verb) {$verb.DoIt()}
Now you might be wondering what the 'DoIt' line is for. Well, it's a bit of running in circles but in simple words, DoIt() executes a verb on the FolderItem associated with the verb. Nothing else.
So to resume, the script does the pinning in three steps:
  1. Use the Verbs() method of a FolderItem object to get a list of verbs applicable to the target.
  2. Filter the verbs down to the one with a name matching the expected action.
  3. Execute the DoIt() method to invoke the verb.
When you execute the code above, it's just like Powershell is doing the clicking for you and your pinned apps on the taskbar get saved in both locations below:

In the registry under:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Taskband

In the hidden TaskBar folder:
C:\Users\Username\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar
 
Let's end this post with a few information on the Shell.Application. If you want to find out all the possible ComObjects on your PC with Powershell, just check in the registry under HKLM:\Software\Classes. We can perform a bit of pattern matching to filter down to the objects that matches the naming syntax of ComObjects with '^\w+\.\w+$', which can be read like two groups of words separated by a dot.

Here's the code to find them all:

Get-ChildItem HKLM:\Software\Classes -ErrorAction SilentlyContinue | ? {
   $_.PSChildName -match '^\w+\.\w+$' -and (Test-Path -Path "$($_.PSPath)\CLSID")
} | Select-Object -ExpandProperty PSChildName
Shell.Application is one of them:

Get-ChildItem HKLM:\Software\Classes | ? PSChildName -Match 'Shell.Application'

    Hive: HKEY_LOCAL_MACHINE\Software\Classes


Name                           Property
----                           --------
Shell.Application              (default) : Shell Automation Service

Now that we know where it is stored in the Windows Registry, we can also see its CLSID, which is {13709620-C279-11CE-A49E-444553540000}, as you can see opening the key. This value can be used to perform the pinning in another way that I am going to show you.

First of all we have to convert the CLSID to a type, which is done like this:

$Type= [Type]::GetTypeFromCLSID('13709620-C279-11CE-A49E-444553540000')
Then we will leverage the [System.Activator] class, which is generally used to create types of objects. Its CreateInstance method create an instance of the object. This is usually used in .NET Late Binding, as explained here, but I think it's worth having a look:

$Object = [Activator]::CreateInstance($Type)
Then we can proceed like seen before defining the path of the file, its name and the verb to apply:

[Activator]::CreateInstance([Type]::GetTypeFromCLSID('13709620-C279-11CE-A49E-444553540000')).Namespace('C:\Windows\System32\WindowsPowershell\v1.0\').ParseName('powershell.exe').InvokeVerb('taskbarpin')
That's kind of a long one-liner, and unfortunately there is no way of making it shorter as far as I can tell. You should now be able to read it, because you know the basics concepts I have just shown you. I hope you learned something. Stay tuned for more.

Monday, February 23, 2015

PowerShell Summit Europe 2015

Good news for Powershell scripters around Europe is that the second edition of the European Summit is being set up by Powershell.org. This year the event will be held in Sweden, at the Microsoft office in Kista, on September 14-16 2015.


So, starting on Febrary 27th, you are all invited to register and for a reason. The European Powershell Summit is a unique occasion to get in touch with people from the Powershell Team, as well as with well-known Powershell MVPs, and you are given the possiblity to make new connections, learn the secrets of the language and have some fun with other passionate system administrators and developers alike.
 
You must know that the event will be confirmed if and only if 20 to 30 people get registered by mid-April, so, without further ado, go to http://eventmgr.azurewebsites.net/event/home/PSEU15 and start checking the registration policy.
 
Make your voice heard. Show the world that the European Powershell Community is a vibrant place with smart people that are happy to have such an occasion to meet up with the elite of the Powershell world. Use Twitter, Facebook, or whatever social network you like to spread the word.
 
And I'll probably see you there!

Wednesday, December 17, 2014

How to manage the FSMO roles with Powershell

There was a time when I used legacy commands to do my daily duties. Most of you have at least used them once or more: stuff like dcpromo.exe, comp.exe, netdom.exe, xcopy.exe, tasklist.exe and taskkill.exe. It was a time when the Windows system administrators where the laughing stocks of Unix sysadmins, and for a good reason! Our skils were limited by the possibilities of the graphical user interface and we had a limited understanding of the way those old black boxes (read NT4, Windows 2000, 2003) worked under the hood. The console was just a joke, which we barely used to do a few ipconfigs or pings.

Today Microsoft is opening. There are new possibilities: Virtualization was a great (or giant?) step forward. The Cloud brought a new way of thinking to datas and to system administration. Interconnecting systems requires today a greater view of what's behind our operating systems. That's how I came to Windows Powershell. I wanted something better to do my job. I wanted to open and learn a way of managing computers which could give me satisfaction beyond that upsetting mouse clicking. Something that could make me feel prouder.

Powershell was the answer to this. A plyayful tool which in good hands can give great results.

Let me take an example: FSMO roles.

We all have an Active Directory out there indexing users, computers and resources. In these old times when you wanted to check what were the holders for each and every role you had to move down to the console and run something like this:

netdom query fsmo

Schema master               labdc01.contoso.com
Domain naming master        labdc01.contoso.com
PDC                         labdc01.contoso.com
RID pool manager            labdc01.contoso.com
Infrastructure master       labdc01.contoso.com

The command completed successfully.
Very static. I don't even want to show you how long and intricated was the procedure to learn how to move or seize those roles in case you needed to.

Today with Powershell we have a new way to manage those FSMO roles and make those Flexible Single Master Roles more... flexible! I want to show you now a few lines of code that are used to shuffle around your domain controllers the FSMO roles at random.

But a bit of theory first.

There are five roles: two at forest level and three at domain level. The Schema Master is the first Forest role and is in charge of keeping a writable copy of the Schema partition. The Domain Naming Master is the second forest role and keeps a writeable copy of the Configuration partition (that is the place where the logical view of your domains and trusts and the physical view of your Active Directory sites are stored). Then, at Domain level, there is the PDC Emulator, which manages time synchronization between clients and DCs (have ever heard about the 5 minutes kerberos ticket lifetime?), as well as GPO edition. The RID Master, which gives RID scopes to other DCs in the Domain. And the Infrastructure Master, which manages groups coherency between domains.

The two Forest roles can be found with Get-AdForest:

Get-ADForest | Select-Object SchemaMaster,DomainNamingMaster

SchemaMaster                         DomainNamingMaster
------------                         ------------------
labdc01.contoso.com                  labdc01.contoso.com
The three Domain roles can be found with Get-AdDomain:

Get-ADDomain | Select-Object PDCEmulator,RIDMaster,InfrastructureMaster

PDCEmulator             RIDMaster               InfrastructureMaster
-----------             ---------               --------------------
labdc01.contoso.com     labdc01.contoso.com     labdc01.contoso.com
Once you know that a list of all your domain controllers can be generated with:

Get-ADDomainController -filter * | select -ExpandProperty Name
then you can put in place a powerful oneliner that moves each FSMO role to a random DC in less than a second. Like shuffling and serving cards for a game, but with FSMO roles instead:

0..4 | % {Move-ADDirectoryServerOperationMasterRole -Identity (Get-Random (Get-ADDomainController -filter * | select -ExpandProperty Name)) -OperationMasterRole $PSItem -Confirm:$False -Verbose}
Notice the 0..4 | % {}. This is a quick way to index the five FSMO roles without having to manually write them down: Powershell will make the translation for you:
  • PDCEmulator: 0
  • RIDMaster: 1
  • InfrastructureMaster: 2
  • SchemaMaster: 3
  • DomainNamingMaster: 4
Here's the result:


Now I could be willing to play more and shuffle my FSMO roles around every day, then send me an e-mail in case I loose a DC, so that I know what roles were on it in that particular moment. I would store the e-mail in a dedicated folder and have an history of all role moves in my Domain.

Let's prepare the e-mail. The ingredients here are a couple of variables, a bit of splatting, a PSObject and a connection to a valid mail server:

$ForestFSMORoles = Get-ADForest | Select-Object SchemaMaster,DomainNamingMaster

$DomainFSMORoles = Get-ADDomain | Select-Object PDCEmulator,RIDMaster,InfrastructureMaster

$FSMORoles = New-Object PSObject -Property @{

                    InfrastructureMaster = $DomainFSMORoles.InfrastructureMaster

                    PDCEmulator = $DomainFSMORoles.PDCEmulator

                    RIDMaster = $DomainFSMORoles.RIDMaster

                    DomainNamingMaster = $ForestFSMORoles.DomainNamingMaster

                    SchemaMaster = $ForestFSMORoles.SchemaMaster

  }

$EmailSplatting = @{
 
            To = 'administrator@contoso.com'

            From = 'administrator@contoso.com'

            Subject = "FSMO Roles owners of the day for $((Get-AdForest).Name)"

            Body = $FSMORoles | ConvertTo-Html | Out-String

            SMTPServer = 'smtp.contoso.com'

            BodyAsHtml = $True

            }
        
Send-MailMessage  @EmailSplatting
Just put the code in a .ps1, put the ps1 in a scheduled task and you're done.
 
What do you think? Funny, isn't it? Of course I am not reponsible of what you do with your FSMO roles. I am just showing the power of Powershell and the fun it is to make the most out of it. Limited only by our imagination.

Wednesday, December 3, 2014

Powershell oneliner to get disk usage by file extension

These days storing multimedia files (like family videos, travel pictues, etc) requires a lot of storage and since I am a big fan of SSDs (and those kind of disks are still quite expensive), I wanted to show the simple Powershell one-liner I wrote in the weekend that checks what kind of file extensions are (over)consuming space on all my local drives.

The first step is to retrieve a list of all my local drives, which resumes to a query to Win32_LogicalDisk:

(Get-CIMInstance -class Win32_LogicalDisk -Filter 'drivetype=3').DeviceID
C:
D:
E:
L:
N:
Z:
A lot of drives, so a lot of work that Powershell will silently do for me.

Let's start to run Get-ChildItem against this list of local drives and hunt for every file in every subfolder:

Get-ChildItem $((Get-CIMInstance -class Win32_LogicalDisk -Filter 'drivetype=3').DeviceID) `
-File -Recurse
Once you know (thanks Get-Member!) that Get-ChildItem returns the file extension as a full grown property, you can ask Powershell to make the grouping for you:

Get-ChildItem $((Get-CIMInstance -class Win32_LogicalDisk -Filter 'drivetype=3').DeviceID) `
-File -Recurse |

 Group-Object Extension
The next step is to send this result down the pipeline to Select-Object, which is very good at defining what I call 'calculated properties'.

The properties I need for my report are:
- the extension itself
- the total size of all the files for the given extension
- the Count of those files

Since Group-Object returns four properties:

Count       Property   int Count {get;}                                                
Group       Property   System.Collections.ObjectModel.Collection[psobject] Group {get;}
Name        Property   string Name {get;}                                              
Values      Property   System.Collections.ArrayList Values {get;} 
we have to build on top of this to get what we want.

Let's start by letting Select-Object chew the Group properties (which is a PSObject, as you can see above) to get the total file size for each extension:

... | Select-Object ..., @{n='Size';e={$($_.Group | Measure-Object Length -Sum).Sum}}, ...

Name        Size Count
----        ---- -----
.JPG  2507712043  2704
.jpeg     319979     1
.mp4  1605261912   119
.tif    22024960     1

Very well, now we have to beautify this to make it readable. I'll perform rounding on the size of the files after having converted it to megabytes:

... | Select-Object Name, @{n='Size (MB)';e={[math]::Round((($_.Group | Measure-Object Length -Sum).Sum / 1MB), 2)}}, Count
Then I'll change the name property to Extension, suppress the dot at the beginning of the string (through some very complex regex!), and make the resulting string uppercase:

... | Select-Object @{n='Extension';e={($_.Name -replace '^\.').ToUpper()}}, ...
Here's the final result, sorted by total file size by extension:

gci $((gcim Win32_LogicalDisk -Filter 'drivetype=3').DeviceID) -File -Rec |
  
  Group Extension |

  Select @{
   n='Extension';
   e={($_.Name -replace '^\.').ToUpper()}
   },

   @{
   n="Size (MB)";
   e={[math]::Round((($_.Group | Measure Length -Sum).Sum/1MB),2)}
   },

   Count |

  Sort 'Size (MB)' -Desc

Extension Size (MB) Count
--------- --------- -----
JPG         2391,54  2704
MP4          1530,9   119
TIF              21     1
JPEG           0,31     1
Of course the one-liner can be modified to taste.

You could for instance pass to Get-ChildItem a manual list of drives or folders in the form or an array:

Get-ChildItem @('c:\','d:\','n:\pictures','n:\videos') -File -Recurse
Or you could use a different measure unit from MB. In Powershell V5 there are for instances five measure units: KB, MB, GB, TB and PB. The one you choose depends on the amount of files you have.

Powershell, anything is possible.

Friday, November 28, 2014

First look at ConvertFrom-String in Powershell v5

I've been playing with ConvertFrom-String for some days now and must admit that this cmdlet is to me one of the best improvement that came with WMF5.0 and Powershell v5.
 
For the moment there are not so many resources to learn from, but those that exist (especially the ones by fellow MVP François-Xavier Cart) are very well written. Let me mention them, so that this gives you a starting point for getting a good grasp on the mechanism behind it:
In addition to this posts there is a magic interface by Doug Finke on GitHub that allows you to quickly test your ConvertFrom-String template against any set of data. So, before you proceed, I definitively suggest you copy Doug's interface on your test bed to speed up your learning curve.

Ok, now that you have checked those resources and that you know that ConvertFrom-String is aimed at adding structure to unstructured string content, let's quickly get to the basic syntax.

ConvertFrom-String supports two ParameterSets:

ConvertFrom-String [-Delimiter ] [-PropertyNames ] -InputObject  []
and

ConvertFrom-String [-TemplateContent ] [-TemplateFile ] -InputObject  []
The cmdlet's default parameter set is FromDelimiter, which splits any line of text on whitespaces, so, while this does nothing

PS C:\> '12345' | ConvertFrom-String
this works and generates a certain amount of properties:

PS C:\> '1 2 3 4 5' | ConvertFrom-String

P1 : 1
P2 : 2
P3 : 3
P4 : 4
P5 : 5
The properties are dinamically casted to a type:

PS C:\> '1 2 3 4 5' | ConvertFrom-String | Select-Object -ExpandProperty P1 | Get-Member


   TypeName: System.Byte

PS C:\> 'a b c d e f g' | ConvertFrom-String | Select-Object -ExpandProperty P1 | Get-Member


   TypeName: System.Char
But I don't want to spend more time speaking of this, since Doug and François-Xavier did a great job of analyzing the basics of this cmdlet in their blog posts. I just suggest you get yourself familiar with the way the -TemplateFile parameter works against a txt file and the way the -TemplateContent parameter works againsts a Here-String before you proceed.

I want to try to build a concrete example of how to use ConvertFrom-String to build a structured object starting from the output of the legacy command Tracert.exe (guys, I know there is the 'Test-NetConnection -TraceRoute' option, but for the sake of this test I want to base my work on a legacy command that is not supposed to produce anything other then raw text).

Tracert prints a text which looks like this:

Tracing route to happysysadm.com [167.4.251.13] over a maximum of 30 hops:

  1     1 ms    <1 ms    <1 ms  HOST1 [164.129.210.252] 
  2     1 ms     1 ms     1 ms  host2.contoso.com [164.129.250.168] 
  3    11 ms    12 ms    12 ms  host3.subdomain.abc.com [10.230.15.194] 
  4    15 ms    15 ms    12 ms  10.230.14.9 
  5     9 ms    16 ms    16 ms  10.230.14.46 
  7    40 ms    40 ms    41 ms  10.75.200.1 
  8    38 ms    39 ms    39 ms  10.75.200.33 
  9     *        *        *     Request timed out.
 10     *        *        *     Request timed out.
 11     *        *        *     Request timed out.
 12    38 ms    39 ms    39 ms  167.4.251.13


Trace complete
From this printed output I want to extract the name of each device I cross on my route to the target and its IP address.

As you can understand, our ConvertFrom-String template based example, which is what we want to use here to get those messy data sorted out, needs to take care of different facts which I am going to oversimplify here:
  • the device name can come with or without a DNS suffix
  • the device name can be lowercase or uppercase or a mix of both
  • the device name can be missing!!!
  • the IP address can come inside square brackets or alone
  • there can be lines with a 'Request timed out' text instead of a pair Devicename/IPaddress
  • there are additional lines of text at the beginning and at the end of the output ('Tracing route...' and 'Trace complete...')
I copy paste the output of Tracert (I suggest you use the clip command: Tracert happysysadm.com | clip) inside Doug's ConvertFrom-String Buddy in both the Data and the Template textboxes

ConvertFrom-String Buddy interface by fellow MVP Doug Finke

then start modifying the template box. I want to create a HostInfo sequence on each line, so, following the well-known syntax
{[optional-typecast]namesequence-spec:example-value}
I modify the first line of the template as follow:

1     1 ms    <1 ms    <1 ms  {HostInfo*:{Computername:HOST1} [{IPAddress:164.129.210.252}]} 
and I get the following result:

HostInfo                                                                                            
--------                                                                                            
{@{ExtentText=HOST1 [164.129.210.252]; Computername=HOST1; IPAddress=164.129.210.252}}              
{@{ExtentText=host2.contoso.com [164.129.250.168]; Computername=host2; IPAddress=164.129.250.168}}  
{@{ExtentText=host3.subdomain.abc.com [10.230.15.194]; Computername=host3; IPAddress=10.230.15.194}}
Ok, looks like ConvertFrom-String is learning from my example as expected. Great, but I want the parse engine to take also FQDNs, so I modify the second line

2     1 ms     1 ms     1 ms  {HostInfo*:{Computername:host2.contoso.com} [{IPAddress:164.129.250.168}]} 
and this modification allow me to get the whole FQDN for the second and third record:

HostInfo                                                                                                              
--------                                                                                                              
{@{ExtentText=HOST1 [164.129.210.252]; Computername=HOST1; IPAddress=164.129.210.252}}                                
{@{ExtentText=host2.contoso.com [164.129.250.168]; Computername=host2.contoso.com; IPAddress=164.129.250.168}}        
{@{ExtentText=host3.subdomain.abc.com [10.230.15.194]; Computername=host3.subdomain.abc.com; IPAddress=10.230.15.194}}
This was the easy part.
 
Now I have to get a sequence even when the device name is missing. I must admit I had a hard time getting beyond this and was able to solve only thanks to the help of Gustavo (which you can reach at psdmfb @ microsoft com).

The key here is to tell the parse engine that the ComputerName property is optional on each line of the example, then to add a negative example for ComputerName on the third example so that Powershell does not incorrectly identify an IP as a Computername when this property is missing.

To make it simple, here's the syntax to use:

{!Computername?:10.230.15.194}
The first part of the template then becomes (notice the question marks):

  1     1 ms    <1 ms    <1 ms  {HostInfo*:{Computername?:HOST1} [{IPAddress:164.129.210.252}]} 
  2     1 ms     1 ms     1 ms  {HostInfo*:{Computername?:host2.contoso.com} [{IPAddress:164.129.250.168}]} 
  4    15 ms    15 ms    12 ms  {HostInfo*: {IPAddress:{!Computername?:10.230.14.9}}} 
and returns:

HostInfo                                                                                                              
--------                                                                                                              
{@{ExtentText=HOST1 [164.129.210.252]; Computername=HOST1; IPAddress=164.129.210.252}}
{@{ExtentText=host2.contoso.com [164.129.250.168]; Computername=host2.contoso.com; IPAddress=164.129.250.168}}
{@{ExtentText=host3.subdomain.abc.com [10.230.15.194]; Computername=host3.subdomain.abc.com; IPAddress=10.230.15.194}}
{@{ExtentText=10.230.14.9; IPAddress=10.230.14.9}}
{@{ExtentText=10.230.14.46; IPAddress=10.230.14.46}}
{@{ExtentText=10.75.200.1; IPAddress=10.75.200.1}}
{@{ExtentText=10.75.200.33; IPAddress=10.75.200.33}}
{@{ExtentText=Request timed out.; Computername=Request}}
{@{ExtentText=Request timed out.; Computername=Request}}
{@{ExtentText=Request timed out.; Computername=Request}}
{@{ExtentText=167.4.251.13; IPAddress=167.4.251.13}}
Woah! I got what I want plus some undesired text: {@{ExtentText=Request timed out.; Computername=Request}}

If from the Template I remove the line with

9     *        *        *     Request timed out.
I get an error:

ConvertFrom-String appears to be having trouble parsing your data using the template you’ve provided.
So I have to make my HostInfo examples more expressive so the parser knows what kind of data I am after. Nothing easier. I just have to add an example which tells ConvertFrom-String that the IP address can come alone:

14    10 ms    10 ms     7 ms  {HostInfo*:{IPAddress:10.230.15.193}} 
and, there you are, we have a working template which output the following result:

HostInfo                                                                                                              
--------                                                                                                              
{@{ExtentText=HOST1 [164.129.210.252]; Computername=HOST1; IPAddress=164.129.210.252}}
{@{ExtentText=host2.contoso.com [164.129.250.168]; Computername=host2.contoso.com; IPAddress=164.129.250.168}}
{@{ExtentText=host3.subdomain.abc.com [10.230.15.194]; Computername=host3.subdomain.abc.com; IPAddress=10.230.15.194}}
{@{ExtentText=10.230.14.9; IPAddress=10.230.14.9}}
{@{ExtentText=10.230.14.46; IPAddress=10.230.14.46}}
{@{ExtentText=10.75.200.1; IPAddress=10.75.200.1}}
{@{ExtentText=10.75.200.33; IPAddress=10.75.200.33}}
{@{ExtentText=167.4.251.13; IPAddress=167.4.251.13}}
What's brilliant here is that Doug's ConvertFrom-String Buddy generate all the required code for you to copy paste it into your ISE and keep up working on the resulting object. What else?

On top of this you could add any possible action, like in the following example:

$targetData = @'
Tracing route to happysysadm.com [167.4.251.13] over a maximum of 30 hops:

  1     1 ms    <1 ms    <1 ms  HOST1 [164.129.210.252] 
  2     1 ms     1 ms     1 ms  host2.contoso.com [164.129.250.168] 
  3    11 ms    12 ms    12 ms  host3.subdomain.abc.com [10.230.15.194] 
  4    15 ms    15 ms    12 ms  10.230.14.9 
  5     9 ms    16 ms    16 ms  10.230.14.46 
  7    40 ms    40 ms    41 ms  10.75.200.1 
  8    38 ms    39 ms    39 ms  10.75.200.33 
  9     *        *        *     Request timed out.
 10     *        *        *     Request timed out.
 11     *        *        *     Request timed out.
 12    38 ms    39 ms    39 ms  167.4.251.13


Trace complete
'@

$TemplateContent = @'
Tracing route to happysysadm.com [167.4.251.13] over a maximum of 30 hops:

  1     1 ms    <1 ms    <1 ms  {HostInfo*:{Computername?:HOST1} [{IPAddress:164.129.210.252}]} 
  2     1 ms     1 ms     1 ms  {HostInfo*:{Computername?:host2.contoso.com} [{IPAddress:164.129.250.168}]} 
  4    15 ms    15 ms    12 ms  {HostInfo*: {IPAddress:{!Computername?:10.230.14.9}}} 
  9     *        *        *     Request timed out.
  14    10 ms    10 ms     7 ms  {HostInfo*:{IPAddress:10.230.15.193}} 

Trace complete
'@

$targetData |
    ConvertFrom-String -TemplateContent $TemplateContent |
    Select-Object -ExpandProperty HostInfo |
    Select-Object IPAddress | % {
                                "Your code to run against $($_.IPAddress) goes here"
                                }
If you want to get in the deep of ConvertFrom-String, focus on the use of the -Debug switch:

$targetData |
    ConvertFrom-String -TemplateContent $TemplateContent -Debug

DEBUG: Property: HostInfo
Program: ESSL((Contains(Number([0-9]+(\,[0-9]{3})*(\.[0-9]+)?), Dot(\.), Number([0-9]+(\,[0-9]{3})*(\.[0-9]+)?), 1)): 1, 2, ...: Number([0-9]+(\,[0-9]{3})*(\.[0-9]+)?)
, Dynamic Token(\ ms\ \ )(\ ms\ \ )...ε, 3 + ε...ε, 0)
-------------------------------------------------
Property: Computername
Program: ESSL((EndsWith(Dot(\.), Number([0-9]+(\,[0-9]{3})*(\.[0-9]+)?), Right Bracket(\]))): 0, 1, ...: ε...ε, 1 + ε...WhiteSpace(( )+), Left Bracket(\[), Number([0-9
]+(\,[0-9]{3})*(\.[0-9]+)?), 1)
-------------------------------------------------
Property: IPAddress
Program: ESSL((Contains(Number([0-9]+(\,[0-9]{3})*(\.[0-9]+)?), Dot(\.), Number([0-9]+(\,[0-9]{3})*(\.[0-9]+)?), 1)): 0, 1, ...: ε...Number([0-9]+(\,[0-9]{3})*(\.[0-9]
+)?), Dot(\.), Number([0-9]+(\,[0-9]{3})*(\.[0-9]+)?), 1 + Number([0-9]+(\,[0-9]{3})*(\.[0-9]+)?), Dot(\.), Number([0-9]+(\,[0-9]{3})*(\.[0-9]+)?)...ε, 1)
-------------------------------------------------
As you can understand using this debugging option, and to make a long story short, ConvertFrom-String is an intelligent tool that writes regex for you! Let's make a simple example to better grasp the concept:

$targetData = @'
a
'@

$TemplateContent = @'
{Letter*:a}
'@

$targetData | ConvertFrom-String -TemplateContent $TemplateContent -Debug

DEBUG: Property: Letter
Program: ESSL((EndsWith(all lower((?<![\p{Lu}\p{Ll}])(\p{Ll})+))): 0, 1, ...: ε...ε, 1 + ε...ε, 0)
The regex above is trying to match the given text against some Unicode categories. Without going too deep in this vast subject, what you must know is that each Unicode char matches one or often more than one category. An example of category is \p{L} or \p{Letter} which represents all the possible letters whatever the language. Another exampe of category is \p{N} or \p{Number} that contains any numeric char out there.
 
In the simple example above the ConvertFrom-String engine tries to match lowercase (\p{Ll}) and uppercase (\p{Lu}) letters. Got it?
 
That's all for the moment on ConvertFrom-String. The last piece of information I can give is that ConvertFrom-String has an alias: cfs.
 
If you want to learn more, know that there is a brilliant video on text parsing by fellow MVP Tobias Weltner (minutes 24 to 32 are specific to ConvertFrom-String) In its video, Tobias shows also the power of ISESteroids console, so it it worth checking it out.
 
Tobias Weltner on ConvertFrom-String with ISESteroids
 
If you liked this post, be kind, share!

Tuesday, November 11, 2014

Of Powershell, Pester, DSC and...

It's been since 2009 that I am using Powershell as my favourite administration tool and I must admit it has never evolved as fast as this year. A full bunch of new products and of interesting concepts are born and growing fast both in the language itself and in its surroudings, making it one of the key competence to have in the Windows IT world nowadays.
 
Beside this, Microsoft is pushing its Windows development as fast as it can, and the release of Windows 10 (and of its Server version), with its ton of new cmdlets, is a clear sign of the fact that people in Redmond are doing their best to build on the success of Powershell.
 
On top of this, the community is strongly focusing on making Powershell (which is a tool built for sysadmins and aimed at system administration) take advantage of well established software development processes like Test-Driven Development (TDD). The star on stage here is named Pester, a project started by Scott Muc a few years back, which is a Unit-Testing solution for your Powershell code.
 
Last but not least, by now you should have heard of Desired State Configuration, which, starting with Windows 2012 R2 and Powershell 4.0, is a configuration management solution aimed at preventing configuration drift in your environment. In a few words, DSC provides a set of PowerShell language extensions, in the form of new Windows PowerShell cmdlets, that you can use to declaratively specify how you want your environment to be configured. Talking of DSC with your colleagues, you will hear a lot about the concept of idempotency, which, to make it simple, is the way DSC ensures that the environment desired state will be reached by applying the entire configuration, regardless of the current state.
 
I am currently having fun testing all of this, and, let me tell you, if you are new to Powershell, it's time to get a grasp on it before it is too late.

To stimulate your curiosity, heres' some screenshots from my current labs.

Here you can see that in Windows 10 the Start button is back:


Same for the Server version:


Both come with Powershell 5.0, the ultimate version:


Installing Pester on Windows 10 is now a breeze, thanks to the Package Manager I have already talked of in a previous blog post. Just run

Find-Package Pester
to check which version is available (version 3.1.1 at the time of testing), then:

Install-Package Pester
and you'll get it installed.

Here's a screenshot of the full installation process:


The Pester module will appear in you Module folder:


These are the cmdlets it comes with (I'll come back on them in a future post):

Get-Command -Module Pester

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        AfterEach                                          3.1.1      Pester
Function        Assert-MockCalled                                  3.1.1      Pester
Function        Assert-VerifiableMocks                             3.1.1      Pester
Function        BeforeEach                                         3.1.1      Pester
Function        Context                                            3.1.1      Pester
Function        Describe                                           3.1.1      Pester
Function        Get-MockDynamicParameters                          3.1.1      Pester
Function        Get-TestDriveItem                                  3.1.1      Pester
Function        In                                                 3.1.1      Pester
Function        InModuleScope                                      3.1.1      Pester
Function        Invoke-Mock                                        3.1.1      Pester
Function        Invoke-Pester                                      3.1.1      Pester
Function        It                                                 3.1.1      Pester
Function        Mock                                               3.1.1      Pester
Function        New-Fixture                                        3.1.1      Pester
Function        Set-DynamicParameterVariables                      3.1.1      Pester
Function        Setup                                              3.1.1      Pester
Function        Should                                             3.1.1      Pester

So now, there are a lot of new things to learn out there. It's time for you to start having a look at all of this and that's why I am giving you a short list of recent and interesting blog posts to start from.
Stay tuned for more on Powershell!

Monday, November 10, 2014

How to use Powershell to setup a GPO for Script Execution Policy and for WinRM

Last week, during the deployment of a new Active Directory Domain, I was challenged by a colleague of mine to write a Powershell  function that performs  the two following jobs on all the computers in the new Domain:
  • set the script Execution Policy to RemoteSigned
  • enable Powershell Remoting for remote management
To be honest I never thought it had any real use to modify my Default Domain Policy to apply the above settings using Powershell instead of the GUI, since this is a kind of job you do once and for all in your Domain and Powershell brings no added value, unless.... unless you can re-use the same function for all your labs requiring a new Domain. So I accepted the challenge.
 
The function I came up with is the result of a lot of try and guess. I studied the existing policies on others Domain I have and came to the conclusion that the operations my function had to perform were be aimed at modifying a few registry keys, as well as to modify GptTmpl.inf, which is a INF file that keeps the security setting for a given GPO.
 
Let's see this in detail.
 
To change the Execution Policy for scripts, you have to play with the  HKLM\Software\Policies\Microsoft\Windows\Powershell key, and
  • set the value of ExecutionPolicy to RemoteSigned
  • set the value of EnableScripts to 1
Setting up Powershell Remoting configuration is a bit trickier, since you have to work on two fronts. First of all you must go under the HKLM\Software\Policies\Microsoft\Windows\WinRM\Service key and
  • set AllowAutoConfig to 1
  • set IPv4Filter and the IPv6Filter setting to * (or whatever settings is good for you)
Then you have also to set the WinRM service to start automatically, by adding it to the GptTmpl.inf file of your GPO. In my case I applied all this settings to my 'Default Domain policy' but of course you can create a dedicated GPO for this. It's up to you how to tackle the subject in your Domain.

Here's the complete function I wrote:
#Requires –Modules ActiveDirectory

function Set-PowershellDomainPolicy()
    {
    $domain = (Get-ADDomain).forest
    $id = (Get-GPO -name 'Default Domain Policy').id
    $ExecutionPolicyParams = @{
            name='Default Domain Policy';
      key='HKLM\Software\Policies\Microsoft\Windows\PowerShell';
            }
    try {
        Set-GPRegistryValue @ExecutionPolicyParams -ValueName ExecutionPolicy -Value RemoteSigned -Type String -ErrorAction Stop
        Set-GPRegistryValue @ExecutionPolicyParams -ValueName EnableScripts -Value 1 -Type DWord -ErrorAction Stop
        "Script execution policy changed succesfully!"
        }
    catch { "Error changing script execution policy" }

    $RemotingParams = @{
            Name='Default Domain Policy';
            Key = 'HKLM\Software\Policies\Microsoft\Windows\WinRM\Service';
            }
    
    try {
        Set-GPRegistryValue @RemotingParams -ValueName 'AllowAutoConfig' -Value 1 -Type DWord
        Set-GPRegistryValue @RemotingParams -ValueName 'IPv4Filter' -Value '*' -Type String
        Set-GPRegistryValue @RemotingParams -ValueName 'IPv6Filter' -Value '*' -Type String
        "Registry setting for Powershell Remoting OK!"
        }
    catch { "Error enabling remoting policy" }

     #Setting up the here-string
    $inf = @'
[Service General Setting]
"WinRM",2,""
'@

    try {
        $inf |
            Out-File "C:\Windows\SYSVOL\sysvol\$domain\Policies\{$id}\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf" -Append -ErrorAction Stop
        "WinRM startup set to Automatic in GptTmpl.inf!"
        }
    catch { "Error setting WinRM automatic startup"}
    }

Set-PowershellDomainPolicy
Which you can run by executing:
Set-PowershellDomainPolicy
As you can see, the central cmdlet of my function is Set-GPRegistryValue, which, If I remember well, exists from Powershell 4.0. This cmdlet takes as parameter the GUID of the GPO, which I retrieve using Get-GPO.
 
The Policy runs in a few seconds and its ouput is shown below:
DisplayName      : Default Domain Policy
DomainName       : contoso.com
Owner            : CONTOSO\Domain Admins
Id               : 31b2f340-016d-11d2-945f-00c04fb984f9
GpoStatus        : AllSettingsEnabled
Description      : 
CreationTime     : 05/11/2014 08:18:48
ModificationTime : 10/11/2014 15:44:48
UserVersion      : AD Version: 0, SysVol Version: 0
ComputerVersion  : AD Version: 25, SysVol Version: 25
WmiFilter        : 

DisplayName      : Default Domain Policy
DomainName       : contoso.com
Owner            : CONTOSO\Domain Admins
Id               : 31b2f340-016d-11d2-945f-00c04fb984f9
GpoStatus        : AllSettingsEnabled
Description      : 
CreationTime     : 05/11/2014 08:18:48
ModificationTime : 10/11/2014 15:44:48
UserVersion      : AD Version: 0, SysVol Version: 0
ComputerVersion  : AD Version: 26, SysVol Version: 26
WmiFilter        : 

Script execution policy changed succesfully!

DisplayName      : Default Domain Policy
DomainName       : contoso.com
Owner            : CONTOSO\Domain Admins
Id               : 31b2f340-016d-11d2-945f-00c04fb984f9
GpoStatus        : AllSettingsEnabled
Description      : 
CreationTime     : 05/11/2014 08:18:48
ModificationTime : 10/11/2014 15:44:48
UserVersion      : AD Version: 0, SysVol Version: 0
ComputerVersion  : AD Version: 27, SysVol Version: 27
WmiFilter        : 

DisplayName      : Default Domain Policy
DomainName       : contoso.com
Owner            : CONTOSO\Domain Admins
Id               : 31b2f340-016d-11d2-945f-00c04fb984f9
GpoStatus        : AllSettingsEnabled
Description      : 
CreationTime     : 05/11/2014 08:18:48
ModificationTime : 10/11/2014 15:44:48
UserVersion      : AD Version: 0, SysVol Version: 0
ComputerVersion  : AD Version: 28, SysVol Version: 28
WmiFilter        : 

DisplayName      : Default Domain Policy
DomainName       : contoso.com
Owner            : CONTOSO\Domain Admins
Id               : 31b2f340-016d-11d2-945f-00c04fb984f9
GpoStatus        : AllSettingsEnabled
Description      : 
CreationTime     : 05/11/2014 08:18:48
ModificationTime : 10/11/2014 15:44:48
UserVersion      : AD Version: 0, SysVol Version: 0
ComputerVersion  : AD Version: 29, SysVol Version: 29
WmiFilter        : 

Registry setting for Powershell Remoting OK!
WinRM startup set to Automatic in GptTmpl.inf!
If you want you can manually check that the settings are applied by checking the content of the INF file:
 
 
And for the other settings, check you Group Policy Management GUI:
 
 
Don't forget to run this function on a Domain Controller or on any member server having RSAT.
 
On your clients you must wait for 90 minutes (with a random offset of 0 to 30 minutes) for the GPO to re-apply, or just logoff and logon again.
 
As a side information, this GPO worked well against my new beta Windows 10 clients!
 
If you have any question, feel free to ask and .... please share!
Related Posts Plugin for WordPress, Blogger...