PowerShell classes and password gizmos…

Over the years I have rewritten every useful chunk of code I’ve found according to what my skill could provide and also (much less often, I’ll admit) what PowerShell offered at the time. PowerShell introduced classes a few months back and I’ve decided it’s time to update some more code. This time, I’m going to try to make a PowerShell class to handle my credentialing needs. And trust me, my credentials are needy.

I’m going to use a couple of functions from previous articles that will merge here. In my first ever post about accessing MySQL programmatically, I showed you a function called myCStr (I used ‘filter’ in the post) that will create a database connection string with a rudimentary database query tool. Later on I ranted about another way to handle authentication with PowerShell secure strings and trimmed down the original


filter myCStr {
    param (
        [System.String]$Server,
        [System.String]$uid = $env:USERNAME,
        [System.String]$pwd = (get-mycreds).GetNetworkCredential().password
    )

    if ($Server -eq "myFavServer") {
        $myCStr = "Server=$Server;port=23306;uid=$uid;pwd=$pwd"
    }
    if ($Server -eq "my2ndFavServer") {
        $myCStr = "Server=$Server;port=33306;uid=$uid;pwd=$pwd;"
    }
    else {
        $myCStr = "Server=$Server;port=3306;uid=$uid;pwd=$pwd;"
    }
    $myCStr
}

In that article I also made a ‘password gizmo’

function set-mycreds{
    param(
        [string]$username = $env:USERNAME,
        [string]$message = "mycreds for mysql"
    )
    $creds = get-credential -Message $message -UserName $username
    $global:myCred = $creds.Password
}
function get-mycreds {

     if(-not $myCred){ set-mycreds }
     [PSCredential]::new( $env:USERNAME, $myCred )

}

They work together to create a reliable data object. By reliable, I mean I always know what I am going to get from it. Since it’s so trusty I can use it in a diverse set of coding circumstances and I am pretty sure I can make it handle 100% of my shell console authentication needs. What would this look like if I were to use it as a PowerShell class? Well, what would a basic authentication ‘class’ look like? I guess we need a class structure that has user name and password properties

class myCreds
{
    [string]$UserName
    [string]$Password
}

This is similar to using New-Object (or some variation) to spin up a PSObject or Add-Type with a definition, this way just takes a lot less code, but stores the password as plain text in memory.

PS C:\bjm\pwrshl> $myCreds = [myCreds]::new()
PS C:\bjm\pwrshl> $myCreds.UserName = "brendan"
PS C:\bjm\pwrshl> $myCreds.Password = "brendansPassword"
PS C:\bjm\pwrshl> $myCreds | ft -a

UserName Password
-------- --------
brendan brendansPassword

PS C:\bjm\pwrshl>

It would be advantageous for the class to possess a constructor, that will make it much more efficient to instantiate (create) an instance.

class myCreds
{
    [string]$UserName
    [string]$Password
    myCreds ([string]$UserName, [string]$Password)
    {
        $this.UserName = $UserName
        $this.Password = $Password
    }
}

Notice the constructor uses ‘strongly typed’ input params, that is, they both have [string] in front of the $variable name (we’ll get back to that). Now I create the $myCreds array with a single command.

PS C:\bjm\pwrshl> $myCreds = [myCreds]::new('brendan','brendansPassword')
PS C:\bjm\pwrshl> $myCreds | ft -a

UserName Password
-------- --------
brendan brendansPassword

PS C:\bjm\pwrshl>

While there are applications for something like this with Invoke-WebRequest, MySQL, ssh, plink or pscp and other plaint-text auth, it is not very versatile. Now I have to type my UserName… I also miss the fact that my password is no longer encrypted… Add to that, most of my authentication is AD based and requires a credential object, like I used to have. I can flip this over, though, and make it a little more like my function.

class myCreds
{
    [string]$UserName
    [SecureString]$Password

    static $script:myCreds = [myCreds]::set()

    static [void] set()
    {
        if ( -not $script:myPw ) {
            $script:myPw = (
                Get-Credential -UserName $env:USERNAME -Message "$($env:USERNAME)'s password"
            ).Password
        }
    }
    static [pscredential] get()
    {
        return [pscredential]::new($env:USERNAME, $script:myPw)
    }
}

Before I go on, let me explain a little bit. Line 4:

[SecureString]$Password

$Password is no longer a [string], it’s a [SecureString]. At this point, it doesn’t really matter because I’m never going to return a [myCreds] object anymore, this class only returns a [PSCredential]. Should I need a clear text copy of my password, [PSCredential] can easily create it for me.

PS C:\bjm\pwrshl> [myCreds]::get().GetNetworkCredential().Password
brendansPassword

On line 6:

static $script:myCreds = [myCreds]::set()

I call (instantiate) the class, this is sort of a ‘short cut’. Without this line I would have to make a set call to load the $myPw variable, with that tidbit I can just get an instance. If the $script:myPw variable is not set I will get prompted for my password. As a bonus, when I do get prompted I will no longer be hassled with typing the seven letters of my first name!

On line 8 I have replaced the constructor syntax with a set method. I have also removed ConvertFrom-SecureString, which was a holdover from when I wrote passwords into a file and then read them when during run time.

static [void] set()
{
    if ( -not $script:myPw ) {
        $script:myPw = (
            Get-Credential -UserName $env:USERNAME -Message "$($env:USERNAME)'s password"
        ).Password
    }
}

On line 11 I have reduced the scope of the variable that contains the Secure.String password. Formerly I loaded this into $global: but after consideration, it doesn’t need to leave my script.

$script:myPw = (

Line 16 starts the get method. The get method reads the $env:USERNAME and $script:myPw variables and gives out a [PSCredential] object.

static [pscredential] get()
{
    return [pscredential]::new($env:USERNAME, $script:myPw)
}

Now, my class has all the functionality of the functions I used to use. Right now, the code takes a few more lines than the functions did. It will make up the difference when I consume the object. Plus it’s a PowerShell class now, so I can inherit it’s properties to do some other cool stuff. For now, I just am just going to blend my new class with my old function.

filter myCStr {
param (
[System.String]$Server,
[System.String]$uid = [myCreds]::get().UserName,
[System.String]$pwd = [myCreds]::get().GetNetworkCredential().Password
)

if ($Server -eq "myFavServer") {
$myCStr = "Server=$Server;port=23306;uid=$uid;pwd=$pwd"
}
if ($Server -eq "my2ndFavServer") {
$myCStr = "Server=$Server;port=33306;uid=$uid;pwd=$pwd;"
}
else {
$myCStr = "Server=$Server;port=3306;uid=$uid;pwd=$pwd;"
}
$myCStr
}
PS C:\bjm\pwrshl> myCStr MyMySQLServer
Server=MyMySQLServer;port=3306;uid=brendan;pwd=brendansPassword;

So there you have it, yet another way to handle credentials! I hope this information comes in handy for conjuring a PSCredential in your shell or creating MySQL connection strings dynamically. Next time I plan to use these same functions to demonstrate even more advanced features of PowerShell classes.

When writing this article I used a few other sites for reference:
Michael Willis@xainey gives an indepth discussion of classes and some great examples.
ps1 from PowerShell.com/Idera has 6 short articles to get you started creating your own classes. If you aren’t getting their Daily Tips email, I highly recommend them.
And what would a Google search for PowerShell info be without a link to an article by Ed Wilson (a.k.a. The Scripting Guy). Here he gives his down and dirty on classes (and mispells Camaro).

Advertisements
This entry was posted in powershell and tagged , . Bookmark the permalink.

Leave a Reply

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

WordPress.com Logo

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

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s