Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Everything you wanted to know about the
Like many other languages, PowerShell has statements for conditionally executing code in your scripts. One of those statements is the If statement. Today we will take a deep dive into one of the most fundamental commands in PowerShell.
Note
The original version of this article appeared on the blog written by @KevinMarquette. The PowerShell team thanks Kevin for sharing this content with us. Please check out his blog at PowerShellExplained.com.
Conditional execution
Your scripts often need to make decisions and perform different logic based on those decisions.
This is what I mean by conditional execution. You have one statement or value to evaluate, then
execute a different section of code based on that evaluation. This is exactly what the if
statement does.
The if statement
Here is a basic example of the if statement:
$condition = $true
if ( $condition )
{
Write-Output "The condition was true"
}
The first thing the if statement does is evaluate the expression in parentheses. If it evaluates
to $true, then it executes the scriptblock in the braces. If the value was $false, then it
would skip over that scriptblock.
In the previous example, the if statement was just evaluating the $condition variable. It was
$true and would have executed the Write-Output command inside the scriptblock.
In some languages, you can place a single line of code after the if statement and it gets
executed. That isn't the case in PowerShell. You must provide a full scriptblock with braces for
it to work correctly.
Comparison operators
The most common use of the if statement for is comparing two items with each other. PowerShell has
special operators for different comparison scenarios. When you use a comparison operator, the value
on the left-hand side is compared to the value on the right-hand side.
-eq for equality
The -eq does an equality check between two values to make sure they're equal to each other.
$value = Get-MysteryValue
if ( 5 -eq $value )
{
# do something
}
In this example, I'm taking a known value of 5 and comparing it to my $value to see if they
match.
One possible use case is to check the status of a value before you take an action on it. You could
get a service and check that the status was running before you called Restart-Service on it.
It's common in other languages like C# to use == for equality (ex: 5 == $value) but that doesn't
work with PowerShell. Another common mistake that people make is to use the equals sign (ex:
5 = $value) that is reserved for assigning values to variables. By placing your known value on the
left, it makes that mistake more awkward to make.
This operator (and others) has a few variations.
-eqcase-insensitive equality-ieqcase-insensitive equality-ceqcase-sensitive equality
-ne not equal
Many operators have a related operator that is checking for the opposite result. -ne verifies that
the values don't equal each other.
if ( 5 -ne $value )
{
# do something
}
Use this to make sure that the action only executes if the value isn't 5. A good use-cases where
would be to check if a service was in the running state before you try to start it.
Variations:
-necase-insensitive not equal-inecase-insensitive not equal-cnecase-sensitive not equal
These are inverse variations of -eq. I'll group these types together when I list variations
for other operators.
-gt -ge -lt -le for greater than or less than
These operators are used when checking to see if a value is larger or smaller than another value.
The -gt -ge -lt -le stand for GreaterThan, GreaterThanOrEqual, LessThan, and LessThanOrEqual.
if ( $value -gt 5 )
{
# do something
}
Variations:
-gtgreater than-igtgreater than, case-insensitive-cgtgreater than, case-sensitive-gegreater than or equal-igegreater than or equal, case-insensitive-cgegreater than or equal, case-sensitive-ltless than-iltless than, case-insensitive-cltless than, case-sensitive-leless than or equal-ileless than or equal, case-insensitive-cleless than or equal, case-sensitive
I don't know why you would use case-sensitive and insensitive options for these operators.
-like wildcard matches
PowerShell has its own wildcard-based pattern matching syntax and you can use it with the -like
operator. These wildcard patterns are fairly basic.
?matches any single character*matches any number of characters
$value = 'S-ATX-SQL01'
if ( $value -like 'S-*-SQL??')
{
# do something
}
It's important to point out that the pattern matches the whole string. If you need to match
something in the middle of the string, you need to place the * on both ends of the
string.
$value = 'S-ATX-SQL02'
if ( $value -like '*SQL*')
{
# do something
}
Variations:
-likecase-insensitive wildcard-ilikecase-insensitive wildcard-clikecase-sensitive wildcard-notlikecase-insensitive wildcard not matched-inotlikecase-insensitive wildcard not matched-cnotlikecase-sensitive wildcard not matched
-match regular expression
The -match operator allows you to check a string for a regular-expression-based match. Use this
when the wildcard patterns aren't flexible enough for you.
$value = 'S-ATX-SQL01'
if ( $value -match 'S-\w\w\w-SQL\d\d')
{
# do something
}
A regex pattern matches anywhere in the string by default. So you can specify a substring that you want matched like this:
$value = 'S-ATX-SQL01'
if ( $value -match 'SQL')
{
# do something
}
Regex is a complex language of its own and worth looking into. I talk more about -match and
the many ways to use regex in another article.
Variations:
-matchcase-insensitive regex-imatchcase-insensitive regex-cmatchcase-sensitive regex-notmatchcase-insensitive regex not matched-inotmatchcase-insensitive regex not matched-cnotmatchcase-sensitive regex not matched
-is of type
You can check a value's type with the -is operator.
if ( $value -is [string] )
{
# do something
}
You may use this if you're working with classes or accepting various objects over the pipeline. You could have either a service or a service name as your input. Then check to see if you have a service and fetch the service if you only have the name.
if ( $Service -isnot [System.ServiceProcess.ServiceController] )
{
$Service = Get-Service -Name $Service
}
Variations:
-isof type-isnotnot of type
Collection operators
When you use the previous operators with a single value, the result is $true or $false. This is
handled slightly differently when working with a collection. Each item in the collection gets
evaluated and the operator returns every value that evaluates to $true.
PS> 1,2,3,4 -eq 3
3
This still works correctly in an if statement. So a value is returned by your operator, then the
whole statement is $true.
$array = 1..6
if ( $array -gt 3 )
{
# do something
}
There's one small trap hiding in the details here that I need to point out. When using the -ne
operator this way, it's easy to mistakenly look at the logic backwards. Using -ne with a
collection returns $true if any item in the collection doesn't match your value.
PS> 1,2,3 -ne 4
1
2
3
This may look like a clever trick, but we have operators -contains and -in that handle this more
efficiently. And -notcontains does what you expect.
-contains
The -contains operator checks the collection for your value. As soon as it finds a match, it
returns $true.
$array = 1..6
if ( $array -contains 3 )
{
# do something
}
This is the preferred way to see if a collection contains your value. Using Where-Object (or
-eq) walks the entire list every time and is significantly slower.
Variations:
-containscase-insensitive match-icontainscase-insensitive match-ccontainscase-sensitive match-notcontainscase-insensitive not matched-inotcontainscase-insensitive not matched-cnotcontainscase-sensitive not matched
-in
The -in operator is just like the -contains operator except the collection is on the right-hand
side.
$array = 1..6
if ( 3 -in $array )
{
# do something
}
Variations:
-incase-insensitive match-iincase-insensitive match-cincase-sensitive match-notincase-insensitive not matched-inotincase-insensitive not matched-cnotincase-sensitive not matched
Logical operators
Logical operators are used to invert or combine other expressions.
-not
The -not operator flips an expression from $false to $true or from $true to $false. Here
is an example where we want to perform an action when Test-Path is $false.
if ( -not ( Test-Path -Path $path ) )
Most of the operators we talked about do have a variation where you do not need to use the -not
operator. But there are still times it is useful.
! operator
You can use ! as an alias for -not.
if ( -not $value ){}
if ( !$value ){}
You may see ! used more by people that come from another languages like C#. I prefer to type it
out because I find it hard to see when quickly looking at my scripts.
-and
You can combine expressions with the -and operator. When you do that, both sides need to be
$true for the whole expression to be $true.
if ( ($age -gt 13) -and ($age -lt 55) )
In that example, $age must be 13 or older for the left side and less than 55 for the right side. I
added extra parentheses to make it clearer in that example but they're optional as long as the
expression is simple. Here is the same example without them.
if ( $age -gt 13 -and $age -lt 55 )
Evaluation happens from left to right. If the first item evaluates to $false, it exits early and
doesn't perform the right comparison. This is handy when you need to make sure a value exists before
you use it. For example, Test-Path throws an error if you give it a $null path.
if ( $null -ne $path -and (Test-Path -Path $path) )
-or
The -or allows for you to specify two expressions and returns $true if either one of them is
$true.
if ( $age -le 13 -or $age -ge 55 )
Just like with the -and operator, the evaluation happens from left to right. Except that if the
first part is $true, then the whole statement is $true and it doesn't process the rest of the
expression.
Also make note of how the syntax works for these operators. You need two separate expressions. I
have seen users try to do something like this $value -eq 5 -or 6 without realizing their mistake.
-xor exclusive or
This one is a little unusual. -xor allows only one expression to evaluate to $true. So if both
items are $false or both items are $true, then the whole expression is $false. Another way to
look at this is the expression is only $true when the results of the expression are different.
It's rare that anyone would ever use this logical operator and I can't think up a good example as to why I would ever use it.
Bitwise operators
Bitwise operators perform calculations on the bits within the values and produce a new value as the result. Teaching bitwise operators is beyond the scope of this article, but here is the list of them.
-bandbinary AND-borbinary OR-bxorbinary exclusive OR-bnotbinary NOT-shlshift left-shrshift right
PowerShell expressions
We can use normal PowerShell inside the condition statement.
if ( Test-Path -Path $Path )
Test-Path returns $true or $false when it executes. This also applies to commands that return
other values.
if ( Get-Process Notepad* )
It evaluates to $true if there's a returned process and $false if there isn't. It's
perfectly valid to use pipeline expressions or other PowerShell statements like this:
if ( Get-Process | where Name -EQ Notepad )
These expressions can be combined with each other with the -and and -or operators, but you may
have to use parenthesis to break them into subexpressions.
if ( (Get-Process) -and (Get-Service) )
Checking for $null
Having a no result or a $null value evaluates to $false in the if statement. When checking
specifically for $null, it's a best practice to place the $null on the left-hand side.
if ( $null -eq $value )
There are quite a few nuances when dealing with $null values in PowerShell. If you're interested
in diving deeper, I have an article about everything you wanted to know about $null.
Variable assignment within the condition
I almost forgot to add this one until Prasoon Karunan V reminded me of it.
if ($process=Get-Process notepad -ErrorAction Ignore) {$process} else {$false}
Normally when you assign a value to a variable, the value isn't passed onto the pipeline or console. When you do a variable assignment in a sub expression, it does get passed on to the pipeline.
PS> $first = 1
PS> ($second = 2)
2
See how the $first assignment has no output and the $second assignment does? When an assignment
is done in an if statement, it executes just like the $second assignment above. Here is a clean
example on how you could use it:
if ( $process = Get-Process Notepad* )
{
$process | Stop-Process
}
If $process gets assigned a value, then the statement is $true and $process gets stopped.
Make sure you don't confuse this with -eq because this isn't an equality check. This is a more
obscure feature that most people don't realize works this way.
Variable assignment from the scriptblock
You can also use the if statement scriptblock to assign a value to a variable.
$discount = if ( $age -ge 55 )
{
Get-SeniorDiscount
}
elseif ( $age -le 13 )
{
Get-ChildDiscount
}
else
{
0.00
}
Each script block is writing the results of the commands, or the value, as output. We can assign the
result of the if statement to the $discount variable. That example could have just as easily
assigned those values to the $discount variable directly in each scriptblock. I can't say that I
use this with the if statement often, but I do have an example where I used this recently.
Alternate execution path
The if statement allows you to specify an action for not only when the statement is $true, but
also for when it's $false. This is where the else statement comes into play.
else
The else statement is always the last part of the if statement when used.
if ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
else
{
Write-Warning "$path doesn't exist or isn't a file."
}
In this example, we check the $path to make sure it's a file. If we find the file, we move it. If
not, we write a warning. This type of branching logic is very common.
Nested if
The if and else statements take a script block, so we can place any PowerShell command inside
them, including another if statement. This allows you to make use of much more complicated logic.
if ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
else
{
if ( Test-Path -Path $Path )
{
Write-Warning "A file was required but a directory was found instead."
}
else
{
Write-Warning "$path could not be found."
}
}
In this example, we test the happy path first and then take action on it. If that fails, we do another check and to provide more detailed information to the user.
elseif
We aren't limited to just a single conditional check. We can chain if and else statements
together instead of nesting them by using the elseif statement.
if ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
elseif ( Test-Path -Path $Path )
{
Write-Warning "A file was required but a directory was found instead."
}
else
{
Write-Warning "$path could not be found."
}
The execution happens from the top to the bottom. The top if statement is evaluated first. If that
is $false, then it moves down to the next elseif or else in the list. That last else is the
default action to take if none of the others return $true.
switch
At this point, I need to mention the switch statement. It provides an alternate syntax for doing
multiple comparisons with a value. With the switch, you specify an expression and that result gets
compared with several different values. If one of those values match, the matching code block is
executed. Take a look at this example:
$itemType = 'Role'
switch ( $itemType )
{
'Component'
{
'is a component'
}
'Role'
{
'is a role'
}
'Location'
{
'is a location'
}
}
There three possible values that can match the $itemType. In this case, it matches with Role. I
used a simple example just to give you some exposure to the switch operator. I talk more
about everything you ever wanted to know about the switch statement in another article.
Array inline
I have a function called Invoke-SnowSql that launches an executable with several command-line arguments. Here is a clip from that function where I build the array of arguments.
$snowSqlParam = @(
'--accountname', $Endpoint
'--username', $Credential.UserName
'--option', 'exit_on_error=true'
'--option', 'output_format=csv'
'--option', 'friendly=false'
'--option', 'timing=false'
if ($Debug)
{
'--option', 'log_level=DEBUG'
}
if ($Path)
{
'--filename', $Path
}
else
{
'--query', $singleLineQuery
}
)
The $Debug and $Path variables are parameters on the function that are provided by the end user.
I evaluate them inline inside the initialization of my array. If $Debug is true, then those values
fall into the $snowSqlParam in the correct place. Same holds true for the $Path variable.
Simplify complex operations
It's inevitable that you run into a situation that has way too many comparisons to check and your
if statement scrolls way off the right side of the screen.
$user = Get-ADUser -Identity $UserName
if ( $null -ne $user -and $user.Department -eq 'Finance' -and $user.Title -match 'Senior' -and $user.HomeDrive -notlike '\\server\*' )
{
# Do Something
}
They can be hard to read and that make you more prone to make mistakes. There are a few things we can do about that.
Line continuation
There some operators in PowerShell that let you wrap you command to the next line. The logical
operators -and and -or are good operators to use if you want to break your expression into
multiple lines.
if ($null -ne $user -and
$user.Department -eq 'Finance' -and
$user.Title -match 'Senior' -and
$user.HomeDrive -notlike '\\server\*'
)
{
# Do Something
}
There's still a lot going on there, but placing each piece on its own line makes a big difference. I generally use this when I get more than two comparisons or if I have to scroll to the right to read any of the logic.
Pre-calculating results
We can take that statement out of the if statement and only check the result.
$needsSecureHomeDrive = $null -ne $user -and
$user.Department -eq 'Finance' -and
$user.Title -match 'Senior' -and
$user.HomeDrive -notlike '\\server\*'
if ( $needsSecureHomeDrive )
{
# Do Something
}
This just feels much cleaner than the previous example. You also are given an opportunity to use a variable name that explains what it's that you're really checking. This is also and example of self-documenting code that saves unnecessary comments.
Multiple if statements
We can break this up into multiple statements and check them one at a time. In this case, we use a flag or a tracking variable to combine the results.
$skipUser = $false
if( $null -eq $user )
{
$skipUser = $true
}
if( $user.Department -ne 'Finance' )
{
Write-Verbose "isn't in Finance department"
$skipUser = $true
}
if( $user.Title -match 'Senior' )
{
Write-Verbose "Doesn't have Senior title"
$skipUser = $true
}
if( $user.HomeDrive -like '\\server\*' )
{
Write-Verbose "Home drive already configured"
$skipUser = $true
}
if ( -not $skipUser )
{
# do something
}
I did have to invert the logic to make the flag logic work correctly. Each evaluation is an
individual if statement. The advantage of this is that when you're debugging, you can tell
exactly what the logic is doing. I was able to add much better verbosity at the same time.
The obvious downside is that it's so much more code to write. The code is more complex to look at as it takes a single line of logic and explodes it into 25 or more lines.
Using functions
We can also move all that validation logic into a function. Look at how clean this looks at a glance.
if ( Test-SecureDriveConfiguration -ADUser $user )
{
# do something
}
You still have to create the function to do the validation, but it makes this code much easier to
work with. It makes this code easier to test. In your tests, you can mock the call to
Test-ADDriveConfiguration and you only need two tests for this function. One where it returns
$true and one where it returns $false. Testing the other function is simpler because it's
so small.
The body of that function could still be that one-liner we started with or the exploded logic that we used in the last section. This works well for both scenarios and allows you to easily change that implementation later.
Error handling
One important use of the if statement is to check for error conditions before you run into
errors. A good example is to check if a folder already exists before you try to create it.
if ( -not (Test-Path -Path $folder) )
{
New-Item -Type Directory -Path $folder
}
I like to say that if you expect an exception to happen, then it's not really an exception. So check your values and validate your conditions where you can.
If you want to dive a little more into actual exception handling, I have an article on everything you ever wanted to know about exceptions.
Final words
The if statement is such a simple statement but is a fundamental piece of PowerShell. You will
find yourself using this multiple times in almost every script you write. I hope you have a better
understanding than you had before.