Listen to me on Coding101!

November 10, 2015 FoxDeploy

Last week, I had an amazing time at the Microsoft MVP Summit, it was a dream come true!  Speaking of true, I even got to meet Jim Truher and Bruce Payette!  Simply a wonderful, wonderful time.

Probably my favorite part about being there was getting to be on a live podcast recording for TWiT network’s Coding 101 show, along with Sarah Dutkiewicz, Adam Bertram, Jeremy Clark, June Blender, & Jeff Wouters!  We got to talk about what got us into coding, how to become an MVP, and our favorite (and worst) interview questions.  I feel like I did pretty well, despite my heart trying to pound out of my chest.

https://twit.tv/shows/coding-101/episodes/90?autostart=false

Let me know how you think I did!

Continue Reading...

Using PowerShell and oAuth

November 02, 2015 FoxDeploy

the oAuth Flow

Like most of my posts here, I’m going to try to make something sound easy, when in reality I’ve spent months crying into my coffee trying to understand it. In truth, I’ve been trying to get oAuth to work for more than a year now.

It all started with a simple goal, I just wanted to check my blog stats easily using WordPress’s REST API, and I wanted to do it from PowerShell, should be simple, right? WRONG

My initial issue was that I didn’t want to understand oAuth, I just wanted to copy and paste some stuff and hope that it worked. I can tell you now that I’ve worked it out, it really isn’t that difficult, but knowing what’s happening will make it all much easier.

What is oAuth?

oAuth was made to solve a problem, that of sharing information between two different web services. We need oAuth because a user may want to right click a file from Dropbox.com and post it on Facebook with one click, for instance. Or have their Twitter send a Tweet when they update their Blog on WordPress. oAuth is a crucial verification step when tying two services together, and it’s worth the time to spend learning how it works. Furthermore, most of the coolest REST APIs out there require you to authenticate using oAuth in order to even use them.

Now, let’s see what the steps are to get your application (or script) linked to a service that uses oAuth.

The Core Steps of oAuth

oAuth isn’t too complicated. Here’s what a normal oAuth exchange looks like. This happens the first time a user links two services (or instead links PowerShell or Python to a web service) and generally is a one time thing.

Prompt the User for permission

We’ll prompt the user with a login window, they’ll sign in and if they agree with the permissions we’re asking for, we receive an Access Code

Exchange the Access Code for an Authorization Token

We’ll submit that code in exchange for an authorization token. This is a one-time thing, as most auth tokens last quite a long time.

Use the Authorization Token for future requests

Now that we’ve got an Authorization Token, we can use this over and over to make action on behalf of our user.

The last piece of info to know is that there are special URLs to use for each stage of the authorization process. For the rest of this post, assume I’m talking about Wordpress.com, which requires you to use oAuth for authentication before you can start using their API.

All of the code I’m referring to here can be found in the PSWordPress repo on GitHub. In my implementation of oAuth for WordPress, all of the following steps (except signing up for the API) are performed within the Connect-WordPress Account cmdlet, if you’d like to follow along.

How to begin - Register for the API

Any service which uses oAuth as the gateway to a REST API is going to have some mechanism in place for you to register for the API. If you want to use the WordPress.com API, for instance, you’ll register for it here.  Just to be clear, you’re not signing up for oAuth, rather oAuth is simply the mechanism that let’s your users trust your app or service with a bit of access to their account from something else, say, Twitter or FaceBook.

Signing up for an API will normally involve you picking out a name for your project, providing a thumbnail image, and providing the URL of your application. Because the primary use case of oAuth is to allow services to talk to each other, even though we’ll be using PowerShell, we still must provide a URL. I’m using the URL of my blog, FoxDeploy.com, but you could pick literally any URL in the world.

The key things you need to copy when you sign up are in this list below.  You’ll need to use them when you send the user off to the approval page, and then you’ll also need it when you exchange the access code for an authorization token:

  • ClientID, a numerical ID WordPress assigned us.
  • ClientSecret, a randomly generated strong password assigned to us. Generally, keep this secret. It’s not such a big deal for WordPress but some APIs will require payment, so this could be as costly as giving out a credit card number.
  • A list of URLs we need to query for different things

oAuth Info

Copy all of this stuff down somewhere safe (don’t put it in a GitHub project, for instance!)

Get an Access Token

The first real step in implementing oAuth, once you’ve got your Client details and the URLs, is to display a logon box to a user.

This step is mostly prompting a user to provide you their credentials to a given service. We can grab the URL that we saw when we signed up for the API, and send the user there, providing a few values to represent your application/project, and then the user will login. When the user signs in, they’ll be redirected to the URL we specify, and the access token will be embedded at the end of the address they go to; we need to simply substring out the Access token and we’re set.

Taking a look at the oAuth page for WordPress, we see where we need to provide our values to make the login box appear.

If you were to plop in values for clientID, and blogID, you’d see a standard oAuth sign in window, like this:

oAuth Window

To display a login box from PowerShell, you should use the cmdlet I’ve got here, Show-oAuthWindow. In this next section, we’ll dig into what this is actually doing, so that you can swap parts in and out as needed to fit whatever you’re working on.

Showing a login window from PowerShell

Show-oAuthWindow is where the magic happens. I found this example on the Hey! Scripting Guy Blog, written by Microsoft PowerShell PFE (Wow, talk about my dream job!) Chris Wu. It’s really beautifully simple.

An assembly is loaded into PowerShell, to allow us to display some .net forms objects. Then the window size is specified, and we provide the URL for the browser window to go to (the ‘Authorize’ URL from my project screenshot above). We watch the process and instruct the window to close when the user is redirected to an address with Error= or code= in the URL.

Function Show-OAuthWindow { Add-Type -AssemblyName System.Windows.Forms $form = New-Object -TypeName System.Windows.Forms.Form -Property @{Width=440;Height=640} $web = New-Object -TypeName System.Windows.Forms.WebBrowser -Property @{Width=420;Height=600;Url=($url -f ($Scope -join "%20")) } $DocComp = { $Global:uri = $web.Url.AbsoluteUri if ($Global:Uri -match "error=\[^&\]\*|code=\[^&\]\*") {$form.Close() } }

Show-OAuthWindow -URL "https://public-api.wordpress.com/oauth2/authorize?client\_id=$clientID&redirect\_uri=$blogURL&scope=global&response\_type=code"


If you were to run this code, you’d see a login box like this one:

The user clicks Approve, and then our Access Code comes back in the form of some characters appended to the URL

The user clicks Approve, and then our Access Code comes back in the form of some characters appended to the URL

Assuming the user interacts with this successfully, they will be redirected to the $redirectURL, and our Access Code will be within the URL, looking something like the following: [https://foxdeploy.com/?code=cw9hk1xG9k](https://developer.wordpress.com/?code=cw9hk1xG9k).  We’ll store the address in $uri, make it a global variable and then close the window, and we’re left with a $URI value that includes our access code.  We merely need to regex out the Access Code.

$regex = '(?<=code=)(.\*)(?=&)' $authCode = ($uri | Select-string -pattern $regex).Matches\[0\].Value $global:accessCode = $accessCode Write-output "Received an accessCode, $accessCode"

At this point, we have the Access Code, and it’s time to cash it in for an Authorization Code.

Trade the Access Token to get an Auth Token

Now that we have the time sensitive Access Token, it’s time to cash that in for a mostly-permanent authToken. Looking back at the WordPress API Docs again, they say the following (referring to our accessCode):

They showed us how to do it in curl. Scroll down to see how to do it in PowerShell

They URL listed there is actually the Token URL we saw earlier when we sign up, and tell us to do a cUrl to that URL and add our (the ClientSecret), the project ID (Client ID) and your redirect URL, along with the Access Token we just got . Take all of these, present them as the body for one last Post and we’ll get back our Auth Token. Once we’ve done this, we don’t need to repeat the steps again until our Authorization Token expires.

If you’re looking at my Connect-WordPressAccount cmdlet, we’re now up to line 46. The code calls Get-WordPressAuthToken (gitHub link), and passes in params it already had, like $clientSecret, $clientID and $blogURL. It also passes in $authCode, which we received in the last step.  I’ve excerpted this bit below:

#Continuing in Connect-WordPress Account Get-WordPressAuthToken -ClientID $ClientID -clientSecret $clientSecret -blogURL $blogURL -authCode $authCode -Debug

#store the token $password = ConvertTo-SecureString $accessToken -AsPlainText -Force $password | ConvertFrom-SecureString | Export-Clixml $configDir -Force

Now, to look within Get-WordPressAuthToken, here is how to post an access code and get back a token with PowerShell, instead of the cURL example in their screen shot.

Function Get-WordPressAuthToken{ \[CmdletBinding()\] param($ClientID,$blogURL,$clientSecret,$authCode)

try { $result = Invoke-RestMethod https://public-api.wordpress.com/oauth2/token \` -Method Post -ContentType "application/x-www-form-urlencoded" \` -Body @{client\_id=$clientId; client\_secret=$clientSecret; redirect\_uri=$blogURL; grant\_type="authorization\_code"; code=$authCode} -ErrorAction STOP }


We call the oAuth2/token endpoint, and just provide a list of parameters, including the Access Token we received when the user authorized our app. If this request worked, we’ll see something like the following.

{ "access\_token": "YOUR\_API\_TOKEN", 
  "blog\_id": "blog ID", 
  "blog\_url": "blog url", 
  "token\_type": "bearer" 
}

We simply need to take the Access Token and save it for future use. We’re done with authorizing now!

Storing the Token Safely

The user has now trusted us to partially act on their behalf for WordPress/whatever service.

This token we have is the functional equivalent to them just handing us their computer logged in, with Chrome open and signed in as them on this service. Anything you can do as a person using the service, someone with your Auth Token could do as well. This means you need to find a safe place to store it.

I opted to use the secure string cmdlets to store them safely within the user’s appdata folder, so if you’re looking at my own example, I use the system security API to safely store this info.  It can only be read (by default) by that user, or administrators of that PC, so it is fairly safe.  When the user loads any cmdlet from my PowerShell module, the variable is reloaded into $accessToken so that all of the PowerShell cmdlets that need the token ‘just work’.

#store the token $password = ConvertTo-SecureString $accessToken -AsPlainText -Force $password | ConvertFrom-SecureString | Export-Clixml $configDir -Force


Using THE token to actually query the API

The final thing to do is to try this token out against one of the many, many endpoints available. The awesome thing about Rest APIs is that they almost always have some totally kick-butt documentation, including pages and pages of addresses we can query for nifty info. Here’s a snippet of just a few, listed on developer.wordpress.com/docs/api. I’ll pick one out, like /me

append any of these to /rest/v1.1 and you’re in business

oAuth me

According to this, I just need to query the URL listed and provide my Authorization Token as a header.

In PowerShell, this looks something like this:

Invoke-RestMethod https://public-api.wordpress.com/rest/v1.1/me -Method Get -Headers @{"Authorization" = "Bearer $accessToken"}

Getting data back from a REST API, awesome!

That’s really all there is to it!  The data will come back already serialized, in most cases, and you’re in business.

Let’s make some cmdlets!

At this point, all you need to do to make a slue of cmdlets is to look at the endpoints, find nifty ones, and then just setup functions that ask for the right info. This is the fruit of my labors, right here.


Function Get-WordPressStats { \[Cmdletbinding()\] param( \[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true, Position=0)\] \[Alias("domainName")\] $ID, $accessToken=$Global:accessToken)

Invoke-RestMethod https://public-api.wordpress.com/rest/v1.1/sites/$ID/stats -Method Get \` -Headers @{"Authorization" = "Bearer $accessToken"} | Select-object -ExpandProperty Stats |Select-Object Views\*,Visit\*
}

get-wordpressStats

Let’s REST

I hope you liked this post. I’m very happy to have finally worked out an approach that makes sense to me for oAuth. If you’ve got some specific requests or some REST APIs that are giving you fits, let me know and I’ll do my best to help you figure it out!

Sources

For helping me to understand how to present parameters for oAuth

Wordpress Reference for API, with actually really good documentation

Continue Reading...

Windows vs Intel Raid Performance Smackdown

October 30, 2015 FoxDeploy

Update: Turns out I was pretty wrong about Storage Spaces and have been totally schooled and corrected about them here, on Reddit and at Redmond.  I’ll be revising this post heavily, adding some iometer numbers.  Stay tuned!

In this post, I’ll share my results comparing hardware to software RAID on modern Windows 10, then to take things up a notch, I’ll see how a Storage Spaces volume with Parity (maybe Microsoft is charged money for using the word ‘Raid’, because they avoid that word like the plague!) compares to the hardware solution (Spoilers: it ain’t pretty) in pretty much every configuration I could think of.

I’ve had a lot of issues in the FoxDeploy lab recently. Two months ago, I had a number of SSDs in a Storage pool on Server 2012 R2, then decided I should go to the desktop experience, and moved to Windows 10.  Let’s call this rebuild #1.

Since then, I’ve had an SSD fail in a Windows Mirror, resulting in a total loss of VMs, as I didn’t have backup working.  Rebuild #2

Next, I decided to take three drives, two physical SSDs and a chunk of unpartioned space from a six year old spinner, and put them in a Raid-5.

As it turns out, this kills the drive.  Goodbye, Big Betty, I will miss you. Rebuild #3.

At this point, I had it with these janky solutions.  I’ve recently come into possession of some new SSD drives, and this time, I’m going to do it right, damn it! I wanted to finally have some resiliency in case one of my drives died.  But along the way, I needed to know how it would perform.

Findings - Raid 0 Testing

For starters, here’s our benchmark.  It’s a single OCZ 120 GB SSD disk, connected via 3GBPS sata.

Single disk, sata 3

Single Disk Performance

Not bad, very respectable. Now, let’s see what happens when I use Windows to take two of these and stripe them. This is also called Raid 0, as in ‘you have zero parity and are totally hosed if one of these drives dies’.

Expectations: You gain the full capacity of both drives and speed to spare, as data is randomly written across drives.  Speed should be roughly double that of a single drive.  However, if one dies, you are up a creek, because you will not be able to recover your data. You’ll tend to get ~double the read and write performance when striping.

two disks, SATA 3, windows raid 0

WIndows Raid 0

If we compare the two, we see roughly double the performance as well.  Windows raid is achieving parity via the OS. Since data striping (putting chunks of data on the disk) is handled by the operating system, there is a potential for disk performance to suffer when the rest of the OS is under heavy load.  Microsoft has had years to work out the errors and speed things up though, so don’t let this stop you if your motherboard doesn’t support Raid.

Now, Let’s see how Windows Raid-0 stacks up to actual hardware raid.

TWO DISKS, SATA 3, Hardware RAID 0

Hardware Raid 0

To put those side-by-side, here’s the difference you can expect when comparing hardware Raid-0 to Software Raid-0.

SIDE BY SIDE, INTEL % CHANGE VS SOFTWARE RAID 0

Raid0 testing Intel Performance Increase over Microsoft

Winner - Intel There is quite a performance edge to gain by using a hardware controller, even nowadays. I was very surprised to see such a disparity here.  In every regard there were noticeable speed gains, at least on paper.

Findings - Raid 1 testing

Now, these big numbers were very fun, but as I’m doing this to test my performance while operating safely with parity, I needed to see what would happen when actually using mirroring.

Expectations - With Raid 1, you should receive roughly double read speed, while writes will suffer, because of the need to create and distribute parity sets across disks. You lose the whole capacity of one drive, but have a full backup of your active volume in case it dies. Nothing is safer than Raid 1! First, I tested in Windows for Raid 1, also known as a ‘Mirrored Set’.

TWO DISKS, SATA 3, WINDOWS RAID 1

Windows Raid 1

Wow, the reads are still good, but boy does the writes take a huge faceplant. Writes were roughly half of the write speed for a single drive, which was surprisingly bad, in my opinion.  Functionally, this makes sense, as the OS has to track syncing writes to two different disks.  Who knew that it would levy such a heavy penalty though.

Now, to compare this to using dedicated Raid hardware.

TWO DISKS, SATA 3, hardware RAID 1

Intel Raid 1

While the Intel raid controller blows the Software Raid out of the water on sequential reads, surprisingly the Windows software Raid was better in nearly every other respect. It seems that no matter if you use a hardware or a software Raid controller, you should expect to lose performance when you’re duplicating every write, which makes sense.

We can compare this to our single disk performance though, and see something else happening.

Side by side, intel % change vs software raid 1

Raid1 testing Intel % increase over Microsoft Software Raid

Winner - Windows If you need super fast sequential reads, like you’re working with a single big file that happens to be on one contiguous area of disk, if you could control that, then you’d want the Intel Controller. If not, Windows looks to be good enough, and takes the lead in most categories.

Findings - Raid 5 testing

Raid 5 was the defacto standard for parity across volumes smaller than a TB. Now, it’s been replaced with other Raid and parity options, but it’s still probably the most prevalent way of safely allowing for disk failures without a tremendous speed loss. With Raid-5, you lose the space of one drive.  With that being said, you can get some beefy performance, and have parity!

Raid 5 in Windows 10, some notes before we begin

In this round, we’ll be leaving the Disk Management wizard for Windows and instead go into the control panel to create a Storage Space!  Previously, there was no way for end user running home or PRO Windows to do make a parity volume, which reserved that feature for Windows Server users only.  Storage Spaces is Microsoft’s new, much easier to use approach to Disk Management with loads of options.

You can configure this any way you want to! Just spend a LOT of time reading documentation.

While Microsoft doesn’t call it ‘Raid 5’, it’s possible using Storage Spaces to create a Parity Pool, which will effectively achieve the same results.

Raid-5 by any other name. Double Parity would be Raid-10!

It should be noted that I had to tune this array to get these results.  First off, a default Storage Spaces drive will be created with a Write-back Cache of 1 GB.  It should also be said that there IS a distinct performance penalty at play here.  In order to play things safe, there is some mandatory disk journaling involved here, meaning that your writes aren’t going to be cached very much.  Instead, the storage subsystem will wait for confirmation of all writes, and it does so VERY conservatively.

It’s not uncommon to see R/WR delays into the tens of thousands of milliseconds while writes are performed.  You can somewhat minimize this behavior by running these two PowerShell cmdlets.  The first GREATLY boosts the write cache amount, which I’ve seen to boost performance, while the second cmdlet specifies that there is Power protection (like a UPS).  To quote the help documentation ‘If you specify a value of $True for this parameter, the storage pool does not perform flush operations, and the pool removes write-through attributes from commands.’  It also helps.

Get-StoragePool select -First 1 set-storagepool -WriteCacheSizeDefault 8GB Get-StoragePool -FriendlyName “Storage Pool” Set-StoragePool -IsPowerProtected $true

Now, after that length pre-amble, let’s hop to it.

three DISKS, SATA 3, HARDWARE RAID 5

ThreedrivesIntelRaid5 Hardware Raid 5

As expected from hardware Raid 5, we receive roughly 100% read speed from all drives, for some truly mammoth read speeds.  Writes hang out around 2/3rd’s of the total write speed for all drives, making this the power-user’s option.  It should be noted that I had to specify a custom disk policy for this Raid 5 volume to enable write caching and disable flush operations.  This is definitely a power-mode configuration, but depends on battery backup to ensure there is no data loss.

Be warned, you will lose data if you have a power outtage. Only check these if you’ve got reliable UPS in place.

If you’re using Raid 5, you’re probably data loss averse, so only specify these settings if you’ve actually got a battery backup.

Now, onto the final option, Storage Spaces with Parity.  This is super secure for your data, however…writes…well…

THREE DISKS, SATA 3, Storage spaces parity volume (raid 5)

Storage Spaces Journaling aggressively protects data, but at a cost

I reached out to some peers for help, to see if I was mistuning this array, because, frankly, damn.

According to Aidan Finn, Microsoft MVP of Datacenter and Hyper-V, I’m doing it wrong.  This is not meant for performance, nor is it a good idea for storing VHD files.  You should be using parity volumes as a replacement for a super expensive NetApp or LeftHand SAN Solution, with wonderful configurability for hundreds of potential configuration.  You use this to save money for your archival storage, but shouldn’t expect to get equal performance.

Still, the numbers are pretty shocking.  Here’s Storage Spaces Parity volume compared to a single drive, and to a Raid 5 Volume.

Great for Reads versus a single drive, but writes suffer incredibly.

Winner - Intel

Now, Storage Spaces can do things you cannot do using a simple Intel Raid Controller.  For instance, you could easily take four full JBOD (Just a bunch of disk) arrays, filled with 8 TB drives and also slap in a stack of PCI-e or SSD storage to create a wonderfully resilient storage system with excellent performance.

But it’s not the answer to every problem.  In this case, the Intel Raid Controller was the clear winner, absolutely smoking the Storage Spaces Controller.  This graph says it all

12,000% improvement in writes is mindblowing

In Conclusion

If you want maximum performance for a small number of disks, need some basic parity support, and don’t have more than 13TB in a volume, and you have a hardware Raid Controller, then you should definitely use hardware Raid 5 over storage spaces.

You need maximum speed and will backup your data - Use Raid 0 and backup data

You need maximum speed and some resilience to disk failure - Use Raid 1 or Raid 5

You need to archive monstrous amounts of data across insane configurations of disk arrays - Use Storage Spaces.

Finally, some random charts.

raid 5 Intel RAID-5 performance over Single Disk

raid 1 vs single drive

Did I do it wrong?  Am I missing something?  Correct the author by e-mailing me at Stephen@foxdeploy.com, or commenting here.

Continue Reading...

Hyper-V on Windows 10, how to fix a broken VHD

September 29, 2015 FoxDeploy

I’m running Windows 10 w/ Hyper-V on my home lab pc, and found a strange Hyper-V error this week.

One of the core things you shouldn’t do with differencing disks is to ever, ever move or edit the parent disk, or else you can break the child disk.  I added some more storage and moved my VHDs around to an all SSD Raid, then found that I’d broken the chain of VHDs for one of my VMs.

When trying to start the VM, this is the error you’ll see.

The chain of virtual hard disks is broken. The system cannot locate the parent virtual hard disk for the differencing disk.

Normally, one runs ‘Inspect Disk’ from Hyper-V to locate the parent disk of a child VHD, which will fix the chain of differencing and allow your VMs to access the disks again.  However on Windows 10, clicking ‘Inspect Disk’ will result in Hyper-V throwing a fatal error.

There was a problem with one of the command line parameters. Either ‘BEHEMOTH’ could not be found, or ‘X:\Virtual Machines\Virtual Hard Disks\VM01.vhdx’ is not a valid path.
Property ‘MaxInternalSize’ does not exist in class ‘Msvm_VirtualHardDiskSettingData’.

The path is valid and exists. I’ve found a workaround, that of using PowerShell and the Hyper-V module to run Set-VHD, like so:

set-vhd -Path 'X:\\Virtual Machines\\Virtual Hard Disks\\VM01.vhdx' \` -ParentPath "X:\\Server2012Template\\Virtual Hard Disks\\Server2012Template.vhdx"
Continue Reading...

Part IV - PowerShell GUIs - how to handle events and create a Tabbed Interface

September 08, 2015 FoxDeploy

Learning PowerShell GUIs

This post is part of the Learning GUI Toolmaking Series, here on FoxDeploy. Click the banner to return to the series jump page!


Where we left off

Previously in Part III of our GUI series, we left off with a review of some advanced GUI options, things like radio buttons, text replacement and things like that. In this section, we’ll grow our tool from Part I, our lowly Ping tool, and expand it to become a fully-fledged remote PC management tool. We’ll do this to learn our way through some new GUI concepts we haven’t run into before. I’ll take you through making most of this tool, and leave you at a place where you should be able to take it the rest of the way.

What we’ll cover

If you walk through this guide, I guarantee that we’ll cover each of these items below. If there is something you’d like to see covered in a future post, send me an e-mail! Each of the items covered in this post came from requests from my readers.

Seriously.

I am gathering input for post five right now, so let me know what’d you’d like to do with PowerShell, and we can make it happen.

Making a Tabbed Interface

We’re going to talk about the right way to use this element, to make it obvious to users (something called ‘Discoverability’) that more tools and options exist, if they’d only click this tab and explore around a bit!

In tab one, we’ll drop in the whole of our pinging tool from Part I of this walkthrough, while we’ll hide extra functionality in tabs two, three and four. For extra niftiness, we’ll have the others tabs be disabled (unclickable) until the user first verifies that a PC is online in tab one.

Handling UI Events

This is the part that has me the most excited, mostly because I just learned about how to do it, and it solves so many issues. Want to have your GUI run a chunk of code when the user edits a textBox. Or handle the event which is triggered if the user moves the mouse over a button? We can even wait till the user is finished entering text, and then run a short chunk of code to validate an entry. These are all real world scenarios for a User Interface Developer, and we can easily deal with them in PowerShell.

Let’s get started.

Tabs, not just for playing ‘The house of the rising sun’

Now, I said we’ll be bringing back our pinger…and we kind of will. But we’re not ready to open up that code, just yet.

See, the thing is that while it IS possible to add tabs to an existing project or UI, it’s much easier to start off with a new project, get the tabs, status bar, dock panels, etc setup the way you want them to look, and then copy and paste in other GUI elements from Visual Studio, or some XAML code.

Let’s start the right way with a tabbed UI. We’ll be making a new project for this walkthrough, and at this point, it should feel familiar. Launch Visual Studio, click New, New Project, WPF.

Pick a good starting size for your form (I’ll do 345x450), then add a Tab control from the toolbox.

I like to draw a somewhat wide tab, like this.

[00_Fill all

Then, right-click the top of the tab…uh, holder, right next to your last tab and choose Layout-Fill All. I do this because it’s actually surprisingly hard to drag your tab to fill the window, so this is the easy shortcut.

[01_Fill all

This will expand the tabs to fill the whole UI.

Now that we have our tabs taking up the whole window, we can add more by right-clicking the background and choosing ‘Add Tab Item’. We can also rename the tabs by clicking one and using their properties, or going down to the XAML and changing the name property there.

For our purposes, we’ll name the Tabs ‘Computer Name’, ‘System Info’, ‘Services’ and ‘Processes’. Now, for the process of adding content to tabs.

This is a bit tricky

When it comes to adding guts to our tabs, I don’t like to build the new guts for each tab within the tab window itself.

No, instead, I like to make a new window (right click the project in Solution Explorer, Choose Add, then New Window, and pick Window WPF) for each of my tabs. Then I can decorate them there, get them sized up, and then copy-paste the XAML or the UI elements into the Tab where they’ll reside when I’m done.

This way, I make a good ComputerConnection.XAML window once, then I can copy it and paste it as many times as needed, and reuse it later on down the line. If the code’s already embedded into a tabcontrol, pulling it out to reuse later can be tricky.

Making a new window for each tab is much easier for development

Now with a new window created, I pick the tools I need, build it here, and then when it’s finished, I copy and paste it into the tabbed UI. I found it quite difficult to do this by manually dragging and dropping tools from the toolbox into the tab itself. If you’ve got a gamer mouse or much better dexterity, or are just plain cooler than me (you probably are, I write a blog about a scripting language, that doesn’t scream ‘Taking the cheerleader to prom’ to me) then go ahead and build your whole tool, bypassing the method I provide here.

This is what a blank window looks like. Time to put some clothes on this naked baby!

This is what a blank window looks like. Time to put some clothes on this naked baby!

So I’ve got my new window which I’ve named and titled ComputerName_Tab. I’m going to drop in an image, a textblock, a button, and a textbox, and change the background color to something dark and moody looking.

Important tip

This is a time to make sure we’re using good naming conventions for our UI elements. When we paste our XAML over to PowerShell snippet, we’ll have a lot of $WPFButtons to keep track of, if we’re not careful. From now on, when we add elements, lets take the time to give them a logical name, like ‘VerifyOnline_Button’ and ‘ComputerName_textbox’, etc.

[

Here’s my XAML if you want to be lazy. Just copy and paste this after the window declaration in Visual Studio.

<Grid Background="#FF0B4A80">
    <TextBlock TextWrapping="WrapWithOverflow" VerticalAlignment="Top" Height="89" Width="314" Background="#00000000" Foreground="#FFFFF7F7" Margin="22,21,101,0" >
    This is the FoxDeploy official Awesome Tool.  To begin using this tool, first verify that a system if online, and then progress to any of the above tabs.
</TextBlock>
    <TextBox x:Name="ComputerName" TextWrapping="Wrap" HorizontalAlignment="Left" Height="32" Margin="21,142,0,0" Text="TextBox" VerticalAlignment="Top" Width="135" FontSize="14.667"/>
    <Button x:Name="Verify_Button" Content="Verify Online" HorizontalAlignment="Left" Margin="174,142,0,0" VerticalAlignment="Top" Width="93" Height="32"/>
    <Image x:Name="image1" Stretch="UniformToFill" HorizontalAlignment="Left" Height="98" Margin="10,174,0,0" VerticalAlignment="Top" Width="245" Source="C:\Users\Stephen\Dropbox\Speaking\Demos\module 13\Foxdeploy_DEPLOY_large.png"/>
  </Grid>

Now, that I’ve got this configured the way that I like it, I’m going to copy this whole body of code, and paste it into the master .xaml file, the one with the tabs we made earlier.

Overwrite the you see here

In case it’s not clear, you want to paste your code in between the TabItem tags

When you click away after pasting, the window will update in Visual Studio and you should see your same UI compressed into the tab. You may need to resize some things to make them fit, but this is looking good so far!

05

It’s the same UI, now within the tabbed screen!

Now, let’s flesh out the rest of the tabs. For System Info, I’m going to copy and paste the formatting we already did for Part II of this blog series, excerpted here, after renaming some elements and removing cruft. Don’t worry about looking up the post unless you want to, what I’ve got here will work just fine if you’re following along.

<Grid Background="#FF0B4A80">
  <Button x:Name="Load_diskinfo_button" Content="get-DiskInfo" HorizontalAlignment="Left" Height="24" Margin="10,10,0,0" VerticalAlignment="Top" Width="77"/>
  <ListView x:Name="disk_listView" HorizontalAlignment="Left" Height="115" Margin="10,40,0,0" VerticalAlignment="Top" Width="325" RenderTransformOrigin="0.498,0.169">
      <ListView.View>
          <GridView>
              <GridViewColumn Header="Drive Letter" DisplayMemberBinding ="{Binding 'Drive Letter'}" Width="80"/>
              <GridViewColumn Header="Drive Label" DisplayMemberBinding ="{Binding 'Drive Label'}" Width="80"/>
              <GridViewColumn Header="Size(MB)" DisplayMemberBinding ="{Binding Size(MB)}" Width="80"/>
              <GridViewColumn Header="FreeSpace%" DisplayMemberBinding ="{Binding FreeSpace%}" Width="80"/>
          </GridView>
      </ListView.View>
  </ListView>
</Grid>

Alright, pasting that in to our UI, we should see the following.

06

This is a great time to take this code and try and run it in PowerShell. If it works…you’re in business, and the next step is to hook up some of the UI elements with some Add_ methods.

Copied and paste and it worked in PowerShell!

Copied and paste and it worked in PowerShell!

At this point, we’ll move on to the coolest part, which is how to handle events.

Events and how to handle them

This was something that confused me for a LONG time., in fact it’s been the dreaded question of my bootcamps as well. This is how I used to handle questions like these.

Student: ‘Stephen, how do I do something when the textbox loses focus, or when the text changes, or when blahBlah happens?’.

Me:

Me:

Me: * runs to bathroom and hides till day is over*

Here’s how it works.

Every GUI element in a form has a collection of Events that get triggered on certain conditions. They have names which are pretty easy to understand too, things like MouseEnter, MouseLeave(this event name has always sounded kind of disappointed to me), texthasChanged, etc.

When we’re loading this our form code into memory, the c# compiler is transforming our XAML into intermediate language code, another phrase for code on it’s way to becoming machine executable code. When that happens, we get a whole lot of what developers call ‘syntax sugar’–which sounds awfully diabetic–but means that the tools help us do work and give us shortcuts.

One of those shortcuts is that the compiler snorts through our GUI like a pig looking for truffles, finds all of the events associated with the objects on our form, and sets up handlers for each of the events, in the form of methods that begin with Add_.  We can use these to setup a handler, so that when an event is triggered, something cool happens.

There are a metric pant load of events, for every element. You can see them by piping one of your objects into Get-Member like so:

$WPFComputerName | Get-member Add\* -MemberType Method -force

All we have to do is add some code after the event we want to handle, and that's it. Sugar baby, SUGAR!

All we have to do is add some code after the event we want to handle, and that’s it. Sugar baby, SUGAR!

So, let’s say you want to run a chunk of code when the user moves over to another element in your form (which is called LosingFocus, which happens to me constantly during the day…hey I wonder what’s on reddit…). For example, let’s echo out the value of a box when a user moves away from the box.

$WPFComputerNameBox.Add_LostFocus({Write-Host $WPFComputerName.Text})

I’ll stage this in the context of a little mini GUI, here’s the XAML, and then below, the PowerShell code.

<Grid Background="Azure">
    <Button x:Name="button" Content="Click Me" HorizontalAlignment="Left" Height="32" Margin="62,186,0,0" VerticalAlignment="Top" Width="106"/>
    <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="30" Margin="62,92,0,0" TextWrapping="Wrap" Text="Type stuff here" VerticalAlignment="Top" Width="106"/>
    </Grid>
</Window>

Method Code

$WPFbutton.add_MouseEnter({Write-host 'Yay, the mouse is over me!'}) 
$WPFbutton.Add_MouseLeave({Write-host 'he didnt click me...:('})
$WPFTextBox.Add_TextChanged({Write-host "the user typed something, something like this $($WPFtextBox.Text)"})

As I interact with my form, you can see the background updating in real time.

https://www.youtube.com/watch?v=5cepMPD7j-s

Giving the Form some Smarts

With that done, I’m now going to go back and set the tabs for tabs 2, 3 and 4 as disabled, so that the user will have to interact with tab 1 before proceeding. Do this by adding IsEnabled=”False” to each TabItem header, like this:

<TabItem Header="System Info" IsEnabled="False">

We’re going to plug some logic in at the bottom of our XAML/WPF Snippet. When the user launches this form, we want the $WPFComputerName.Text to default to the user’s computer name. When the user clicks the $WPFVerify button, we’re going to ping the system, if it replies, we’ll activate the rest of the tabs.

When I said earlier that every object in our GUI has a slew of events, that includes the form (background) of our app itself.  We’ll use the Form’s own Add_Loaded({}) event handler to tell our form to run a snip of code when it finishes loading, which is a useful way to set a default value for a box, for instance.

Below that, we’ll give our Verify button some code to run when it throws a click event (when the user clicks the button), via the .Add_Click({}) event handler method.  The code will try to ping the computer in the ComputerName box; if it replies, it will then enable each of the tabs for the user to click them by stepping through each item in the TabControl, and setting the ‘IsEnabled’ property of each to $True, to turn the lights on.

$Form.Add_Loaded(
    {$WPFComputerName.Text = $env:COMPUTERNAME}
)
$WPFVerify.Add_Click(
    {if (Test-Connection $WPFComputerName.Text -Count 1 -Quiet){
        write-host "$($WPFComputerName.Text ) responded, unlocking" $WPFtabControl.Items[1..3] | % {
            $_.IsEnabled = $true} 
        } 
        else{
            write-host "$($WPFComputerName.Text ) did not respond, staying locked" $WPFtabControl.Items[1..3] | % {
                $_.IsEnabled = $false} 
        } 
    }) 

Now when the form loads, the user has to interact with tab 1 before they can go on to the rest of the UI.

User can't click these till they ping a system first User can’t click these till they ping a system first

For our System / Disk Info panel, I’m liberally applying the principle of code reuse to recycle the code from Part II of this series.  There we covered how to instruct PowerShell to assign certain properties to certain columns in a GridView table, using Binding.  This code will grab the hard drive info, and display it in a table on tab II.

$WPFLoad_diskinfo_button.Add_Click({ Function Get-DiskInfo { param($computername =$env:COMPUTERNAME)

Get-WMIObject Win32_logicaldisk -ComputerName $computername | Select-Object @{Name='ComputerName';Ex={$computername}},\` @{Name=‘Drive Letter‘;Expression={$_.DeviceID}},\` @{Name=‘Drive Label’;Expression={$_.VolumeName}},\` @{Name=‘Size(MB)’;Expression={\[int\]($_.Size / 1MB)}},\` @{Name=‘FreeSpace%’;Expression={\[math\]::Round($_.FreeSpace / $_.Size,2)\*100}} }

Get-DiskInfo -computername $WPFComputerName.Text | % {$WPFdisk_listView.AddChild($_)}

})

With all of these changes in place, when the user provides a valid computer name, the other tabs will all unlock, then they can click the Get-DiskInfo button and…get some disk info.

10

Wrapping up

Your next steps to flushing out the rest of the UI is to figure out how to apply the same principles from Tab II to the tabs for Services and Processes.  I’ve got you started here with the GUI for the Services Tab, it’s up to you to handle the GUI for the processes tab, and the logic for both.   If you pay close attention to one of the column headings in the Services Tab, you’ll find you have to think outside of the box to come up with the value I’m asking for there.

Should you come up with something you’re particularly proud of, please sanitize your code (remove company info) and share it in the comments.  I’d recommend making a Gist or putting your code on GitHub or PasteBin, rather than dumping in 30KB of PowerShell and XAML below :)   I’m always surprised by the creativity my readers display, particularly when proving me wrong :p

Part V - Building Responsive Apps using Runspaces and adding Progress Bars

Complete XAML

<Window x:Class="Tab_Me_baby_one_more_time.TabMe"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Tab_Me_baby_one_more_time" mc:Ignorable="d" Title="TabMe" Height="258.4" Width="486.4">
    <Grid>
    <TabControl x:Name="tabControl">
        <TabItem Header="ComputerName">
            <Grid Background="#FF0B4A80">
                <TextBlock TextWrapping="WrapWithOverflow" VerticalAlignment="Top" Height="89" Width="314" Background="#00000000" Foreground="#FFFFF7F7" Margin="10,10,150.4,0" > This is the FoxDeploy official Awesome Tool. To begin using this tool, first verify that a system if online, and then progress to any of the above tabs. </TextBlock>
                <TextBox x:Name="ComputerName" TextWrapping="Wrap" HorizontalAlignment="Left" Height="32" Margin="20,67,0,0" Text="TextBox" VerticalAlignment="Top" Width="135" FontSize="14.667"/>
                <Button x:Name="Verify" Content="Verify Online" HorizontalAlignment="Left" Margin="173,67,0,0" VerticalAlignment="Top" Width="93" Height="32"/>
                <Image x:Name="image1" Stretch="UniformToFill" HorizontalAlignment="Left" Height="98" Margin="9,99,0,0" VerticalAlignment="Top" Width="245" Source="C:\\Users\\Stephen\\Dropbox\\Speaking\\Demos\\module 13\\Foxdeploy_DEPLOY_large.png"/>
            </Grid>
        </TabItem>
        <TabItem Header="System Info" IsEnabled="False">
            <Grid Background="#FF0B4A80">
                <Button x:Name="Load_diskinfo_button" Content="get-DiskInfo" HorizontalAlignment="Left" Height="24" Margin="10,10,0,0" VerticalAlignment="Top" Width="77"/>
                <ListView x:Name="disk_listView" HorizontalAlignment="Left" Height="115" Margin="10,40,0,0" VerticalAlignment="Top" Width="325" RenderTransformOrigin="0.498,0.169">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Drive Letter" DisplayMemberBinding ="{Binding 'Drive Letter'}" Width="80"/>
                <GridViewColumn Header="Drive Label" DisplayMemberBinding ="{Binding 'Drive Label'}" Width="80"/>
                <GridViewColumn Header="Size(MB)" DisplayMemberBinding ="{Binding Size(MB)}" Width="80"/>
                <GridViewColumn Header="FreeSpace%" DisplayMemberBinding ="{Binding FreeSpace%}" Width="80"/>
            </GridView>
        </ListView.View>
                </ListView>
                <Label x:Name="DiskLabel" Content="Disk info for system: " HorizontalAlignment="Left" Height="24" Margin="117,10,0,0" VerticalAlignment="Top" Width="197" Foreground="#FFFAFAFA"/>
            </Grid>
        </TabItem>
        <TabItem Header="Services" IsEnabled="False">
            <Grid Background="#FF0B4A80">
                <Button x:Name="Load_services" Content="Load Services" HorizontalAlignment="Left" Height="24" Margin="10,10,0,0" VerticalAlignment="Top" Width="77"/>
                <ListView x:Name="service_listView" HorizontalAlignment="Left" Height="115" Margin="10,40,0,0" VerticalAlignment="Top" Width="355">
                    <ListView.View>
                        <GridView>
              <GridViewColumn Header="Name" DisplayMemberBinding ="{Binding ServiceName}" Width="80"/>
              <GridViewColumn Header="DisplayName" DisplayMemberBinding ="{Binding 'DisplayName'}" Width="100"/>
              <GridViewColumn Header="Status" DisplayMemberBinding ="{Binding 'Status'}" Width="80"/>
              <GridViewColumn Header="AutoStart" DisplayMemberBinding ="{Binding 'AutoStart'}" Width="80"/>
                        </GridView>
                    </ListView.View>
                </ListView>
                <Button x:Name="Stop_service" Content="Stop Service" HorizontalAlignment="Left" Height="24" Margin="129,10,0,0" VerticalAlignment="Top" Width="77"/>
                <Button x:Name="Start_service" Content="Start Service" HorizontalAlignment="Left" Height="24" Margin="258,10,0,0" VerticalAlignment="Top" Width="77"/>
            </Grid>
        </TabItem>
        <TabItem Header="Processes" IsEnabled="False">
            <Grid Background="#FF0B4A80">
                <TextBlock Name="processes_text" TextWrapping="WrapWithOverflow" VerticalAlignment="Top" Height="89" Width="314" Background="#00000000" Foreground="#FFFFF7F7" Margin="10,10,150.4,0" > Do something cool :) </TextBlock>
            </Grid>
        </TabItem>
    </TabControl>
    </Grid>
</Window>

My syntax highlighter was causing issues with the PowerShell code I posted. Instead, you’ll find the completed script here on the Github page for this post.

For additional reading on Events, I’d recommend checking out Keith Hill’s Blog Post here, and June Blender’s excellent post here. Both helped me to finally understand how it is that this works, and I truly appreciate it.

If you have any features you’d like to see me include next time, drop a comment here, hit me up on Twitter, or send me an e-mail at myFirstName At Foxdeploy.com. (You do have to replace MyFirstName with Stephen, by the way!)

If you have a particular scenario and it’s interesting enough, I might write a whole post on it. So if you want to recreate the ConfigMgr 2012 UI in PowerShell XAML, let me know :)

Who knows where we’ll go from here…

!! GUI Progress bars? Must have!

GUI Progress bars? Must have!

Continue Reading...

'GUI your life with PowerShell' - Slides and Scripts available

August 25, 2015 FoxDeploy

Here are the slides and scripts from my session at Atlanta TechStravaganza at the Georgia Tech Research Institute.

Graphical Interfaces for PowerShell

Scripts available on GitHub, here.

Continue Reading...


Microsoft MVP

Five time Microsoft MVP, and now I work for the mothership


Need Help?

Get help much faster on our new dedicated Subreddit!

depicts a crowd of people in a night club with colored lights and says 'join the foxdeploy subrreddit today'


Blog Series
series_sml_IntroToDsc
series_sml_PowerShellGUI series_sml_IntroToRaspberryPi Programming series_sml_IntroToWindows Remote Management Series The Logo for System Center Configuration Manager is displayed here Depicts a road sign saying 'Learning PowerShell Autocomplete'




Blog Stats