Wednesday, August 12, 2015

Use Powershell: Event Viewer with GUI

Powershell scripts enable you to take solve a problem using a repeatable method. The solution is even better when you don't have to run it every time the problem occurs. Lets face it, adding a GUI increases the audience and usability or your scripts. Especially when the proposed solution is used by co-workers who have zero experience in scripting (I mean that in a sincerely nice way).

A personal example: As a former Systems Engineer, and current Support Specialist with my current employer, I find myself and my peers constantly reviewing event logs to see which component of the software blew up. When an environment goes down, efficiency is key. The fallout from unscheduled downtime for an extended amount of time really isn't something anyone needs to deal with (especially as a vendor). That being said, having a set of troubleshooting tools ready to go is vital. As part of a small group of team members supporting a large number of enterprise clients, we need to support each other in anyway we can. It should be obvious where I'm going with this. The interactive Powershell scripts I develop and release to team members are useless if they have to go around digging through a console output and changing variables to get to the root of a problem or perform a simple task. This is where the GUI comes into play. As mentioned before, anything I can do to improve efficiency is a bonus. Here's on example of a tool I through together to view event logs which prevents me from waiting for Server Manager from loading or going in to the appropriate MMC snap-in. For those who have more advanced methods of accessing this information, then just take this for what its worth and enjoy learning to draw a GUI for your solutions.

Final remarks before getting started: While the initial code to these scripts may look daunting, after reviewing the various sections, it should be quite apparent how the windows form is drawn, data is populated, and actions are applied to the buttons (you'll find that actions can be triggered in many different cases, but we're limiting this to a button for now).

Lets Draw a Box:


Step 1 is to load the appropriate assemblies that allow us to draw the .NET windows form elements (the stuff that draws the GUI). We do this by adding the following lines at the beginning of our script since we can't draw the form before these are loaded:
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
FYI: [void] suppresses output from loading these assemblies.

After we load the assemblies, we ready to draw our first box. It's recommend you play around with this in Powershell ISE or PowerGUI so you can make changes on the fly and easily execute your script over and over. The next block of script will create a new form. Line 1 creates a the form element. Line 2 defines the text in the title bar. Line 3 sets the size. Line 4 defines the start position. And finally, line 5 displays the form.
$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = "PS Event Viewer"
$objForm.Size = New-Object System.Drawing.Size(300,350) 
$objForm.StartPosition = "CenterScreen"
[void] $objForm.ShowDialog()

Now click play in ISE or run your script. You should have output similar to below:

Excercise: Change the text title bar text.

Congrats, your first Windows Form built purely in Powershell!

Adding Form Controls:


Adding form controls is very similar to creating the form. We loaded the assemblies and created the form element. Let's dive in and add the controls. First we'll start off with only the script block related to displaying the label control in the form. Line 1 creates a new label form control. Line 2 sets the x,y location coordinates in the parent form element (the form we just created). Line 3 sets the control size. Line 4 is the text being displayed in the control. Line 5 adds the control to the form (think parent/child).
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,10) 
$objLabel.Size = New-Object System.Drawing.Size(350,40) 
$objLabel.Text = "The power of Powershell `nHello World!"
$objForm.Controls.Add($objLabel)

In order to properly demonstrate this, we need to tie this in to the script from the previous section. While this slightly varies from the finished product, it's important to see the basic structure and build on that. You'll also note that the show form line ([void] $objForm.ShowDialog() ) has been moved to the bottom. We don't want to display the windows form until all controls have been created and added. Refer to the inline comments to see how we added this to the script.
$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = "PS Event Viewer"
$objForm.Size = New-Object System.Drawing.Size(300,350) 
$objForm.StartPosition = "CenterScreen"

######### NEW SECTION #########

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,10) 
$objLabel.Size = New-Object System.Drawing.Size(350,40) 
$objLabel.Text = "The power of Powershell `nHello World!"
$objForm.Controls.Add($objLabel) 

###############################

[void] $objForm.ShowDialog()

Once again, run this script. If you copy and pasted my script above, you should get the following output:
Excercise: Change the text and properties of the label control.

Understanding what we did above, it should be reasonable to assume the next additions to our code will make sense without reviewing each form control. Those familiar with VB, will recognize the controls instantly. Those who aren't, we'll get to that in later posts. Next we'll add a listbox and button control.
$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = "PS Event Viewer"
$objForm.Size = New-Object System.Drawing.Size(300,350) 
$objForm.StartPosition = "CenterScreen"

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,10) 
$objLabel.Size = New-Object System.Drawing.Size(350,40) 
$objLabel.Text = "Created by Zachary Higgins (higginsps.blogspot.com) `n`nSelect Log Name:"
$objForm.Controls.Add($objLabel) 

######### NEW SECTION #########

$objListBox = New-Object System.Windows.Forms.ListBox 
$objListBox.Location = New-Object System.Drawing.Size(10,60) 
$objListBox.Size = New-Object System.Drawing.Size(260,20) 
$objListBox.Height = 210

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,275)
$OKButton.Size = New-Object System.Drawing.Size(150,23)
$OKButton.Text = "OK"

$objForm.Controls.Add($OKButton)
$objForm.Controls.Add($objListBox) 

###############################

[void] $objForm.ShowDialog()

And the output...

Populating Form Control With Data:


Some form controls can be populated with data that we use to execute blocks of script later on. In this case, we get the names of the event logs available on the server or workstation, and add an entry for in the list box control for each one. First and foremost, we need to understand how we get the objects (log names) being populated in the list box control.

In a Powershell Terminal, execute a get-eventlog -list. You should get something similar to the following (maybe more, maybe less, the beauty of this script is that it doesn't matter):

Now we take this information, and apply this to the form control. Again, the new section of code has been added in between the commented lines. First we declare $eventlogs and populate that variable with our event log names. Note that we're only selecting the log names since that's all we need to reference when we pull the actual events from that log. If we had additional text, it would need stripped away, adding further complication outside the scope of this write-up. Once $eventlogs is declared, we drop into a foreach statement and run through each object (log name) which is declared as $eventlog. Each event log name is added to the listbox control ($objListBox) as an item. This foreach continues until each event log name has been added.
$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = "PS Event Viewer"
$objForm.Size = New-Object System.Drawing.Size(300,350) 
$objForm.StartPosition = "CenterScreen"

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,10) 
$objLabel.Size = New-Object System.Drawing.Size(350,40) 
$objLabel.Text = "Created by Zachary Higgins (higginsps.blogspot.com) `n`nSelect Log Name:"
$objForm.Controls.Add($objLabel) 

$objListBox = New-Object System.Windows.Forms.ListBox 
$objListBox.Location = New-Object System.Drawing.Size(10,60) 
$objListBox.Size = New-Object System.Drawing.Size(260,20) 
$objListBox.Height = 210

######### NEW SECTION #########

$eventlogs = (Get-EventLog -List).Log
foreach($eventlog in $eventlogs){
    [void] $objListBox.Items.Add("$eventlog")
}

###############################

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,275)
$OKButton.Size = New-Object System.Drawing.Size(150,23)
$OKButton.Text = "OK"

$objForm.Controls.Add($OKButton)
$objForm.Controls.Add($objListBox) 
[void] $objForm.ShowDialog()

Once again, execute the script thus far and view the new output. We're almost done!

Performing An Action With Form Control:


We have our data populated in the list box form control. Now lets do something with it. We'll apply an action to the "OK" button which outputs all events in the selected log to an out-gridview format. This action can be demonstrated by executing the following cmdlet in a separate Powershell terminal: Get-EventLog -LogName "Application" | Out-GridView. The out-gridview will output something similar to below:

Simply add the next property to the button form control we defined earlier. Quite literally, one line. More advanced actions can be defined here, however let's keep it simple for now.
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,275)
$OKButton.Size = New-Object System.Drawing.Size(150,23)
$OKButton.Text = "OK"

######### NEW SECTION #########

$OKButton.Add_Click({$eventlog=$objListBox.SelectedItem;Get-EventLog -LogName $eventlog | Out-GridView})

###############################


To see how this all fits together, compare this to the full script below. When running this script, selecting the log name and pressing OK, will take that selected log name and output all events to the out-gridview we went over earlier.

This concludes creating a basic GUI in Powershell (with functionality). You can grab the completed script below. I encourage the act of vandalism and overall breakage of scripts when learning. Take this, and create something else!

As always, comment/question/request below!
# CREATED BY ZACHARY HIGGINS
# http://higginsps.blogspot.com

$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = "PS Event Viewer"
$objForm.Size = New-Object System.Drawing.Size(300,350) 
$objForm.StartPosition = "CenterScreen"

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,10) 
$objLabel.Size = New-Object System.Drawing.Size(350,40) 
$objLabel.Text = "Created by Zachary Higgins (higginsps.blogspot.com) `n`nSelect Log Name:"

$objListBox = New-Object System.Windows.Forms.ListBox 
$objListBox.Location = New-Object System.Drawing.Size(10,60) 
$objListBox.Size = New-Object System.Drawing.Size(260,20) 
$objListBox.Height = 210

$eventlogs = (Get-EventLog -List).Log
foreach($eventlog in $eventlogs){
    [void] $objListBox.Items.Add("$eventlog")
}

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,275)
$OKButton.Size = New-Object System.Drawing.Size(150,23)
$OKButton.Text = "OK"
$OKButton.Add_Click({$eventlog=$objListBox.SelectedItem;Get-EventLog -LogName $eventlog | Out-GridView})

$objForm.Controls.Add($objLabel) 
$objForm.Controls.Add($OKButton)
$objForm.Controls.Add($objListBox) 
[void] $objForm.ShowDialog()

No comments:

Post a Comment