Thursday, July 10, 2014

Implementing a Custom PowerShell DSC Resource

Lately I’ve been working on custom Desired State Configuration resources, and learning as I go.

The basic pattern I’m following is below.  Basically I do the following:

  • Search for the target object
  • If found, compare the target object properties to the properties that I get from DSC

On thing I’ve latched onto is the $PSBoundParameters automatic variable.  It has been a handy way to loop through the properties that are considered ‘Desired’.  In my experimenting I’ve discovered that I also need to loop through the properties of the target object to identify properties that are not ‘Desired’ (this little discovery wasn’t a happy one since I have more work to do).

Anyhow, here is a snippet for the Test-TargetResource function:

Function Get-TargetResource

{

    # TODO: Add parameters here

    # Make sure to use the same parameters for

    # Get-TargetResource, Set-TargetResource, and Test-TargetResource

    param(

    )

}

 

Function Set-TargetResource

{

    # TODO: Add parameters here

    # Make sure to use the same parameters for

    # Get-TargetResource, Set-TargetResource, and Test-TargetResource

    param(

    )

}

 

Function Test-TargetResource

{

    # TODO: Add parameters here

    # Make sure to use the same parameters for

    # Get-TargetResource, Set-TargetResource, and Test-TargetResource

    param(

      [parameter(Mandatory = $true)]

             [System.String]

             $DisplayName,

 

             [System.String]

             $Description,

 

             [Switch]

             $Enabled,

 

             [System.String]

             $StringProperty1,

 

             [System.String]

             $StringProperty2,

 

             [System.String]

             $StringProperty3,

 

             [System.String]

            $StringPropertyWithSpecialComparison,

 

             [ValidateSet("Present","Absent")]

             [System.String]

             $Ensure

    )

 

    $PSBoundParameters

 

    $targetObject = Get-SomeTargetObject

 

    if ($Ensure -eq 'Present')

    {

        if ($targetObject -eq $null)

        {

            Write-Verbose "Target Object '$DisplayName' not found."

            return $false

        }

        elseif ($targetObject -is [array])

        {

            Write-Verbose "Mulitple Target Objects found.  This will be corrected by deleting the objects then creating a new one based on the desirable state."

            return $false

        }

        else

        {

            Write-Verbose "Target Object found, diffing the properties: $($targetObject.ObjectID)"

            $objectsAreTheSame = $true

            foreach ($boundParameter in $PSBoundParameters.GetEnumerator())

            {

                Write-Verbose "Comparing $($boundParameter.Key)"

                switch ($boundParameter.Key)

                {

                    {$_ -in @(

                        'StringProperty1'

                        'StringProperty2' 

                        'StringProperty3'              

                    )} {

                        Write-Verbose " From DSC: $($boundParameter.Key)"

                        Write-Verbose " From Obj: $($targetObject.($boundParameter.Key))"

                        if ($boundParameter.Value -ne $targetObject.($boundParameter.Key))

                        {

                            Write-Verbose " Target Object property is not the same."

                            $objectsAreTheSame = $false

                        }

                    }

                    'StringPropertyWithSpecialComparison' {

                        Write-Verbose " From DSC: $($boundParameter.Key)"

                        Write-Verbose " From Obj: $($targetObject.SpecialPropertyNameThatDoesNotMatchTheBoundParameter)"

                        if ($boundParameter.Value -ne $targetObject.SpecialPropertyNameThatDoesNotMatchTheBoundParameter)

                        {

                            Write-Verbose " Target Object property is not the same."

                            $objectsAreTheSame = $false

                        }

                    }

                }#Closing: switch

            }#Closing: foreach 

        }#Closing: else

    }#Closing: if ($Ensure -eq 'Present')                 

}#Closing: Function Test-TargetResource

 

Neatly Formatting a HashTable in Verbose Output

Had a problem where the Verbose output I was emitting looked like crap because all of the entries were neatly aligned until I tried to share the contents of a hash table.

Nicely formatted verbose output seems obsessive until you have to consume it.  Keeping the verbose output pretty makes it easier to spot issues when things go wrong.  If the details you’re after are all over the screen then it will be too difficult to scan, so you’ll miss stuff.

First, we need to turn up the verbose noise, and create a sample hashtable.

 

### Turn verbose noise up

$VerbosePreference = 'Continue'

 

### Sample table, of hash

$hashTable = @{

    foo     = '10'

    bar     = '2'

    fitzbar = '300000'

    baz     = '400'

}

 

 

The way I had been outputting is like this:

 

Write-Verbose "This is nicely aligned: foo"

Write-Verbose "This is my hashtable: $($hashTable | Out-String)"

Write-Verbose "This too is nicely aligned and easy to scan"

 

Which produced this (below). Not bad, the details are there but it doesn’t make it easy to scan the verbose output because my eyes no have to dart over to the left.

 

 

VERBOSE: This is nicely aligned: foo

VERBOSE: This is my hashtable:

Name                           Value                                           

----                           -----                                           

baz                            400                                             

bar                            2                                               

foo                            10                                              

fitzbar                        300000                                          

 

 

 

VERBOSE: This too is nicely aligned and easy to scan

 

 

A couple more lines of code fixed that problem:

 

### Find the longest Key to determine the column width

$columnWidth = $hashTable.Keys.length | Sort-Object| Select-Object -Last 1

 

### Output the HashTable using the column width

Write-Verbose "This is nicely aligned: foo"

Write-Verbose "This is my hashtable: "

$hashTable.GetEnumerator() | ForEach-Object {

    Write-Verbose ("  {0,-$columnWidth} : {1}" -F $_.Key, $_.Value) -Verbose

}

Write-Verbose "This too is nicely aligned and easy to scan"

 

Now I get each hashtable entry on its own line, nicely aligned.

 

VERBOSE: This is nicely aligned: foo

VERBOSE: This is my hashtable:

VERBOSE:   baz     : 400

VERBOSE:   bar     : 2

VERBOSE:   foo     : 10

VERBOSE:   fitzbar : 300000

VERBOSE: This too is nicely aligned and easy to scan

 

Free eBooks Courtesy of PowerShell.org

FYI – there’s a bunch of free eBooks on PowerShell.org.  The one I’m currently interested in is the DSC Book.

http://powershell.org/wp/ebooks/

DSC is an awesome feature of Windows (yes, built into the server!) and my current pet project for FIM configuration management.