Advanced Windows PowerShell Scripting Video Training

Advanced Windows PowerShell Scripting Video Training
Advanced Windows PowerShell Scripting Video Training

Friday, February 26, 2016

Finding Your DSC Event Log Messages

Happy Friday!!!

Often when I am writing large amounts of code, I like to enter items into an event log of some type.  For those of you who have been sitting in my Advanced PowerShell classes where we use SAPIEN PowerShell Studio, you may have noticed that I will sometimes include a tabbed page where I have a log file.   

In DSC we have an Analytic log that we can send messages to using a DSC resource called Log,
PS C:\WINDOWS\system32> Get-DscResource -Name Log

ImplementedAs   Name   ModuleName                     Version    Properties        
-------------   ----   ----------                     -------    ----------        
Binary          Log    PSDesiredStateConfiguration    1.1        {Message, Depend...

This log file contains all of the verbose messages sent to the Analytic log.  

Look for Event ID 4098 with a ‘Warning’ level. 

Warnings are represented with the value of 3. Unfortunately, not all event 4098 will be yours.  Take a look at this configuration:

# Create your configuration.
Configuration TestMessage
{
    Import-DscResource -ModuleName PSDesiredStateConfiguration

    $Message = "`n `nConfigEventID: 001`n"
    $Message += "Task XYZ Completed`n"
    $Message += "`nAdd Whatever else you need to add"
    Node "localHost"
    {

        Log TestMessage
        {
            # This will write a message to the event log with event
            # ID 4098.

            Message = "$Message"
        }
    }
}

You can see that I am creating a custom message that I want to make sure is visually separated from the rest of the metadata presented in the events message.  My next set of code allows me to filter out all of the other information and just see my message.

# Create a hash for Get-WinEvent
$EventHash = @{LogName = 'Microsoft-Windows-DSC/Analytic'
              ID = 4098
              Level = 3}

# View the log files for our message.
(Get-WinEvent -FilterHashTable $EventHash -Oldest |
    Where-Object Message -Like "*ConfigEventID: 001*" |
    Select-Object -ExpandProperty Message).Split("`n")[6..10]

The result is:
ConfigEventID: 001
Task XYZ Completed


Add Whatever else you need to add

This may be a bit easier to read than this:
PS C:\PS> Get-WinEvent -FilterHashTable $EventHash -Oldest


   ProviderName: Microsoft-Windows-DSC

TimeCreated            Id   LevelDisplayName Message                                                                                                                      
-----------            --   ---------------- -------                                                                                                                      
2/25/2016 3:20:07 PM   4098 Warning          Job {4D5478C7-DC16-11E5-80BA-00155D130217} : ...                                                                                     
2/25/2016 3:22:02 PM   4098 Warning          Job {92101197-DC16-11E5-80BA-00155D130217} : ...                                                                                    
2/25/2016 3:23:51 PM   4098 Warning          Job {CEA254D0-DC16-11E5-80BA-00155D130217} : ...                                                                                    
2/25/2016 3:23:51 PM   4098 Warning          Job {CEA254D0-DC16-11E5-80BA-00155D130217} : ...                                                                                     
2/25/2016 3:24:32 PM   4098 Warning          Job {EBAAF632-DC16-11E5-80BA-00155D130217} : ...                                                                                     
2/25/2016 3:38:38 PM   4098 Warning          Job {E4202499-DC18-11E5-80BA-00155D130217} : ...   

Or this:
Job {1791C5F3-DC1F-11E5-80BA-00155D130217} :
Displaying messages from built-in DSC resources:
        WMI channel 1
        ResourceID: [Log]TestMessage
        Message : [S1]:                            [[Log]TestMessage]

ConfigEventID: 001
Task XYZ Completed


Add What ever else you need to
Job {2930B945-DC1F-11E5-80BA-00155D130217} :
Displaying messages from built-in DSC resources:
        WMI channel 1
        ResourceID: [Log]TestMessage
        Message : [S1]:                            [[Log]TestMessage]

ConfigEventID: 001
Task XYZ Completed

Add Whatever else you need to add

Just remember to enable the analytic and debug logs first.  Also, this may not be the best way to log information.  Remember that all verbose information will be kept in this log as well.




Wednesday, February 17, 2016

Using Calculated Properties with Select-Object

From time to time, I take a good look at how I am teaching PowerShell just to make sure that I am providing the most effective delivery.  That may be why when someone audits the class, they say they learn new material.  Besides, if I did not make changes, I would get bored.

Over the last few months I’ve been teaching how to create calculated properties with Select-Object a little differently.  This is a key skill to have so I need to make sure that we get it right.  Here is an explanation of how to create a custom property.

A custom property simply adds an additional property to an object in the PowerShell pipeline.  It does not change the original object, just the copy of it in the pipeline. Take a look at the properties of Get-Date.
PS C:\> Get-date | Select *


DisplayHint : DateTime
DateTime    : Thursday, February 11, 2016 7:46:10 PM
Date        : 2/11/2016 12:00:00 AM
Day         : 11
DayOfWeek   : Thursday
DayOfYear   : 42
Hour        : 19
Kind        : Local
Millisecond : 564
Minute      : 46
Month       : 2
Second      : 10
Ticks       : 635908167705642214
TimeOfDay   : 19:46:10.5642214
Year        : 2016

We are going to add a new property called OurDate. This new property will have a value consisting of the current year, month, and day in this format: YYYY-MM-DD.  Here is the code to accomplish this:
Get-Date |
    Select-Object -Property *,
    @{
        Name="OurDate";
        Expression={"$($_.Year)-$($_.Month)-$($_.Day)"}
    }

We first place a DateTime object into the pipeline by calling Get-Date. Next we pipe it to Select-Object.  We use the –Property parameter to select which properties to keep from the original object.  In this case, we use ‘*’ to keep all of them.  Next we add a second property.  This one is a hash table.
A hash table is a key/value pair.  In our case, a Property Name / Property Value pair.  Hash tables in PowerShell are denoted by @{}.  Inside of the curly braces is where we declare the property name and the property value.

The Name value provides us with the property name.  You can see that we set this value equal to a string of what we want to call this new calculated property, “OurDate”.

The Expression is the calculated part.  The actual calculation is done inside of its own set of curly braces ‘{}’. While calculating this property, you can reference the current object in the PowerShell pipeline by using one of two generic variables.  $_ is usable in all versions of PowerShell.  $PSItem is for PowerShell 3 and higher.  They both represent the current object in the PowerShell pipeline.  Because this variable represents the object, you have access to all the properties and methods of the object. In our example, we are creating a string that contains the values of 3 properties from the object in the pipeline.  Since we are creating this value as a string, each time we reference the object, we need to place it inside of a subexpression.  That would be the $() surrounding each call to the object.  Here is the end result.

DisplayHint : DateTime
DateTime    : Thursday, February 11, 2016 8:56:23 PM
Date        : 2/11/2016 12:00:00 AM
Day         : 11
DayOfWeek   : Thursday
DayOfYear   : 42
Hour        : 20
Kind        : Local
Millisecond : 151
Minute      : 56
Month       : 2
Second      : 23
Ticks       : 635908209831513532
TimeOfDay   : 20:56:23.1513532
Year        : 2016
OurDate     : 2016-2-11

Take a look at the last item.  It is our calculated property.
You can shorten this code up a bit:
Get-Date |
    Select-Object -Property *,
    @{
        N="OurDate";
        E={"$($_.Year)-$($_.Month)-$($_.Day)"}
    }

Or
Get-Date |
    Select-Object -Property *,
    @{N="OurDate" ; E={"$($_.Year)-$($_.Month)-$($_.Day)"}}

You can also look at the examples in the help file for Select-Object. Take a look at example #4 for another example of creating a calculated property.



Monday, February 15, 2016

Using Invoke-CIMMethod

In my PowerShell class last week, we took a lab a bit further.  This lab involved using the old WMI commands.  Well, that is no fun.  I was providing my answers to the lab using the new CIM cmdlets. I did this once before, but I forgot to update my answers.  You see, there is a difference in how you provide information to Invoke-CIMMethod and Invoke-WMIMethod

The objective was to change the Start Mode of the WinRM service.  To do this in WMI:

       Get-WmiObject –Class Win32_Service –Filter "Name='WinRM'" |
           Invoke-WmiMethod –Name ChangeStartMode –Argument 'Automatic'

You would think that you could do the same with the CIM cmdlets, but PowerShell did not like this.
     Get-CimInstance -ClassName Win32_Service –Filter "Name='WinRM'" |
        Invoke-CimMethod -MethodName ChangeStartMode -Arguments 'Automatic'
Invoke-CimMethod : Cannot bind parameter 'Arguments'. Cannot convert the "Automatic" value of type
"System.String" to type "System.Collections.IDictionary".
At line:2 char:65
+ ...   Invoke-CimMethod -MethodName ChangeStartMode -Arguments 'Automatic'
+                                                               ~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Invoke-CimMethod], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.Management.Infrastructure.CimCmdlets.I
   nvokeCimMethodCommand

Using Invoke-CIMMethod, the –Arguments parameter requires a System.Collections.IDictionary object.  Say what???? So, I took a look at the MSDN documentation for System.Collections.IDictionary.  In the Remarks section, I learned that this object is simply a key/value pair. 

My next stop was Win32_Service on MSDN. In particular, the ChangeStartMode method of the Win32_Service class. Here I found out the key is StartMode and the value is Automatic.



Now all that is left is to correctly format my command line.
     Get-CimInstance -ClassName Win32_Service –Filter "Name='WinRM'" |
      Invoke-CimMethod -MethodName ChangeStartMode -Arguments @{startmode='Automatic'}


The –Arguments parameter is now a key/value pair.

Wednesday, February 10, 2016

Adding Some Animation into Your GUI

Yesterday, I posted some code on how to draw on a GUI using transparent colors.  To further demonstrate this transparency, we are going to add some animation.


You can use your code from yesterday and just add to it.  The only object that you need to add is a timer control. Simply drag the timer control from the Toolbox in SAPIEN PowerShell Studio onto your form.


The timer control will appear under he actual form in the Designer tab.


Here are some differences from yesterday’s code.  (The entire code will be posted below)
First we need to set the bounds for the circles to move.  They will be bouncing off the walls.  Remember that the ellipse origin is the upper left corner, not the center.  That is why our right and bottom boundaries are 156 and not 256.  The ellipses have a size of 100 pixels.
# Set the X and Y max and min values.
$XMin = 0
$YMin = 0
$XMax = 156
$YMax = 156

We need to declare the starting positions of all three ellipses.  Since these values will be modified in events, we assign them to be script level variables. We need to provide both X and Y positions for all three ellipses.
# Set the starting values for the circles.
$Script:RX = 78 ; $Script:RY = 50
$Script:GX = 50; $Script:GY = 95
$Script:BX = 106; $Script:BY = 95

We need to provide a rate of movement each time our timer’s Tick event executes. Here we will send all three ellipses into different directions.
# Set the directions
$Script:RXMove = 1 ; $Script:RYMove = 1
$Script:GXMove = 1; $Script:GYMove = -1
$Script:BXMove = -1; $Script:BYMove = 1

We need to set an interval value for the timer.  The interval determines how long windows will wait before executing the Tick event of the timer.  It is in milliseconds.  Once set, we execute the Start method.
# Set the speed of the motion by controlling how fast the Timer ticks.
$Timer1.Interval = 10 # Milliseconds.

# Start the timer.
$timer1.Start()

In the paint event, we need to change the X and Y values to reflect or position variables as opposed to hard coding them.
# Create the rectangles that the circles will fit inside of.
$RRec = [System.Drawing.Rectangle]::New($Script:RX, $Script:RY, 100, 100)
$GRec = [System.Drawing.Rectangle]::New($Script:GX, $Script:GY, 100, 100)
$BRec = [System.Drawing.Rectangle]::New($Script:BX, $Script:BY, 100, 100)
      
# Paint the circles
$_.Graphics.FillEllipse($RBrush, $RRec)
$_.Graphics.FillEllipse($GBrush, $GRec)
$_.Graphics.FillEllipse($BBrush, $BRec)

The $timer1_Tick event is next.  We need to monitor the values of our position variables.  Since we are only changing them by a value of 1, we can look to see when they are equal to our boundaries.  If they do equal the boundary, then we multiple the movement direction by -1.  This reverses the movement’s directions.
# Change directions if a value hits a boundary
If ($Script:RX -eq $XMin -or $Script:RX -eq $XMax) { $Script:RXMove *= (-1) }
If ($Script:GX -eq $XMin -or $Script:GX -eq $XMax) { $Script:GXMove *= (-1) }
If ($Script:BX -eq $XMin -or $Script:BX -eq $XMax) { $Script:BXMove *= (-1) }

If ($Script:RY -eq $YMin -or $Script:RY -eq $YMax) { $Script:RYMove *= (-1) }
If ($Script:GY -eq $YMin -or $Script:GY -eq $YMax) { $Script:GYMove *= (-1) }
If ($Script:BY -eq $YMin -or $Script:BY -eq $YMax) { $Script:BYMove *= (-1) }

Next we commit the movement values to the current position values.

# Change the position values
$Script:RX += $Script:RXMove
$Script:GX += $Script:GXMove
$Script:BX += $Script:BXMove

$Script:RY += $Script:RYMove
$Script:GY += $Script:GYMove
$Script:BY += $Script:BYMove

Finally, we instruct the picture box, $PB1 to invoke its Paint event.  This will use the PictureBox Control’s double buffer to redraw the ellipses without any flickering.
# Tell the Paint box to redraw itself.
$PB1.Refresh()


Here is the code in its entirety.  Remember to set up the form in SAPIEN PowerShell Studio as described in yesterday’s post with the modifications from todays.

<#
===============================================================================
Transparent Color Demonstration with Animation
Jason A. Yoder
Twitter: @JasonYoder_MCT

Created with SAPIEN PowerShell Studio
===============================================================================
#>

# Create your brushes with their colors
$RBrush = [System.Drawing.SolidBrush]::New([System.Drawing.Color]::FromArgb(100, 255, 0, 0))
$GBrush = [System.Drawing.SolidBrush]::New([System.Drawing.Color]::FromArgb(100, 0, 255, 0))
$BBrush = [System.Drawing.SolidBrush]::New([System.Drawing.Color]::FromArgb(100, 0, 0, 255))

# Set the X and Y max and min values.
$XMin = 0
$YMin = 0
$XMax = 156
$YMax = 156

# Set the starting values for the circles.
$Script:RX = 78 ; $Script:RY = 50
$Script:GX = 50; $Script:GY = 95
$Script:BX = 106; $Script:BY = 95

# Set the directions
$Script:RXMove = 1 ; $Script:RYMove = 1
$Script:GXMove = 1; $Script:GYMove = -1
$Script:BXMove = -1; $Script:BYMove = 1

# Set the speed of the motion by controlling how fast the Timer ticks.
$Timer1.Interval = 10 # Milliseconds.

# Start the timer.
$timer1.Start()

# Events ______________________________________________________________________

$formTransparentrColorDem_Load={
       # No activities need to take place in the load event.
      
}#end formTransparentrColorDem_Load

$PB1_Paint=[System.Windows.Forms.PaintEventHandler]{
#Event Argument: $_ = [System.Windows.Forms.PaintEventArgs]
      
       # Create the rectangles that the circles will fit inside of.
       $RRec = [System.Drawing.Rectangle]::New($Script:RX, $Script:RY, 100, 100)
       $GRec = [System.Drawing.Rectangle]::New($Script:GX, $Script:GY, 100, 100)
       $BRec = [System.Drawing.Rectangle]::New($Script:BX, $Script:BY, 100, 100)
      
       # Paint the circles
       $_.Graphics.FillEllipse($RBrush, $RRec)
       $_.Graphics.FillEllipse($GBrush, $GRec)
       $_.Graphics.FillEllipse($BBrush, $BRec)
      
}#end PB1_Paint

$timer1_Tick={
      
       # Change directions if a value hits a boundary
       If ($Script:RX -eq $XMin -or $Script:RX -eq $XMax) { $Script:RXMove *= (-1) }
       If ($Script:GX -eq $XMin -or $Script:GX -eq $XMax) { $Script:GXMove *= (-1) }
       If ($Script:BX -eq $XMin -or $Script:BX -eq $XMax) { $Script:BXMove *= (-1) }
      
       If ($Script:RY -eq $YMin -or $Script:RY -eq $YMax) { $Script:RYMove *= (-1) }
       If ($Script:GY -eq $YMin -or $Script:GY -eq $YMax) { $Script:GYMove *= (-1) }
       If ($Script:BY -eq $YMin -or $Script:BY -eq $YMax) { $Script:BYMove *= (-1) }
      
       # Change the position values
       $Script:RX += $Script:RXMove
       $Script:GX += $Script:GXMove
       $Script:BX += $Script:BXMove
      
       $Script:RY += $Script:RYMove
       $Script:GY += $Script:GYMove
       $Script:BY += $Script:BYMove
      
       # Tell the Paint box to redraw itself.
       $PB1.Refresh()
} #end timer1_Tick