MVP Virtual Conference

Passing this on from Microsoft.

Register to attend the Microsoft MVP Virtual Conference

Hi All – I wanted to let you know about a great free event that Microsoft and the MVPs are putting on, May 14th & 15th.  Join Microsoft MVPs from the Americas’ region as they share their knowledge and real-world expertise during a free event, the MVP Virtual Conference.

The MVP Virtual Conference will showcase 95 sessions of content for IT Pros, Developers and Consumer experts designed to help you navigate life in a mobile-first, cloud-first world.  Microsoft’s Corporate Vice President of Developer Platform, Steve Guggenheimer, will be on hand to deliver the opening Key Note Address.

Why attend MVP V-Conf? The conference will have 5 tracks, IT Pro English, Dev English, Consumer English, Portuguese mixed sessions & Spanish mixed sessions, there is something for everyone! Learn from the best and brightest MVPs in the tech world today and develop some great skills!

Be sure to register quickly to hold your spot and tell your friends & colleagues.

The conference will be widely covered on social media, you can join the conversation by following @MVPAward and using the hashtag #MVPvConf.

Register now and feel the power of community!

clip_image002

Posted in Conference, Microsoft | Leave a comment

PowerShell V5 New Feature: Protect/Unprotect-CmsMessage

Windows PowerShell V5, due out sometime in 2015, sports a number of new features: OneGet, PowerShell Get, enhanced DSC, ConvertFrom-String, support for authoring classes in PowerShell script, Compress/Expand-Archive, support for creating symbolic links, hard links and junctions, etc.

One of the more obscure but useful features is the support for cryptographically protecting messages as documented in the IETF standard RFC5652.  This involves the creation of a certificate which I will show you how to do.  You can then protect and unprotect messages using that certificate.  However, where it gets interesting is when you export a public certificate from the original certificate.  You can give the public certificate to anybody and they can use that to encrypt (protect) a message.  That message cannot not unencrypted (unprotected) by anyone except the individual that holds the original certificate.  The original certificate contains both the public and private key.  It is the private key that is used to unprotect the message. 

This is the fundamental basis for asymmetric cryptography.   One key encrypts a message that only the other key can decrypt.  The public key can be distributed broadly but the private  key needs to be held securely.

Here is how it works.  First, we need to create a certificate INF file that is configured for document encryption.  We will feed this file into a tool that will create our certificate:

[Version]
Signature = "$Windows NT$"

[Strings]
szOID_ENHANCED_KEY_USAGE = "2.5.29.37"
szOID_DOCUMENT_ENCRYPTION = "1.3.6.1.4.1.311.80.1"

[NewRequest]
Subject = "cn=foo.bar@baz.com"
MachineKeySet = false
KeyLength = 2048
KeySpec = AT_KEYEXCHANGE
HashAlgorithm = Sha1
Exportable = true
RequestType = Cert
KeyUsage = "CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_DATA_ENCIPHERMENT_KEY_USAGE"
ValidityPeriod = "Years"
ValidityPeriodUnits = "1000"

[Extensions]
%szOID_ENHANCED_KEY_USAGE% = "{text}%szOID_DOCUMENT_ENCRYPTION%"

Save this content into a text file called DocumentEncryption.inf.  Then run the following command:

PS C:\> certreq -new DocumentEncryption.inf DocumentEncryption.cer
Installed Certificate:
  Serial Number: 106ace908d57cc9548f830cc67e672e0
  Subject: CN=foo.bar@baz.com
  Thumbprint: 852dcc0c3384c5050e58ee5e655aee3981bc309f
  Microsoft Strong Cryptographic Provider
  526fad1d353ed734b914417afade0eff_f8d1fad6-91ae-4e10-aee5-017e5a7a97d9

 

This not only creates the certifcate .cer file but it also installs the certificate into the Certificate store.  You can see it by listing the cert: drive using the new dynamic parameter –DocumentEncryptionCert e.g.:

PS C:\> Get-ChildItem Cert:\CurrentUser\My\ -DocumentEncryptionCert


    Directory: Microsoft.PowerShell.Security\Certificate::CurrentUser\My


Thumbprint                                Subject
----------                                -------
852DCC0C3384C5050E58EE5E655AEE3981BC309F  CN=foo.bar@baz.com

 

At this point, you can round trip a protected message on this machine, under the user account that created and imported the certificate using certreq.exe:

PS C:\> $msg = Protect-CmsMessage -Content "This is a secret message!" -To *foo.bar@baz.com
PS C:\> $msg
-----BEGIN CMS-----
MIIBsgYJKoZIhvcNAQcDoIIBozCCAZ8CAQAxggFKMIIBRgIBADAuMBoxGDAWBgNVBAMMD2Zvby5i
YXJAYmF6LmNvbQIQEGrOkI1XzJVI+DDMZ+Zy4DANBgkqhkiG9w0BAQcwAASCAQA5EgX8IpfY+7aG
ozMJIO+ytwrBKp2PMluFjSBSj8aRkwk8T8kDHoSs9++jb9gu/GQUBdZc27AqIawFsBZILVFDmpej
1mLM+9RZ31rom/VxguNLPeqrz1SNN8ox47yO3cX/1QbfhtUhlYPeNpjMU3W9DF48br7SUlAAzLKj
Jrs6SoOmvV7TA93sLE9y5WQYq6FZlfjjZOf1Fio50nd91+oqDL6uUL4qsv4bKpGWGW/bSK7WT23S
20K+qimKfX4ScO6XGYP70NQy6q/unlROACpzssvbPf4e+VU9GHAuajj50SABJ2E9M32bYtmuAneN
n4p7Elbu3rsQyikgIT3HU1hhMEwGCSqGSIb3DQEHATAdBglghkgBZQMEASoEEHaFotyx5PCQ9UPL
mhcF/QCAIKr02MPfZl2GL0qOSensD34jPMpN2dKKN2PL2kgRIsLN
-----END CMS-----
PS C:\> $msg | Unprotect-CmsMessage
This is a secret message!

 

BTW the –To parameter will also take the certificate thumbprint as well as the path to the .CER file.  That demos the basic capability but round tripping on the same machine isn’t all that interesting.  Where it gets interesting is when you provide others with a public key in which they can protect messages they send to you.  Those protected messages can only be decrypted by you.

First lets export the public key part of the certificate:

 
$cert = Get-ChildItem -Path cert:\CurrentUser\My\852DCC0C3384C5050E58EE5E655AEE3981BC309F 
Export-Certificate -Cert $cert -FilePath DocumentEncryption-Public.cer 

Now copy the DocumentEncryption-Public.cer file to another machine.  Let’s use it to protect a message:

PS C:\> $msg = Protect-CmsMessage -Content "I am he as you are he as you are me" `
                                  -To .\DocumentEncryption-Public.cer PS C:\> $msg -----BEGIN CMS----- MIIBwgYJKoZIhvcNAQcDoIIBszCCAa8CAQAxggFKMIIBRgIBADAuMBoxGDAWBgNVBAMMD2Zvby5i YXJAYmF6LmNvbQIQEGrOkI1XzJVI+DDMZ+Zy4DANBgkqhkiG9w0BAQcwAASCAQBl3BE6UDdXxNRe /TATEgqasqVL4FZi2RVsm6s8RWUKH/GIUUe1EI2N3BeBHZP847DCAkiAKrd16Kds3yaCF4mcmKao lCd0TiInUA5WenDnxO40VW85MJrLWM6sjhQB0bMCNa0UMRV9IzRAAr1lSKDKcWupMScCQvQQ9JHR qCLRgPmWtGA+oMvfl5xs8FTS6oUvNOGh3MwMW7ZOMrk5y6vBltiI5TY34PRVZ/pYl+jnyjSi/tfP vmGp/GqmK9OgIGpgRjRJ8QHnWJ4CXAvL3zj3LXXyPevHKXBum8EOwpiM//zF5kl6gPdYrrqqQJuG OdQkNHXLZunZwtySAVL9n5TcMFwGCSqGSIb3DQEHATAdBglghkgBZQMEASoEEA9egmjPe/PsOo5j Wadz5WGAMAObP28v4rN1iCEjqEuY9Yjhgu8/m8kD2eWdm7/KRbpFCADSF6k5crTyqMApMUug6Q== -----END CMS-----

 

In case you were wondering, having the public key is not enough to decrypt the message – even on the same machine you used to encrypt the message with the public certificate:

PS C:\> $msg | Unprotect-CmsMessage –To .\DocumentEncryption-Public.cer
Unprotect-CmsMessage : The enveloped-data message does not contain the
specified recipient.
At line:1 char:8
+ $msg | Unprotect-CmsMessage
+        ~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Unprotect-CmsMessage], Crypto
   graphicException
    + FullyQualifiedErrorId : System.Security.Cryptography.CryptographicExcept
   ion,Microsoft.PowerShell.Commands.UnprotectCmsMessageCommand

 

If you would prefer to install the public key certificate on the second machine, you can do that easily:

PS C:\> Import-Certificate -FilePath DocumentEncryption-Public.cer `
                           -CertStoreLocation cert:\CurrentUser\My

 

Then you can use either the certification thumbprint or the subject e.g. *foo.bar@baz.com as the argument to the –To parameter on Protect-CmsMessage.

Now let’s copy paste the protected message back to the original machine with the certificate containing the private key and unprotect it:

PS C:\> $msg = @'
>>> -----BEGIN CMS-----
>>> MIIBwgYJKoZIhvcNAQcDoIIBszCCAa8CAQAxggFKMIIBRgIBADAuMBoxGDAWBgNVBAMMD2Zvby5i
>>> YXJAYmF6LmNvbQIQEGrOkI1XzJVI+DDMZ+Zy4DANBgkqhkiG9w0BAQcwAASCAQBl3BE6UDdXxNRe
>>> /TATEgqasqVL4FZi2RVsm6s8RWUKH/GIUUe1EI2N3BeBHZP847DCAkiAKrd16Kds3yaCF4mcmKao
>>> lCd0TiInUA5WenDnxO40VW85MJrLWM6sjhQB0bMCNa0UMRV9IzRAAr1lSKDKcWupMScCQvQQ9JHR
>>> qCLRgPmWtGA+oMvfl5xs8FTS6oUvNOGh3MwMW7ZOMrk5y6vBltiI5TY34PRVZ/pYl+jnyjSi/tfP
>>> vmGp/GqmK9OgIGpgRjRJ8QHnWJ4CXAvL3zj3LXXyPevHKXBum8EOwpiM//zF5kl6gPdYrrqqQJuG
>>> OdQkNHXLZunZwtySAVL9n5TcMFwGCSqGSIb3DQEHATAdBglghkgBZQMEASoEEA9egmjPe/PsOo5j
>>> Wadz5WGAMAObP28v4rN1iCEjqEuY9Yjhgu8/m8kD2eWdm7/KRbpFCADSF6k5crTyqMApMUug6Q==
>>> -----END CMS-----
>>> '@
204> $msg | Unprotect-CmsMessage
I am he as you are he as you are me

 

OK so these two commands are working as advertised but you may be asking what the practical application of this is.  Well, we’ve always had this issue of how to protect passwords that have to be embedded in scripts.  This asymmetric approach can help here but I think only when the script runs on machines and/or user accounts that are limited in access.  That is, I could envision checking in the public certificate into version control. Anybody could use that cert to protect passwords and embedded the protected password text into a script.  Folks like contractors that have access to source control cannot determine the password from the public key and the protected password plainly visible in scripts.  However, if you protect the private certificate by only installing it on the machines that run the scripts (build machines, test machines) *and* you can limit who can access the machines the scripts run on you have protected your passwords at least a bit more securely.

Disclaimer: I am not a security or certificate guru.  I’m interested in hearing how folks might use this asymmetric encryption functionality.  I’m also curious if the private key certificate can be installed onto a machine so that A) only a specific user can access the key and B) the key can’t be exported.  The INF file contains a field called Exportable that is set to true in the sample INF file above.  I need to experiment with it set to false to see if that would prevent the private key from being exported once it is installed on a specific user account on a specific machine.

Posted in PowerShell 5.0 | 2 Comments

BlackJack, NamedPipes and PowerShell Classes – Oh My!

In my last blog post, I introduced you to using .NET named pipes to implement BlackJack across different PowerShell processes and even across the network.  In this blog post, we will take a look at what it is like to convert the previous procedural implementation to an object-oriented implementation using the class support in the preview version of Windows PowerShell 5.0 – specifically the version in the Windows 10 Technical Preview.  Note: “preview” version means that the classes feature is likely to change between now and when PowerShell 5.0 ships.  Hopefully that means it gets better but there is always the possibility the feature gets pulled.

One of the major benefits of object-oriented programming is encapsulation i.e. you can put related code and state together into a single class definition rather than have it spread across your source code.  This makes it easier to fix bugs because certain types of bugs tend to impact all code that touches a specific data structure.  In procedural code you tend to look all over for that code but in an object-oriented implementation the code tends to be within the same class definition.  Here’s an example from the “procedural” version of the BlackJackDealer script.  These are the variables and functions that deal with cards, the deck and the hand:

$suits = 'Clubs','Diamonds','Hearts','Spades'
$ranks = 'Ace','2','3','4','5','6','7','8','9','10','Jack','Queen','King'

function GetShuffledDeck {
    $deck = 0..3 | Foreach {$suit = $_; 0..12 | Foreach { 
                      $num = if ($_ -eq 0) {11} elseif ($_ -ge 10) {10} else {$_ + 1}
                      [pscustomobject]@{Suit=$suits[$suit];Rank=$ranks[$_];Value=$num}}
                   }
    for($i = $deck.Length - 1; $i -gt 0; --$i) {
        $rndNdx = Get-Random -Maximum ($i+1)
        $temp = $deck[$i]
        $deck[$i] = $deck[$rndNdx]
        $deck[$rndNdx] = $temp
    }
    $deck
}

function GetValueOfHand($hand) {
    $sum = ($hand | Measure-Object Value -Sum).Sum
    if ($sum -gt 21) {
        $sum = ($hand | Foreach {if ($_.Value -eq 11) {1} else {$_.Value}} | Measure-Object -Sum).Sum
    }
    $sum
}

function IsHandBust($hand) {
    (GetValueOfHand $hand) -gt 21
}

function IsHandBlackJack($hand) {
    if ($hand.Length -ne 2) { return $false }
    (GetValueOfHand $hand) -eq 21
}

function DumpHand($hand) {
    $cards = $hand | Foreach {DumpCard $_}
    $OFS = ', '
    "$cards"
}

function DumpCard($card) {
    "$($card.Rank) of $($card.Suit)"
}

$cardNdx = -1
$deck
function DealCard {
    if ($cardNdx -lt 0) {
        WriteToPipeAndLog 'Deck empty, reshuffling deck' > $null
        $script:deck = GetShuffledDeck
        $script:cardNdx = $deck.Length - 1
    } 
    $deck[$script:cardNdx--]
}

Note the script level variables $suits, $ranks and $cardNdx.  With functions you have to be careful to remember to use script scope when you need to modify a script scope variable e.g.:

$deck[$script:cardNdx--]

It’s easy to forget to use the $script: prefix and that can lead to hard to find bugs.   It’s also not so obvious which of these functions are using the script scope variables.  Sure you can use your editor’s Find feature to determine that but in more complex cases involving multiple dot-sourced scripts, using Find can be more challenging.  Ideally you’d like to have the variables with the functions that use those variables encapsulated together.

BTW modules provide encapsulation and at this point in time, modules provide better support for encapsulation than PowerShell classes do. That is, variables in modules default to private but can be made public.  In PowerShell classes, the equivalent is a property but unfortunately at this time, properties can only be public.  But since this post is about classes and not modules, let’s press on.

Let’s look at the equivalent implementation using classes (and an enum).

# Updated for Windows 10 Preview Build 9879 - new 'hidden' keyword 
# and added support for semi-colon separated enum fields.
enum Suit { Clubs; Diamonds; Hearts; Spades }

class Card {
    [Suit]$Suit
    [string]$Rank
    [int]$Value

    Card($s, $r, $v) {
        $Suit = $s
        $Rank = $r
        $Value = $v
    }

    [string] ToString() {
        return "$Rank of $Suit"
    }
}

class Deck {
    hidden [Card[]]$Cards
    hidden [Pipe] $Pipe
    hidden [int]$Index

    Deck([Pipe]$p) {
        $this.Index = 0
        $ranks = 'Ace','2','3','4','5','6','7','8','9','10','Jack','Queen','King'
        $this.Cards = 0..3 | Foreach { $suit = $_; 0..12 | Foreach { 
                              $num = if ($_ -eq 0) {11} elseif ($_ -ge 10) {10} else {$_ + 1}
                              [Card]::new([Suit]$suit, $ranks[$_], $num)
                          }
                      }
        $this.Shuffle()
        $this.Pipe = $p
    }

    [void] Shuffle() {
        for($i = $this.Cards.Length - 1; $i -gt 0; --$i) {
            $rndNdx = Get-Random -Maximum ($i+1)
            $temp = $this.Cards[$i]
            $this.Cards[$i] = $this.Cards[$rndNdx]
            $this.Cards[$rndNdx] = $temp
        }
    }

    [Card] DrawCard() {
        if ($this.Index -gt $this.Cards.Length - 1) {
            $this.Shuffle()
            $this.Index = 0
            Write-Host ($this.Pipe.WriteLine('Deck empty, reshuffling deck'))
        }
        return $this.Cards[$this.Index++]
    }

    [string] ToString() {
        $OFS = ', '
        return "$Cards"
    }
}

class Hand {
    hidden [Deck]$Deck
    hidden [Card[]]$Cards

    Hand([Deck]$d) {
        $this.Deck = $d
        $this.Cards = $d.DrawCard(), $d.DrawCard()
    }

    [Card] DrawCard() {
        $card = $Deck.DrawCard()
        $this.Cards += $card
        return $card
    }

    [int] GetValueOfHand() {
        $sum = ($Cards | Measure-Object Value -Sum).Sum
        for ($i = $Cards.Length - 1; ($i -ge 0) -and ($sum -gt 21); $i--) {
            if ($Cards[$i].Value -eq 11) {
                $Cards[$i].Value = 1
                $sum -= 10
            }
        }
        return $sum
    }

    [string] ToString() {
        $OFS = ', '
        return "$Cards"
    }

    [string] ToDealerString() {
        return $this.Cards[0].ToString() + ', hole card'
    }

    [bool] IsBlackJack() {
        if ($this.Cards.Length -ne 2) { return $false }
        return $this.GetValueOfHand() -eq 21
    }

    [bool] IsBusted() {
        return $this.GetValueOfHand() -gt 21
    }

    [bool] IsMandatoryDealerHit() {
        return $this.GetValueOfHand() -lt 17
    }
}

A couple of things to note here.  First, yes the object-oriented version is more lines of script.  However, more lines of script doesn’t always mean more complex or harder to maintain.  In fact, I’d argue just the opposite in this case.  The script is easier to understand from a perspective of what variables are impacted by what methods.  We can easily see we have three basic types here: Card, Deck and Hand.  Each type knows how to perform the operations required of it i.e. a Deck knows how to shuffle, a Hand knows how to draw a Card from the Deck, a Card knows its value, etc.

Note the enum definition for Suit.  At this point in time, it requires newline as a separator between enum fields.  That makes this definition of Suit take more lines than in my original version.  UPDATE 11/15/2014: I would love to see the team add support for comma as a separator which would tighten up this definition to: Ask and you shall receive.  :-)  There is now semi-colon separator support for enum fields. The following works on PowerShell in build 9879 of the Windows 10 Technical Preview.

enum Suit {Clubs;Diamonds;Hearts;Spades} # in build >= 5.0.9879

The second thing to note is that none of the “methods” use the function keyword.  They are sans any keywords like function or def.  Just a return type (or [void] for no return type), the method name and the parameters.  Nice and succinct.

The third and probably most important difference to note is that every method that has a return value *must* use the return keyword.  This is a major difference from typical PowerShell functions.  In functions, any output that is not captured or redirected is streamed back to the caller.  In this regard, copying script from the command line and pasting it into a function doesn’t result in any behavioral differences.  All command output that isn’t captured in a variable or redirected is “output” from the function.  Class methods however, act more like traditional programming language methods.  They do not automatically stream output back to the caller.  So in a class method, you have to explicitly return the data from the method using the return keyword.

Another thing worth noting is how instances of your class get rendered.  Right now, sending an instance of a class to a Format-* command (or Out-Default by default) will result in the object either having its properties displayed – just like this was a PSCustomObject.  Or if there are no properties, just the class name gets displayed.  Inside of a double-quoted string e.g. “$hand”, the class name is displayed.  However, you can change this behavior by implementing a ToString() method in your class e.g.:

[string] ToString() { return '...Whatever makes sense...' }

You can see that I have done this for the Card, Deck and Hand classes.

I also want to show you the Pipe class:

# The hidden keyword is new to builds >= 5.0.9879 and hides the associated 
# fields for development (Intellisense) purposes.
class Pipe {
    hidden $PipeServer
    hidden $PipeReader
    hidden $PipeWriter

    Pipe() {
        $PipeServer = new-object IO.Pipes.NamedPipeServerStream('BlackJack', 
                                   [System.IO.Pipes.PipeDirection]::InOut)
        $PipeReader = new-object IO.StreamReader($PipeServer)
        $PipeWriter = new-object IO.StreamWriter($PipeServer)
    }

    [void] Dispose() {
        $this.PipeServer.Dispose()
    }

    [void] WaitForConnection() {
        $PipeServer.WaitForConnection()
        $PipeWriter.AutoFlush = $true
    }

    [string] ReadLine() {
        return $PipeReader.ReadLine()
    }

    [string] WriteLine([string]$msg) {
        $PipeWriter.WriteLine($msg)
        return $msg
    }
}

Note how it wraps up the StreamReader and StreamWriter as internal variables.

One feature I really want to see added to PowerShell classes is support for at least a private access modifier.  Like modules, I want to make many of my class properties visible only from inside the class.  And to a lesser extent I want to do that for some methods as well. UPDATE 11/15/2014: I have been informed that having a true “private” modifier would make the debug experience bad. The team has come up with a compromise via a new keyword – hidden.  This effectively tells the Intellisense feature to not display the class member when an instance of the class is being accesed from “outside” the class.  You will still get Intellisense of these members from within the class.  And when you are debugging you will be able to see the hidden fields.  That seems reasonable to me.

The nice thing about the code above is that the user of this Pipe class doesn’t have to deal with individual $PipeServer, $PipeReader or $PipeWriter objects, they just use the instance of this class – $pipe that is created in the main body of this script (see below) using the static new() method.  Using the new() method is how you create instances of your classes.  Note that class constructors can take parameters.  You can see this below in the call to the [Deck] constructor where I pass it the $pipe variable.  Here is the *complete* main body of the dealer script that uses classes:

$pipe = [Pipe]::new()
$deck = [Deck]::new($pipe)
$blackJackGame = [BlackJackGame]::new($pipe, $deck)
$blackJackGame.StartGameLoop()

Pretty simple eh?  Most of the game logic has been encapsulated in the BlackJackGame class.  Here are links to the full implementations of BlackJackDealer with Class.ps1 and BlackJackPlayer with Class.ps1.

For more information on using classes in PowerShell V5, check out Dan Harman’s talk on this topic at the European PowerShell 2014 Summit.

Posted in .NET, PowerShell, PowerShell 5.0 | 1 Comment

Windows PowerShell and Named Pipes

A named pipe is a stream-based mechanism for inter-process communication (IPC).  The .NET Framework has two types for allow you to use named pipes:

MSDN describes named pipes like so:

Named pipes provide one-way or duplex pipes for communication between a pipe server and one or more pipe clients. Named pipes can be used for interprocess communication locally or over a network. A single pipe name can be shared by multiple NamedPipeClientStream objects.

Being a .NET feature, named pipes are easily usable from PowerShell giving you a mechanism to communicate between separate PowerShell processes on the same machine or between different machines.  Note: another way of communicating between separate PowerShell processes in a very decoupled way is to use the Microsoft Message Queue (MSMQ) but that’s a topic for another blog post.

For this demonstration, I’ve chosen to implement BlackJack.  First a disclaimer, I’m no expert in BlackJack.  The implementation is from memory and is just basic BlackJack. There’s no support for doubling down, splitting, insurance, etc.  The point is to show how you can use named pipes to communicate between two PowerShell processes even from two different machines.

Before we get into the code, here is what a game session looks like:

BlackJackDealer:

PS C:\> .\BlackJackDealer.ps1
BlackJack dealer started
Waiting for client connection
Connection established
Connected to Keith.
Starting new game -----------------------------------------
Dealer's hand is Queen of Diamonds, hole card
Keith hand is King of Diamonds, 5 of Spades
Keith drew a 8 of Clubs, updated hand King of Diamonds, 5 of Spades, 8 of Clubs
DEALER's hand is Queen of Diamonds, King of Hearts
Keith busts with King of Diamonds, 5 of Spades, 8 of Clubs
DEALER wins with Queen of Diamonds, King of Hearts

BlackJackPlayer:

PS C:\> .\BlackJackPlayer.ps1 Keith-PC
Enter your name: Keith
BlackJack player connecting to dealer
Connected to dealer
Connected to Keith.
Starting new game -----------------------------------------
Deck empty, reshuffling deck
Dealer's hand is Queen of Diamonds, hole card
Keith hand is King of Diamonds, 5 of Spades
Enter H (hit me) or S (stand): h
Keith drew a 8 of Clubs, updated hand King of Diamonds, 5 of Spades, 8 of Clubs
DEALER's hand is Queen of Diamonds, King of Hearts
Keith busts with King of Diamonds, 5 of Spades, 8 of Clubs
DEALER wins with Queen of Diamonds, King of Hearts
Deal again? Y (yes) N (no):

And that’s why I don’t gamble.  Smile

Let’s get to the implementation which you can download in whole from by OneDrive via the two PowerShell scripts BlackJackDealer.ps1 and BlackJackPlayer.ps1.

First up is the dealer script:

 
$suits = 'Clubs','Diamonds','Hearts','Spades'
$ranks = 'Ace','2','3','4','5','6','7','8','9','10','Jack','Queen','King'

function GetShuffledDeck {
    $deck = 0..3 | Foreach {$suit = $_; 0..12 | Foreach { 
                      $num = if ($_ -eq 0) {11} elseif ($_ -ge 10) {10} else {$_ + 1}
                      [pscustomobject]@{Suit=$suits[$suit];Rank=$ranks[$_];Value=$num}}
                   }
    for($i = $deck.Length - 1; $i -gt 0; --$i) {
        $rndNdx = Get-Random -Maximum ($i+1)
        $temp = $deck[$i]
        $deck[$i] = $deck[$rndNdx]
        $deck[$rndNdx] = $temp
    }
    $deck
}

function GetValueOfHand($hand) {
    $sum = ($hand | Measure-Object Value -Sum).Sum
    if ($sum -gt 21) {
        $sum = ($hand | Foreach {if ($_.Value -eq 11) {1} else {$_.Value}} | Measure-Object -Sum).Sum
    }
    $sum
}

function IsHandBust($hand) {
    (GetValueOfHand $hand) -gt 21
}

function IsHandBlackJack($hand) {
    if ($hand.Length -ne 2) { return $false }
    (GetValueOfHand $hand) -eq 21
}

function DumpHand($hand) {
    $cards = $hand | Foreach {DumpCard $_}
    $OFS = ', '
    "$cards"
}

function DumpCard($card) {
    "$($card.Rank) of $($card.Suit)"
}

$cardNdx = -1
$deck
function DealCard {
    if ($cardNdx -lt 0) {
        WriteToPipeAndLog 'Deck empty, reshuffling deck' | Out-Null
        $script:deck = GetShuffledDeck
        $script:cardNdx = $deck.Length - 1
    } 
    $deck[$script:cardNdx--]
}

$pipeWriter
function WriteToPipeAndLog($msg) {
    $msg 
    $pipeWriter.WriteLine($msg)
}

$npipeServer = new-object System.IO.Pipes.NamedPipeServerStream('BlackJack', 
                              [System.IO.Pipes.PipeDirection]::InOut)
try {
    'BlackJack dealer started'
    'Waiting for client connection'
    $npipeServer.WaitForConnection()
    'Connection established'

    $pipeReader = new-object System.IO.StreamReader($npipeServer)
    $script:pipeWriter = new-object System.IO.StreamWriter($npipeServer)
    $pipeWriter.AutoFlush = $true

    $playerName = $pipeReader.ReadLine()
    WriteToPipeAndLog "Connected to $playerName."

    # Outer game loop
    while (1)
    {
        WriteToPipeAndLog 'Starting new game -----------------------------------------'
        $playerHand  = @(DealCard)
        $dealerHand  = @(DealCard)
        $playerHand += DealCard
        $dealerHand += DealCard

        WriteToPipeAndLog "Dealer's hand is $(DumpCard $dealerHand[0]), hole card"
        WriteToPipeAndLog "$playerName hand is $(DumpHand $playerHand)"

        $playerDealtBlackJack = IsHandBlackJack $playerHand
        $dealerDealtBlackJack = IsHandBlackJack $dealerHand

        if ($playerDealtBlackJack -and $dealerDealtBlackJack) {
            WriteToPipeAndLog "Both the Dealer and $playerName get BLACKJACK. The game is a push"
        }
        elseif (IsHandBlackJack $playerHand) {
            WriteToPipeAndLog "$playerName gets BLACKJACK and wins!"
        }
        elseif (IsHandBlackJack $dealerHand) {
            WriteToPipeAndLog "Dealer gets BLACKJACK and wins!"
        }
        else {
            # Let's play this hand
            $dealerBusts = $false
            $playerBusts = $false

            # Player's turn
            while (1) {
                $stand = $false
                $invalidKey = $false
                $pipeWriter.WriteLine("YOURMOVE")
                $command = $pipeReader.ReadLine()
                switch ($command) {
                    "H"     { }
                    "S"     { $stand = $true }
                    default { $invalidKey = $true }
                }

                if ($invalidKey) {
                    WriteToPipeAndLog "Sorry $playerName, didn't recognize command: $command"
                    continue
                }
                elseif ($stand) {
                    WriteToPipeAndLog "$playerName stands with hand $(DumpHand $playerHand)"
                    break
                }
                else {
                    $newCard = DealCard
                    $playerHand += $newCard
                    WriteToPipeAndLog "$playerName drew a $(DumpCard $newCard), updated hand $(DumpHand $playerHand)"
                    if (IsHandBust $playerHand) {
                        $playerBusts = $true
                        break
                    }
                }
            }

            # Dealer's turn
            WriteToPipeAndLog "DEALER's hand is $(DumpHand $dealerHand)"
            if (!$playerBusts) {
                do {
                    $dealerSum = GetValueOfHand $dealerHand
                    if ($dealerSum -gt 21) {
                        $dealerBusts = $true
                        break;
                    }
                    elseif ($dealerSum -ge 17) {
                        WriteToPipeAndLog "DEALER stands with $(DumpHand $dealerHand)"
                        break
                    }

                    $newCard = DealCard
                    $dealerHand += $newCard
                    WriteToPipeAndLog "Dealer draws $(DumpCard $newCard), updated hand $(DumpHand $dealerHand)"

                    Start-Sleep -Seconds 1
                } while (1)
            }

            # Determine who won
            if ($playerBusts) {
                WriteToPipeAndLog "$playerName busts with $(DumpHand $playerHand)"
                WriteToPipeAndLog "DEALER wins with $(DumpHand $dealerHand)"
            }
            elseif ($dealerBusts) {
                WriteToPipeAndLog "DEALER busts with $(DumpHand $dealerHand)"
                WriteToPipeAndLog "$playerName wins with $(DumpHand $playerHand)"
            }
            else {
                $dealerSum = GetValueOfHand $dealerHand
                $playerSum = GetValueOfHand $playerHand
                if ($dealerSum -gt $playerSum) {
                    $msg = "DEALER wins with $(DumpHand $dealerHand)"
                }
                elseif ($playerSum -gt $dealerSum) {
                    $msg = "$playerName wins with $(DumpHand $playerHand)"
                }
                else {
                    $msg = 'The game is a push'
                }
                WriteToPipeAndLog $msg
            }
        }

        $pipeWriter.WriteLine('ROUNDOVER')
        $pipeWriter.WriteLine('NEWDEAL')
        $command = $pipeReader.ReadLine()
        if ($command -eq 'EXIT') { break }
    }

    Start-Sleep -Seconds 2
}
finally {
    'Game exiting'
    $npipeServer.Dispose()
}

Note lines 62 – 71 of the dealer script is where I setup the server side of the named pipe.  It sits and waits at the WaitForConnection() call for a player to join the game.  The named pipe is a low-level, byte-oriented stream.  To make it simple to pass string messages and commands back and forth, I decorate the PipeStream with a StreamReader and StreamWriter.  I use those to write and read write strings to and from the client.  Once a player has joined, the game loop proceeds to send instructions by using the StreamWriter’s WriteLine() method to the player and it uses the StreamReader’s ReadLine() to receive the player’s instructions as a string e.g. “H” for hit and “S” for stand.

And here is the simpler, player script:

param ($ComputerName = '.')

$npipeClient = new-object System.IO.Pipes.NamedPipeClientStream($ComputerName, 'BlackJack', [System.IO.Pipes.PipeDirection]::InOut,
                                                                [System.IO.Pipes.PipeOptions]::None, 
                                                                [System.Security.Principal.TokenImpersonationLevel]::Impersonation)
$pipeReader = $pipeWriter = $null
try {
    $playerName = Read-Host 'Enter your name'
    'BlackJack player connecting to dealer'
    $npipeClient.Connect()
    'Connected to dealer'

    $pipeReader = new-object System.IO.StreamReader($npipeClient)
    $pipeWriter = new-object System.IO.StreamWriter($npipeClient)
    $pipeWriter.AutoFlush = $true
 
    $pipeWriter.WriteLine($playerName)
    $pipeReader.ReadLine()
    
    # Game loop
    while (1) {
        # Hand loop
        while (1) {      
            while (($msg = $pipeReader.ReadLine()) -notmatch 'YOURMOVE|ROUNDOVER') {
                $msg
            }
            if ($msg -match 'ROUNDOVER') { break }
            $command = Read-Host 'Enter H (hit me) or S (stand)'
            $pipeWriter.WriteLine($command)
        }

        while (($msg = $pipeReader.ReadLine()) -notmatch 'NEWDEAL') {
            $msg
        }
        $res = Read-Host 'Deal again? Y (yes) N (no)'
        if ($res -eq 'N') { 
            $pipeWriter.WriteLine('EXIT')
            break 
        }
        else {
            $pipeWriter.WriteLine('NEWDEAL')
        }
    }
}
finally {
    'Game exiting'
    $npipeClient.Dispose()
}

Lines 3 – 15 in the player script is where I set up the client side of the named pipe and connect to the server. Note the [System.Security.Principal.TokenImpersonationLevel]::Impersonation is required to make the connection work between two different machines.

There is a fair amount to the logic of the game that has nothing to do with named pipes but the above should give you a quick primer on how to use named pipes in your application. In summary, it is pretty straightforward:

  1. Create NamedPipeServerStream
  2. Wrap the server’s PipeStream in StreamReader/StreamWriter objects if you want to read/write string messages.
  3. Have the server pipe WaitForConnection
  4. Create NamedPipeClientStream in client script
  5. Wrap the client’s PipeStream in StreamReader/StreamWriter objects if you want to read/write string messages.
  6. Call Connect() to connect to the server.
  7. Start using StreamWriter.WriteLine() and StreamReader.ReadLine() to pass messages back and forth between the server and the client.

Stay tuned.  My next goal is to show you what this looks like using preview version of  PowerShell V5 classes.

Posted in .NET, PowerShell | 1 Comment

PSCX 3.2.0 Available

A new version of the PowerShell Community Extensions was released this morning on CodePlex.  PSCX 3.2.0 is also available on the PowerShell Resource Gallery Preview site which means you can use the new Install-Module command in WMF 5.0 Preview and Windows 10 Preview to install the module e.g.:

C:\PS> Install-Module Pscx -Scope CurrentUser

This new version fixes a number of reported bugs including an issue with directory listings under PowerShell v5.  The Import-VisualStudioVars command has been updated to work with Visual Studio 14 CTP.  You can now import Visual Studio environment variables based on the Visual Studio “version number” e.g. 140 for VS 14 CTP or the name/year 2010, 2012, 2013 e.g.:

C:\PS> Import-VisualStudioVars 140 -Architecture x86

Another minor feature we exposed in this release is a convenient way to convert decimal numbers to hex.  Today you do the following to convert a decimal number to hex:

C:\PS> "0x{0:X}" -f 5123123
0x4E2C33

With the update, you can do the same a bit more easily:

C:\PS> [hex]5123123
0x4e2c33

The major new addition for this release is the Edit-File command.  This command allows you to interactively open a file for editing as well as automate the editing of a file.  Here is how you would open a file for interactive editing:

C:\PS> Edit-File $profile.CurrentUserAllHosts

This starts notepad.exe by default and loads your profile.ps1 file.  You can change the text editor that is used by setting $Pscx:Preferences.TextEditor = ‘notepad2.exe’ in your profile.

However the real power of Edit-File (alias e) is that can you use it to automate editing files.  Today, this is how you would automate editing a file in PowerShell:

C:\PS> (Get-Content .\Pscx.csproj) -replace '>\s*v3.5\s*<', '>v4.5.1<' | 
           Out-File .\Pscx.csproj –Force

This works fine but has a couple of issues.  First, in order to write back to the same file PowerShell is reading from, you have to read the whole file into memory.  That is why the parentheses are around the Get-Content command.  This is no problem for most text files but if you have a huge file, it could be a problem.  The bigger issue is that Out-File by default writes the text file using Unicode encoding by default.  But this Visual Studio project file does not use Unicode encoding.  It is UTF-8 with no byte order mark or BOM.  You could certainly specify the encoding as a parameter to Out-File but that requires you to know the file’s encoding in the first place.  In addition, you may want to edit a series of files whose encoding varies.

Edit-File solves this problem by detecting the file’s encoding and using that same encoding when it writes back to the file.  Edit-File also solves the issue of editing large files by first, defaulting to “line-by-line” processing of the file.  If the file size is less than 84,000 the file is processed in memory.  If the file size is larger then a temp file is used to hold intermediate results.  These few small features make for a powerful command.  Here is the Edit-File equivalent of the previous command:

C:\PS> Edit-File .\Pscx.csproj '>\s*v3.5\s*<' '>v4.5.1<' –Force

Here are some sample usages of the Edit-File command:

# Open text editor with no file
C:\PS> Edit-File 

# Opens foo.txt in text editor for interactive editing
C:\PS> Edit-File foo.txt 

# Edits the file, replacing instances of foo with bar
C:\PS> Edit-File foo.txt -Pattern foo -Replacment bar 

# If file is readonly, makes it writeable and then replaces foo with bar
C:\PS> Edit-File foo.txt foo bar -Force 

# Same as above but regex is now case-sensitive
C:\PS> Edit-File foo.txt foo bar -CaseSensitive 

# Can take array of patterns/replacements - array sizes must match
C:\PS> Edit-File foo.txt 'foo','ba(.)' 'oof','$1ab' 

# Edit-File takes pipeline input that it can PassThru
C:\PS> GCI . -r *.txt | Edit-File -Pattern foo -Replacement bar -PassThru | 
           Copy-Item -Dest {'E:\temp' + $_.FullName.Substring(2).Replace('\','_')}

# Uses -SingleString to eliminate script between PostBuildEvent tags
C:\PS> GCI . -r *.csproj | 
           Edit-File -Pattern '(?s)(<PostBuildEvent>).*?(</PostBuildEvent)' `
                     -Replacement '$1$2' -SingleString 

# Replaces any empty line with '#--'.
C:\PS> Edit-File foo.txt '(?m)^(?=\r$)' '#--' -SingleString 

One note on the SingleString parameter used in the last two examples above.  Using this parameter causes the whole file to be read into memory as a single string.  Therefore, you may not want to use this parameter on huge (GB) text files.  However, what this parameter enables is the Multiline and Singleline regular expression modes.  This is crucial if your regular expression needs to span multiple lines.

The rest of the changes are outlined in the release notes.  This is the first release of Edit-File so there may be issues.  If you run into any, please file them on the PSCX CodePlex site.  Thanks for using PSCX and enjoy!

Posted in PowerShell, PowerShell 5.0, PSCX | 2 Comments

Windows PowerShell DSC Resource Kit Wave 5–xWindowsOptionalFeature

When PowerShell 4.0 shipped, the major new feature was DSC or Desired State Configuration – a very convenient and declarative way to manage the configuration of your Windows servers.  However as a developer whose IT department doesn’t really allow me anywhere near their servers, I’m interested in DSC from a purely Windows client SKU perspective.  I stand up new test machines and VMs all the time with various Windows client SKUs.  I was disappointed to find that the initial set of DSC resources for Windows clients was missing the equivalent of the WindowsFeature resource that is supported for Windows Server SKU configuration.  Without this resource, you can’t use DSC to configure your machine with features such as IIS, IIS-ASPNET45, etc. 

Well that all changes with the DSC Resource Kit Wave 5.  This version has an updated xPSDesiredStateConfiguration resource that includes xWindowsOptionalFeature.  Add to that the xOneGet resource for installing utilities like dotPeek, SysInternals, and Fiddler and now DSC is going further towards getting my test machines and VMs setup quickly for doing development work.

Here’s basically what I’m doing to configure, well at least start configure, my test machines and VMs.  I’m sure over time I will continue to add to this DSC configuration to automate more of the steps I’m currently doing manually.

First step is to go and grab the DSC Resource Kit Wave 5.  Second, you will need the July “experimental” version of PowerShell (WMF 5) to use these new resources.  You don’t want to install this on a production machine.  This would be a great excuse to spin up a Hyper-V image with Windows 8.1 on it to experiment with this.   To install the July version, go to the Requirements section of the website above and click on either the x64 MSU or x86 MSU links – depending on the bit-width of your OS installation.  Third, download the the resource kit ZIP file via the link at the top of the web page.  After you have downloaded it, be sure to “unblock” the zip before extracting any files from it.  Now fire up PowerShell “as administrator” and execute the following PowerShell commands. Note: the installation of xOneGet will prompt you to install nuget.exe.  Answer “y” to this to allow nuget to be installed. The new Install-Module cmdlets require nuget.

C:\> Set-ExecutionPolicy RemoteSigned –Force
C:\> Enable-PSRemoting –Force
C:\> Install-Module xOneGet
 

Now open the DSC Resource Kit Wave 5 zip you downloaded and unblocked.  Copy the directory xPSDesiredStateConfiguration to your C:\Program Files\WindowsPowerShell\Modules directory.  There should be two directories in there now: xOneGet and xPSDesiredStateConfiguration.

Now open up the 5.0 PowerShell_ISE and copy this script and save it as $home\DevPCConfig.ps1. The first section installs all the Windows optional features required to do ASP.NET development on .NET 4.5.  There is a section in there to modify settings of the console host for both x64 and x86 PowerShell.  Remove that or tweak to your satisfaction.  The last section installs some tools that I use all the time: dotPeek, Fiddler4 and PerfView.  To see what packages are available run:

C:\> Find-Package
 

Now, back to preparing to apply this configuration.  From the admin PowerShell console, cd to the same directory where you saved DevPCConfig.ps1 and execute the script.  Note: this doesn’t actually apply the configuration.  We will do that in the next step.

C:\Users\Keith> .\DevPCConfig.ps1
 

This will create the MOF file that drives the DSC engine.  You can see the file in .\DevPCConfig\localhost.mof.  Now let’s tell the DSC engine to apply this configuration to the machine by executing this command:

C:\Users\Keith> Start-DscConfiguration .\DevPCConfig –Wait –Verbose 
 

This will generate a lot of output but is informative the first time or two.  After that you can drop the –Verbose parameter. 

In an ideal world, I could use the community resource cPSGet to allow me to install my two favorite PowerShell modules but alas, bugs are preventing that from happening.  So as a manual step, I execute the following two commands to install these modules:

C:\> Install-Module Pscx –Scope CurrentUser 
C:\> Install-Module PSReadline –Scope CurrentUser
 

There is still plenty more I find myself tweaking on every new machine I set up.  Over time I’ll be able to “declare” more of that configuration in DevPCConfig.ps1 especially as more Microsoft and community resources become available.

Posted in PowerShell, PowerShell 5.0 | 2 Comments

How to Determine if a Process is 32 or 64 Bit

Your first instinct might be to check [IntPtr]::Size but that only works for the current PowerShell process.  In this scenario you want to check any arbitrary process running on the machine.  There doesn’t seem to be a .NET API for this but fortunately we can easily PInvoke to a Win32 API to get the functionality we need.  Here’s the script that does this:

Add-Type -MemberDefinition @' [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool IsWow64Process( [In] System.IntPtr hProcess, [Out, MarshalAs(UnmanagedType.Bool)] out bool wow64Process); '@ -Name NativeMethods -Namespace Kernel32 Get-Process -Id $id | Foreach { $is32Bit=[int]0 if ([Kernel32.NativeMethods]::IsWow64Process($_.Handle, [ref]$is32Bit)) { "$($_.Name) $($_.Id) is $(if ($is32Bit) {'32-bit'} else {'64-bit'})" } else {"IsWow64Process call failed"} }

Posted in PowerShell | Leave a comment