I had a conversation with a coworker today where he expressed an interest in learning PowerShell. He knew it was critical to his future in IT and could help him solve many day to day challenges but felt that it was too intimidating and didn’t know where to begin. I decided I would do my best to give him a clear and concrete place to start. That’s where this blog post steps in. What I will attempt to do over the remainder of this post is introduce the the core concepts of PowerShell in such a way that you can be immediately productive with it to solve real world problems without feeling burdened at learning the entire syntax and structure all at once.
Let’s start by outlining our scenario. We are working on a windows 8 client machine that a user reports is “acting funny” as random pop ups advertisements are appearing on the screen. You immediately suspect some kind of malware but you need to investigate to confirm your suspicions. How would you go about solving this problem?
The first thing you might do is open up Task Manager and select the Details tab to see if you can spot any unusual processes.
Unfortunately there are so many that it’s difficult to isolate and determine which ones are valid and which ones are not. Now what we might do next if we are desperate is to go through every process one at a time and try to figure out what they do and if they are legitimate or not. You could do that… or you could use PowerShell. It might seem scary but let’s see if this PowerShell thing can do anything to help us here.
- To launch Powershell, simply click the start button and type ‘powershell‘ or launch it through the start menu by clicking this icon
- A window will appear where you can enter commands. The question now is — What do I type?
- The thing to remember is that all the PowerShell commands follow the same basic structure of Verb-Noun. It’s intended to try and mirror the way you might ask the computer to do something in plain English. For example, let’s say you wanted to get a list of all of the services on your computer. The verb in that sentence is Get and the noun is Service. We always separate the verb and the noun by a dash so the PowerShell command to do this is simply Get-Service. (Note, all official Microsoft nouns are always singular) There are many commands to choose from. What if you wanted to get a list of all of the commands available? That’s right, simply type Get-Command. Note that PowerShell commands in the form of Get-Noun are formally called cmdlets (pronounced “command-let”)
- In this case though, we’re on a mission. We’re trying to get a list of all of the processes running on our machine. Let’s try Get-Process
Get-Process
- That worked, great! You can see I typed the command at the top and pressed enter and all of the columns of data was returned to me. But this isn’t much better than what I had with the GUI. Or is it?
- It turns out that PowerShell is actually keeping track of all of the data you just requested automatically behind the scenes in something called an object. Objects are nothing more than a collection of related data packaged together as a single ‘thing’ that we can interact with.
- Objects are very special because they organize the data they keep in a way that we can do some cool stuff with it. Objects for example contain something called methods. Methods are simply actions that allow you to perform specific operations on the object. The nice thing is, all of this is included for you automatically without you lifting a finger.
- Let’s give an example. Let’s say we want to know how many processes are running on our machine. It turns out that the the object that is made for us when we run Get-Process includes a method called count. This does exactly what you think it will and counts how many entries are in the object. To access the count method, we need to wrap the command we want in round brackets ie the “(” and “)”. What do the brackets do? They tell PowerShell to run whatever is inside the brackets first.
- This will return an object. To access the method of that object, we use a period (“.”) to run the method against our object. Have a look at this:
(Get-Process).count
- You can see that this machine currently has 179 processes running on it. That’s something we’ll keep in the back of our minds as we proceed with our investigation
- In the results from get-process, we see that there is a lot of numbers for memory usage and CPU that for our purposes we don’t care about. How can we filter out that data?
- To answer that, we have to take advantage of one of the single biggest features in all of PowerShell and that is something called the Pipeline. You can think of the pipeline just like a physical pipe where information ‘flows’ from left to right. Let’s try an example. Imagine you have a list of first names: “John”, “Steve”, “Sarah”, “Brian”, “Lisa”. Now let’s say you wanted to select only the female names. How could you do that? Imagine now that someone at Microsoft built a cmdlet for you that contains a list of every single female name in the world and contains code that can automatically compare any list of names and return only those ones that are female. They call their function Get-Women. What you can do then is take your list of names and pipe it to this command. To do that you use a special character in PowerShell called the pipe (“|”). This character is typically above the enter key on most keyboards
- To answer the question of “Show me the women in this list” you could type “John”, “Steve”, “Sarah”, “Brian”, “Lisa” | Get-Women. This will pass in all of the names from left to right and then will return “Sarah” and “Lisa”. Easy, right? Of course the function Get-Women doesn’t exist so let’s now look at a real world one instead to drive the point home
- In the malware troubleshooting we are doing, we want to show a list of only the names of all of the processes. We could tell from when we ran the Get-Process command that one of the columns was called ProcessName. Let’s select just that by using a cmdlet called Select-Object. But something to keep in mind here is that Select-Object is something you will use all the time in PowerShell. It’s so common in fact that PowerShell creates an alias for it called simply Select. The two terms are identical and do the exact same thing, just one is shorter and thus easier to type. That’s what we’ll use here
Get-Process | Select processname
- So what we’re doing is taking the entire object that contains all of the data from Get-Process, including all of the memory and cpu numbers but only displaying (or “selecting”) the processname column.
- Now have another look at the output from the Get-Process command. How many columns do you see? There should be 8 columns. That’s pretty good but it turns out there is a ton more information that the object is storing for you if you only know how to ask for it. In order to see all of the data that an object has, we need to get the object and pipe it into a special cmdlet called Get-Member. What Get-Member does is displays all of the types of data that is stored inside the object that was passed into it, which in this case is the get-process object
Get-Process | Get-Member
- There is a lot of data here but don’t be intimidated. There are really only 2 things you have to pay attention to. The first is the Name column. This is the name of the item inside the object that you can interact with. The thing to pay attention to is the MemberType. Remember I mentioned that objects can have methods. Well they also have properties. You can think of methods as actions or making the object do something while properties describe something about the object. I’d say that 95% of the time, you only care about properties. So if we scroll through the list, we see some properties called “Company” and “Path”. (Don’t worry about the difference between Property and ScriptProperty for now as for our purposes they are the same). Since we’re trying to identify malware, knowing the company that made the process and where it resides in the file system certainly sounds useful!
- How do we know to use those? Honestly it’s trial and error. There is so much to PowerShell that no one person can possibly know all of it. So PowerShell is designed to be all about exploration and making it as easy as possible to “fail quickly” to rapidly iterate to find the answer to your problem. In short, don’t be afraid to explore and try all of the properties to see what treasures they may hold.
Get-Process | Select name, path, company
- Ok, now we’re getting somewhere! The problem is now though we still have so much data. How can we go about and filter it? Before we discuss how to filter, let’s quickly discuss how we decided what to filter. Troubleshooting is an art as much as it is a science and ultimately we have to make a constant series of educated guesses until we arrive at our solution. In this case, I’m picking some filtering criteria based on my “gut feeling” but you could equally try approaching this problem from a thousand different ways. So try not to get caught up on the specifics but rather in the technique as this will apply to any kind of query within PowerShell
- With that out of the way, let’s say that since we know we’re looking for Malware, we’re going to suspect that it’s not going to be running on anything made by Microsoft. So let’s filter out everything where the Company name equals Microsoft.
- To do that we’re going to leverage a cmdlet called Where-Object. Note however that because the where filter is used so commonly in PowerShell, it actually has a built in alias called simply Where. The commands are identical and interchangeable – it’s just that “Where” is less typing so we’re going to use that.
- The Where cmdlet needs to know what you want to filter on. In order to do that, it wants to have something called a scriptblock given to it. A scriptblock is nothing more than a specific set of commands that are enclosed in the squiggly brackets “{” and “}”.
- If you were paying attention from earlier, I told you that the round brackets determined the order of execution of commands. This is why programming and scripting can be so frustrating to people. The difference between “()” and “{}” is the difference between a script that works and a script that fails. So be sure to pay attention to the details!
- The question now is what do we put in the scriptblock. This is where we come to probably one of the single most useful but also most difficult concepts to understand in all of PowerShell. That is the infamous $_ character. Often misunderstood, it’s actually not bad once you wrap your head around what it’s for. Remember I said that PowerShell stores its data inside objects? Each of those objects can be thought of as containing many rows, just like in a spreadsheet. In the screenshot above for example, we see “7+ Taskbar Tweaker” on the first line followed by the “ANT Agent” and so on.
- Each one of these are rows inside the object. When you type a command like Get-Process, PowerShell automatically iterates through each of these rows to display them to the screen for you. But what if you want to analyze the contents of each row? For example, what if you wanted to display only those records that contained the word “Microsoft”. The computer is ultimately going to have to access each row one-at-a-time to figure out if the word is in there or not. This all happens incredibly fast since it’s a computer doing the work but at the end of the day it’s doing the same thing you or I would do if we just sat down and evaluated each entry individually. Think of the $_ as representing the current row the computer is working on. It’s PowerShell’s way of answering the question “at this moment in time, which row am I analyzing?”
- Going back to our example, we now know that running Get-Process will generate an object that contains a bunch of different properties including ones called “Company” and “Path”. We know that every single process the cmdlet finds is stored as a separate row inside the object. Lastly we know that in order to access a property inside an object, we use the period.
- Putting this altogether, we’re going to get the object for Get-Process, pipe it to the Where cmdlet and then we’re going to loop through each row inside the object as identified by the $_ special character. We’re then going to access a property inside that object (which again is the get-process object) called Company.
- We’re then going to see if that row contains the keyword “Microsoft”. If it does, it passes the test and is included in the new object that is created that is then passed to the next line in the pipeline which is the select command.
- The last thing to touch on here is something called comparison operators. You probably know that the equals sign (=) is used to denote when something on the left of the equals sign is the same as on the right. I bring this up because it may seem counter-intuitive at first but PowerShell does not use the common characters like the = sign for comparisons. Instead, PowerShell uses words like “match”, and “equals”. To make matters even more interesting, for the most common operators like equals, greater than, not equal to etc, they abbreviate the word. So equals becomes eq. Greater than becomes gt and so on. Lastly, because they are now words, PowerShell needs some way to know that you are giving it a comparison operator and not say the name of the property of an object. To do that, you put a dash in front of the comparison operator. So –eq, –gt, –match and so on. Let’s see all of this together in the real world now
- Oh and one more thing — As great as PowerShell is, it can’t understand your intentions. If you write for example $_.company –match Microsoft, how does PowerShell know if Microsoft is the name of one of the properties in the object or if it is the actual text you want to search? The answer is it doesn’t so you have to tell it. You do that by making it something called a string. A string is simply a group of characters that you tell PowerShell to treat as plain text and to not do anything special with. You tell PowerShell to make a string by wrapping it in double quotes, ie –> ” <—.
Get-Process | Where {$_.company –notmatch “Microsoft” } | select name, path, company
- Ok, now we’re getting somewhere! The Microsoft results are gone but now we see a whole bunch of Google Chrome processes. We know those are valid so let’s filter those out as well
- The way we do that is with something called a boolean operator. This is just a fancy way of saying “and” and “or”. I want to filter out anything that has the words “Microsoft” and “Google” in it. In other words, both conditions have to be true for the overall condition to be met. Just as before, we need to tell Powershell that when we type the word “and” that it’s actually a boolean operator and not a object property so we also have to prefix it with a dash
Get-Process | Where {$_.company –notmatch “Microsoft” –and $_.name –notmatch “chrome” } | select name, path, company
- Nice. The problem we have now though is you see that the command is getting so long that it spreads over two lines. Now there is nothing wrong with that and the computer will have no issue with it, but we as humans can get more easily confused.
- If you want to make it a little easier on yourself to work with this longer script, Microsoft has a solution ready made for you. It’s called the PowerShell ISE or (Integrated Scripting Environment). It’s free and in fact already built into Windows itself. You can launch it from your start menu by typing ISE or clicking on the icon
- Once it’s open, you’ll now have two panes to work with. The top pane is your script editor and the bottom pane is your results. It works basically the same as before only now you have the following advantages:
- You can use your mouse to select text
- You can spread your code over multiple lines
- All the various PowerShell cmdlets and parameters and properties are all automatically color coded making them easier to read
- You can take advantage of something called intellisense whereby PowerShell can automatically type for you!
- The one difference worth noting is that pressing enter no longer executes your script. Instead you need to either press the green start button on the title bar or press F5
- Now that we’ve copied and pasted our in-progress script into the ISE, lets revisit where we left off. We have a list of all of the processes running on the system that are not made by Microsoft and are not Google Chrome
- But if we look closer at the results, we see that some entries are blank in the company field. If we look at the name column, we see that these are primarily “csrss” processes which we know to be core windows system processes and thus would not have a path. Let’s therefore remove any rows where the company is blank.
- To do that, we are going to take advantage of a special character in PowerShell which is the exclamation mark (!). It’s job is to negate or give the opposite meaning to anything that comes after it. More commonly it’s known as a “NOT” boolean operator. This might seem a little confusing at first however because PowerShell is actually doing some extra work for us automatically behind the scenes. In the example below, we see !$_.Company which translates into NOT Company. But what does that even mean? it turns out that in scripting, one of the most common things you will ever do is check to see if a value has something in it or not. If there is no data in the value, it is considered blank. If however the value has absolutely nothing in it, not even an empty value, it is considered a special kind of blank called a null. As I mentioned, checking for these situations is so common that PowerShell makes a giant assumption that says if you don’t provide it a comparison operator, it assumes you want to check if it’s blank. So in other words below when we write “!$_.Company”, PowerShell is filling in and transparently translating that into “(Current Row in Get Process Object).Company –ne “blank”. It’s a very common short hand that takes some getting used to but once you do, you’ll love it.
Get-Process | Where {$_.company –notmatch “Microsoft” –and $_.name –notmatch “chrome” –and !$_.Company } | select name, path, company
- At this point, our script is getting really long and frankly is getting kind of hard to read. It would be much easier to read if we could spread it over multiple lines
- Unfortunately, a new line in scripting is called a carriage return and has a specific meaning to PowerShell. This means if you try to format your code onto new lines so it is readable for you, you’ll likely break the script
- But don’t worry, PowerShell has your back. PowerShell has something called a line continuation character which is a fancy way of saying “let me put this over two lines but when you run it Mr. PowerShell, read it all as one line
- The line continuation character is the tilde or –> ` <—. It’s the character directly to the left of the number 1 on most keyboards. Now you can insert this anywhere you want but it makes sense to try to logically group your commands together
- Now when we look back at the results, we see that we have some processes that aren’t showing a path. Now this would normally be suspicious but looking in the our data we see a process called “happyfuntimes” running out of a temp folder
- This sounds very suspicious. Since we now know that the bad program does show a path, let’s filter out all of those that don’t since they are probably not our culprit. Apply the same concepts as before
Get-Process | Where {$_.company –notmatch “Microsoft” `
-and $_.name –notmatch “chrome” `
-and !$_.Company `
-and $_.path } | select name, path, company
- Ok, this happyfuntimes.exe process seems very unusual, especially because it’s running in the temp folder. Let’s use the same concepts we’ve learned to add another filter to include only those entries above that have “temp” mentioned somewhere in their path
Get-Process | Where {$_.company –notmatch “Microsoft” `
-and $_.name –notmatch “chrome” `
-and !$_.Company `
-and $_.path `
-and $_.path –match “temp”
} | select name, path, company
-
Ah ha! Only one result! That must be our malware! The user says the problem started around 9:30PM. I wonder when this process started. How can we figure that out?
-
The thing to remember again is that almost all PowerShell commands are non-destructive in terms of the object data they keep in memory. Even though you only display 2 columns, all the other data it has is still there waiting for you
-
How can you figure out what data is available? You can take your object and pass it to the select object just as you’ve done before. Only rather than select specific named properties, you can specify a wildcard also known as an asterisk “*” which tells PowerShell to return absolutely everything it has for the object
Get-Process | Where {$_.company –notmatch “Microsoft” `
-and $_.name –notmatch “chrome” `
-and !$_.Company `
-and $_.path `
-and $_.path –match “temp”
} | select *
- Would you look at that. There is a property for starttime! Remember, PowerShell is all about exploration. This kind of surprise discovery happens constantly
- Next let’s present our findings in as plain a way as possible so we can show the user the process that appears to be culprit
- Now that you know the name of the properties you want, just add them to your select command
Get-Process | Where {$_.company –notmatch “Microsoft” `
-and $_.name –notmatch “chrome” `
-and !$_.Company `
-and $_.path `
-and $_.path –match “temp”
} | select path, starttime
- Ok, that’s great, but there is a huge space between the Path and StartTime columns that I find annoying. How can we bring those together?
- There is another command built into PowerShell called Format-Table. This cmdlet, like every other cmdlet in PowerShell accepts additional configuration commands called parameters. Parameters dictate how specifically the cmdlet should function. Just as with comparison operators before, you need to explicitly tell PowerShell that you want to give it a parameter and you do that by prefacing it with a dash (“-“). In this case, the parameter we want to specify is “-Autosize” which will fit our columns better
Get-Process | Where {$_.company –notmatch “Microsoft” `
-and $_.name –notmatch “chrome” `
-and !$_.Company `
-and $_.path `
-and $_.path –match “temp”
} | select path, starttime | Format-Table –AutoSize
- Let’s say we now want to store the results of our research into an easy to use package for later use. We can do this by leveraging one of the single most powerful features of any programming language called variables.
- Variables are simply representations of data. If you studied algebra at all in school then you are already familiar with variables. That’s the thing where let’s say that x = 2. In that case, what is x + 5?
- Here x is representing the value of 2 so when it comes time to execute the math, the 2 will be substituted for the x and the result will be 2 + 5 which equals 7.
- PowerShell has variables as well. But they have the same problem parameters and comparison operators have in that if you just type “x”, PowerShell has no way of knowing if you mean a variable or a property
- PowerShell solves this problem by introducing another special character that is used to denote when a word should be considered a variable. That special character is the dollar sign ($)
- When you put the dollar sign in front of any word, it automatically becomes a variable and can represent some other data
- For example, $MyName = “Robbie”. (Note that my name must be in quotes to denote it as a string of text, otherwise PowerShell will think it’s a command or a property)
- Now any time I want to display “Robbie”, I can simply type $MyName
- That’s cool and all, but it turns out variables are where PowerShell really stands out from all the other programming and scripting languages of the world. Why? Because its variables are all objects.
- That’s right, without any effort at all, you can do this: $AllProcesses = Get-Process
- What just happened there? Every single process running on your computer, along with it’s current memory usage, CPU usage, start time, command line path and everything else you’ve seen is all stored inside the variable called $AllProcesses
- The power of this simply cannot be overstated. You can now compare those results of any of the literally thousands of properties PowerShell has to the current state or to another variable or even subset of variables. Not only that, but you can have as many variables as you want with as many different objects as you want!
- Let’s say we want to place this known bad process into a variable called $BadProcess. All we need to do is simply add this variable name to the front of our code and use the equals sign to assign our process object to it.
- Important note: Whenever saving to an object, you should always remove any select or format-table options. By removing the select option, you gain the ability to look at all of the properties in your new object later. If you include the select clause, all of the other data not explicitly described is removed. That may be OK but generally it never hurts to have options. On top of that, Format-Table does some things to the object that can cause issues for other uses down the road so best practice is to just avoid that problem by not including it. Think of Format-Table as only useful when you want to display data to to the screen. If you’re writing it to a variable instead, there is no point in formatting it
$BadProcess = Get-Process | Where {$_.company –notmatch “Microsoft” `
-and $_.name –notmatch “chrome” `
-and !$_.Company `
-and $_.path `
-and $_.path –match “temp”
}
- Now let’s say that the user confirms that they don’t know what this process is and so you want to stop the process so it is no longer running. Going back to the beginning of this post, most PowerShell commands follow a fairly obvious naming scheme. Guess what the command is to stop a process? That’s right, Stop-Process
- So what we can do is take the variable $BadProcess (which remember contains an object describing the process happyfuntimes.exe including things like the PID (Process ID)) and then we use the pipeline character to pipe this object into the Stop-Process command. The result?
$BadProcess = Get-Process | Where {$_.company –notmatch “Microsoft” `
-and $_.name –notmatch “chrome” `
-and !$_.Company `
-and $_.path `
-and $_.path –match “temp”
}$BadProcess | Stop-Process
- Well nothing it looks like at first. That’s the thing to remember about PowerShell. Generally speaking, if a PowerShell command worked, it won’t give you any output. If you see output, that generally means there was a problem
- But PowerShell is not without options. If you do want to see what the command did, Stop-Process, along with most other cmdlets have a parameter called –verbose that shows what the command actually does.
$BadProcess = Get-Process | Where {$_.company –notmatch “Microsoft” `
-and $_.name –notmatch “chrome” `
-and !$_.Company `
-and $_.path `
-and $_.path –match “temp”
}$BadProcess | Stop-Process –Verbose
- Lastly, we can check our work to see if the process has in fact been killed by counting how many active processes are currently running. Recall that before we started it was 179
- We did it! We solved the case of the annoying pop up Malware and we did it using PowerShell! Through it all we learned about in no particular order:
- Variables
- Objects
- Properties
- Methods
- Strings
- The Pipeline
- Parameters
- Boolean Operators
- $_ (Current Object)
- Comparison Operators
If you found this blog post useful, please leave a comment below. If there is interest, I’d be more than happy to do an additional post where I could demonstrate concepts such as but not limited to:
- Typecasting
- Loops
- Comments
- Functions
- Hash Tables
- Arrays
- Error Handling
- The (Excellent) Help System
- Collections
- If Statements
– Robbie
3 comments
Excellent walk through! Please follow up with more of the “true” case examples!!
it’s really helpful for the beginner to understand the basic concept of Power shell. Thank you
I’m just getting in to real scripting with PowerShell and this is by FAR the best tutorial I have ever read on any programming language. It has a real world issue that is simple enough to understand (not over-complicated is the key) and teaches you so many different things; where as, other sources would take hours to teach this and you’d still not know what you’re doing. Thank you for all of the help. I start my new job tomorrow and I know this will help for miles to come.