PowerShell and Lego share some similarities. You are given a wide variety of building blocks and a system for working with them. Ultimately, your solutions will come down to creativity, and your ability to learn and explore as you work.
When you first start out, you’ll probably stick to using pre-built pieces (built in commands), or maybe you follow the instructions and build a pre-designed system (examples from help or the Internet). Eventually, you learn that you aren’t constrained to these instructions or pre-built pieces. You can build your own pieces (advanced functions, scripts), and your solutions are limited only by your creativity and motivation to explore and learn how things work.
A while back I wrote a quick bit on learning and exploring in PowerShell, and the importance of Get-Command, Get-Help, and Get-Member. Let’s take this a step further and examine a real-world scenario.
Help! I can’t run my application
AppLocker is a handy built-in application control system from Microsoft. Let’s pretend we have this implemented, and that your support team is getting reports of end users who can’t access a critical application.
First things first. I’m assuming you know the technology you work with. Sadly, this isn’t always the case. In this scenario, we know that AppLocker has its own event log, and that if everything is set up correctly, we will see failed attempts in this event log.
I’m going to walk through the stages you might go through when developing a re-usable solution. When you first start out, you might stick to the simple solutions. As you spend time learning PowerShell, you might skip the first few steps and go right to a re-usable function or advanced function.
The pre-built command
The first step you take might be to look at pre-built commands. You know the event log exists, so you take a look in the event viewer GUI:
Boom. Done. Or are you? Is your help desk familiar with the event viewer? Is this the only server the user might be using? Can you re-use this solution or automate it? No. This is a one-off solution that isn’t very helpful to those who aren’t familiar with what is going on behind the scenes.
Let’s try this in PowerShell. First, check to see if there is an AppLocker command for this: Get-Command *AppLocker*. Nothing useful; unfortunately, some product groups don’t provide the most robust PowerShell support. We know we want to get an event, so… Get-Command get-*event*: Get-Event, Get-EventLog, and Get-WinEvent sound helpful.
Which command do I use? I run Get-Help for each of these and quickly find Get-Event is not what I want. I know this is a newer style event log, and I see that Get-EventLog won’t help with these. Get-Help Get-WinEvent –Full gives me what I want.:
Looking back at the GUI, it’s easy to spot the blocked items. In this scenario, we can key off the fact that these aren’t information level alerts. I read the full help for Get-WinEvent, looking at examples and parameter information, and ultimately come up with the syntax:
That’s pretty much as far as you’re going to go without connecting blocks together and exploring. This still isn’t very usable. It hits a single computer and the default output isn’t showing me everything I need to know (who ran into the issue?), so we need to dig deeper!
Just starting out? Did finding that information seem too painful? One of the nice things about PowerShell is that you can apply your knowledge to the many technologies PowerShell can interface with. So now I know Get-WinEvent and Get-Eventlog are the commands I should look for when working with events, and I have a snippet of code I can refer to if I want to use them again. Google would have turned up results immediately as well, but it’s important to know how to discover and get help at the command line. As you spend time with PowerShell, this will all become second nature.
A draft for a script
We need more information! Let’s explore the output of Get-WinEvent. We’ll use Lee Holmes’ fantastic Show-Object. We could also use Select-Object and Get-Member to discover the same information.
Nothing too exciting; I see a SID with no friendly account name, and I see a message property with the blocked file path and some other text I don’t care about:
I look back at the Get-Member results, and notice there’s a ToXml method. Interesting, I’ve heard PowerShell can parse XML. Let’s take a stab at this!
Okay! This is completely unreadable and unusable as is, but we see some promising info. I see a FilePath node and a few other bits that look helpful, so let’s actually parse this XML! I google this and quickly find that all I do is use the [xml] type accelerator to convert my XML text to an object I can explore on my own. After this, I can explore the object using Select-Object and Get-Member, or Show-Object:
Note that XML objects have a bunch of XML specific properties and methods that can crowd things out. In this rare case, the default output is actually more helpful than using select-object, get-member, or show-object to view all the properties.
I see there is an Event node, which has more nodes underneath. Let’s take a look…
Awesome, I have nearly everything I need, and more! I google to find out how to add a property to an object, and find that I can use Add-Member or a calculated property. I’ll use a calculated property in this example.
The basic logic here is that we get the events we care about, and for each event, we convert it to XML, and then pipe it to Select-Object to pick out the properties we care about, as well as calculate properties from the XML we generated. The output is starting to look more usable:
Okay! So, theoretically, I could stop here and have a reasonable script for pulling AppLocker events. But I still don’t have the user name, I can’t hit multiple computers yet without handling the logic myself, and I just have a snippet of code. Who wants a snippet when you can have a function or script that behaves like an actual command?
For a quick illustration, let’s take a look at a quick and dirty function that pulls the information above. Functions and scripts behave nearly identically, so I generally prefer functions, as they are easier to call.
This code is saved in C:\sc\Get-AppLockerEvents.ps1. I can now dot source it in a profile or module and call it like a normal command
Try piping a computer name to this command. Hint: You get an error and no results. How about specifying two computers in ComputerName? Again, no luck.
This solution is usable for sure, but let’s spend a few moments to add some handy features that make it behave like a cmdlet, and make it more convenient for the end user.
Advanced functions are pretty much the end of the road for individual commands that aren’t precompiled in another .NET language. There’s a lot to them, but the basics are reasonably simple to pick up. I tried to gather up best practices for building functions in an old post, and there are many other resources available for learning this.
We now have Get-AppLockerEvent, a command that looks and behaves just like a cmdlet. We can pass in a few computer names down the pipeline and get back the info we need. We also added a few convenient features like the option to specify a start time, and a switch to include information level alerts (items that were allowed to run).
The code is available on the Technet Script Center Repository. It might look lengthy and complicated, but most of the pieces we added become second nature. Much of it can be borrowed from the Cmdlet (advanced function) – complete snippet, or commands you have written in the past.
Where to go from here?
That might have been painful, but at this point, you should be able to whip up a variety of commands geared towards extracting custom data from event logs.
We also learned a bit about XML, which is used in a variety of technologies, from web services and APIs to Group Policy preference items, and we all know how well Group Policy is represented in PowerShell.
At this point, you should be well on your way to coming up with your own solutions using your creativity and ability to learn and explore. You can write sets of functions that work together and build your own modules. You can provide your teams with PowerShell functions that make their lives easier. You might find life easier and more enjoyable when you can write PowerShell functions and delegate them out. If you start writing more Advanced Functions, you can use these modular blocks across various solutions, and build even larger and more creative solutions.