PowerShell for Sysadmins

imageHello, Habitants! PowerShell is both a scripting language and a command shell that allows you to manage your system and automate almost any task. In PowerShell for Sysadmins, Microsoft MVP owner Adam Bertram aka “the Automator” shows you how to use PowerShell so that the reader finally has time for toys, yoga and cats. You will learn to: -Combine commands, control the flow of execution, handle errors, write scripts, run them remotely and test them using the Pester testing framework. -Analyze structured data such as XML and JSON, work with popular services (such as Active Directory, Azure and Amazon Web Services), create server monitoring systems. -Create and design PowerShell modules. -Use PowerShell for convenient,fully automated Windows installation. -Create an Active Directory forest with just a Hyper-V host and a few ISO files. -Create countless web and SQL servers with just a few lines of code! Real-life examples help bridge the gap between theory and work in a real system, and the author's light humor makes reading easier. Stop relying on expensive software and vague advice from the web!



Who is this book for
- , . DevOps, , / (CI/CD).



, PowerShell . PowerShell « Windows» — Microsoft, PowerShell . , , .



Control flow



Let's repeat a little. In Chapter 3, we learned how you can combine commands using a pipeline and external scripts. Chapter 2 covered variables and how to use them to store values. One of the main advantages of working with variables is the ability to use them to write code that works not with value, but with "meaning." Instead of working with the number 3, for example, you will be working with the general concept of $ serverCount. This allows you to write code that works the same whether you have one, two, or a thousand servers. Combine this ability with the ability to save your code in scripts that can be run on different computers, and you can start solving problems on a much larger scale.



However, in life, it sometimes matters whether you work with one server, with two, or with a thousand. So far, you don't have a suitable way to account for this, and your scripts simply work from top to bottom, unable to adapt based on certain values. In this chapter, we will use control flow and conditional logic to write scripts that will execute different commands depending on the values ​​they operate on. By the end of the chapter, you will learn how to use if / then and switch statements, as well as various loops, to give your code the much needed flexibility.



A little about control flow



We will write a script that reads the contents of a file stored on multiple remote computers. To continue, download a file called App_configuration.txt from the book's materials at github.com/adbertram/PowerShellForSysadmins/ and place it in the root of the C: \ drive on multiple remote computers. If you don't have remote computers, just keep reading for now. For this example, I will use the servers named SRV1, SRV2, SRV3, SRV4, and SRV5.



To access the contents of the file, use the Get-Content command and specify the path to the file in the value of the Path parameter argument, as shown below:



Get-Content -Path "\\ servername \ c $ \ App_configuration.txt"



First, let's save all the names of our servers in an array and run this command for each server. Open a new .ps1 file and enter the code from Listing 4.1 into it.



Listing 4.1. Extracting file content from multiple servers



$servers = @('SRV1','SRV2','SRV3','SRV4','SRV5')
Get-Content -Path "\\$($servers[0])\c$\App_configuration.txt"
Get-Content -Path "\\$($servers[1])\c$\App_configuration.txt"
Get-Content -Path "\\$($servers[2])\c$\App_configuration.txt"
Get-Content -Path "\\$($servers[3])\c$\App_configuration.txt"
Get-Content -Path "\\$($servers[4])\c$\App_configuration.txt"
      
      





In theory, this code should work without issue. But this example assumes that something is going wrong for you. What if the SRV2 server is down? What if someone forgot to put App_configuration.txt on SRV4? Or maybe someone changed the file path? You can write a separate script for each server, but this solution won't scale, especially as you start adding more and more servers. You need code that will work depending on the situation.



The idea behind control flow is that it allows you to execute different sets of instructions based on predefined logic. Imagine that your scripts are executed along a specific path. So far, your path is simple - from the first line of code to the last. However, you can add forks along the way, return to places you have already visited, or jump over them. By forking the execution paths of your script, you give it more flexibility, allowing you to handle many situations with a single script.



We'll start by looking at the simplest type of control flow, the conditional statement.



Using conditional statements



In Chapter 2, we learned that there are logical values: true and false. Booleans allow you to create conditional statements that tell PowerShell to execute a specific block of code based on whether an expression (called a condition) evaluates to True or False. A condition is a yes / no question. Do you have more than five servers? Is server 3 running? Does the file path exist? To get started using conditional statements, let's see how to convert such questions to expressions.



Building Expressions with Operators You



can write logical expressions using comparison operators that compare values. To use a comparison operator, you need to place it between two values, for example:



PS> 1 –eq 1
True
      
      





In this case, the –eq operator allows you to determine the equivalence of two values.

Below is a list of the most common comparison operators we will use:



-eq compares two values ​​and returns True if they are equal.



-ne compares two values ​​and returns True if they are not equal.



-gt compares two values ​​and returns True if the first is greater than the second.



-ge compares two values ​​and returns True if the first is greater than or equal to the second.



-lt compares two values ​​and returns True if the first is less than the second.



-le compares two values ​​and returns True if the first is less than or equal to the second.



-contains returns True if the second value is part of the first. For example, this operator allows you to determine if a value is inside an array.



PowerShell also has more advanced comparison operators. We will not dwell on them here, but I recommend that you read about them in the Microsoft documentation at docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comparison_operators or in the PowerShell Help section (see chapter 1).



You can use the above operators to compare variables and values. But the expression doesn't have to be a comparison. Sometimes PowerShell commands can be used as conditions. In the previous example, we wanted to know the server availability. You can use the Test-Connection cmdlet to test the connection to the server. Typically, the output of the Test-Connection cmdlet contains a lot of different information, but using the Quiet parameter you can force the command to return True or False, and using the Count parameter, you can limit the test to one attempt.



PS> Test-Connection -ComputerName offlineserver -Quiet -Count 1
False
      
      





PS> Test-Connection -ComputerName onlineserver -Quiet -Count 1
True
      
      





To find out if the server is down, you can use the –not operator to reverse the expression:



PS> -not (Test-Connection -ComputerName offlineserver -Quiet -Count 1)
True
      
      





Now that you are familiar with basic expressions, let's look at the simplest conditional operator.



The if statement



The if statement works simply: if X is true, then do Y. That's it!



To use a statement in an expression, write the if keyword followed by parentheses containing the condition. The expression is followed by a block of code enclosed in curly braces. PowerShell will only execute this block of code if the expression evaluates to True. If the if expression evaluates to False or returns nothing at all, the code block will not be executed. The syntax for the if / then statement is shown in Listing 4.2.



Listing 4.2. If statement syntax



if () {
   #  ,   
}
      
      





This example has a bit of new syntax: the hash (#) character denotes a comment — this is text that PowerShell ignores. You can use comments to leave helpful notes and descriptions for yourself or someone else who will later read your code.



Now, let's take another look at the code shown in Listing 4.1. I'll show you how to use an if statement to avoid trying to reach a dead server. We already saw in the previous section that the Test-Connection command can be used as an expression that returns True or False, so for now let's wrap Test-Connection in an if statement and then use the Get-Content command to avoid trying to access a dead server. ... For now, we'll only change the code for the first server, as shown in Listing 4.3.



Listing 4.3. Using an if statement to selectively access



$servers = @('SRV1','SRV2','SRV3','SRV4','SRV5')
if (Test-Connection -ComputerName $servers[0] -Quiet -Count 1) {
   Get-Content -Path "\\$($servers[0])\c$\App_configuration.txt"
}
Get-Content -Path "\\$($servers[1])\c$\App_configuration.txt"
----
      
      





Since you have Get-Content in your if statement, you will not run into any errors if you try to access a broken server; if the test fails, your script will know not to try to read the file. The code will try to access the server only if it already knows that it is enabled. But note that this code only fires if the condition is true. Quite often, you will need to specify one script behavior for a true condition and another for a false one. In the next section, you will see how to define behavior for a false condition using the else statement.



Else statement



To provide your if statement with an alternative, you can use the else keyword after the closing parenthesis of the if block, followed by another pair of curly braces containing the code block. As shown in Listing 4.4, we'll use an else statement to return an error to the console if the first server doesn't respond.



Listing 4.4. Using else clause to run code if condition is

not true



if (Test-Connection -ComputerName $servers[0] -Quiet -Count 1) {
   Get-Content -Path "\\$($servers[0])\c$\App_configuration.txt"
} else {
   Write-Error -Message "The server $($servers[0]) is not responding!"
}
      
      





The if / else statement works great when you have two mutually exclusive situations. In this case, the server is either connected or not, that is, we only need two branches of the code. Let's see how to deal with more complex situations.



Elseif statement



The else statement covers the opposite situation: if if doesn't work, then do it anyway. This approach works for binary conditions, that is, when the server is either running or not. But sometimes you have to deal with a large number of options. For example, suppose you have a server that does not have the file you want, and you have stored the name of that server in the $ problemServer variable (add this line of code to your script!). This means that you need an additional check to see if the server you are currently polling is the problem server. This can be accomplished with nested if statements, as shown in the code below:



if (Test-Connection -ComputerName $servers[0] -Quiet -Count 1) {
   if ($servers[0] –eq $problemServer) {
      Write-Error -Message "The server $servers[0] does not have the right
         file!"
   } else {
      Get-Content -Path "\\$servers[0]\c$\App_configuration.txt"
   }
} else {
   Write-Error -Message "The server $servers[0] is not responding!"
}
----
      
      





But there is also a neater way to implement the same logic, with an elseif statement, which allows you to check an additional condition before returning to the else block. The syntax for the elseif block is identical to the syntax for the if block. So, to test the problem server with the elseif statement, run the code in Listing 4.5.



Listing 4.5. Using the elseif block



if (-not (Test-Connection -ComputerName $servers[0] -Quiet -Count 1)) { 
   Write-Error -Message "The server $servers[0] is not responding!"
} elseif ($servers[0] –eq $problemServer) 
   Write-Error -Message "The server $servers[0] does not have the right file!"
} else {
   Get-Content -Path "\\$servers[0]\c$\App_configuration.txt"
}
----
      
      





Please note that we did not just add an elseif statement, but at the same time changed the logic of the code. We can now check if the server is offline using the –not operator. Then, once we have determined the server's network status, we check to see if it is problematic. If this is not the case, we use the else statement to trigger the default file checkout behavior. As you can see, there are several ways to structure your code in this way. The important thing is that the code works and that it is readable for an outsider, whether it is your colleague seeing it for the first time, or you yourself some time after writing it.



You can chain as many elseif statements as you like, allowing for a wide variety of circumstances. However, elseif statements are mutually exclusive: when one of the elseifs evaluates to True, PowerShell only runs its code and does not check for other cases. In Listing 4.5, this did not cause any problems, since you only needed to test the server for "problems" after checking the health, but in the future I advise you to keep this feature in mind.



The if, else, and elseif statements are great for implementing code responses to simple yes / no questions. In the next section, you will learn how to work with more complex logic.



Switch statement



Let's tweak our example a bit. Let's say we have five servers, and on each server the path to the required file is different. Based on what you know now, you will need to create a separate elseif statement for each server. This will work, but there is a more convenient method.



Please note that now we will be working with a different type of condition. If earlier we needed answers to yes / no questions, now we want to get the specific meaning of one thing. Is it an SRV1 server? SRV2? Etc. If you were only working with one or two specific values, an if statement would work, but in this case, a switch statement works much better.



The switch statement allows you to execute different pieces of code depending on some value. It consists of the switch keyword followed by a parenthesized expression. Inside the switch block is a series of statements, structured like this: first you specify a value, followed by a set of curly braces containing the code block, and finally a default block, as shown in Listing 4.6.



Listing 4.6. Switch statement template



switch () {
    {
      # 
   }
    {
   }
   default {
     # ,     
   }
}
      
      





A switch statement can contain an almost unlimited number of values. If the expression evaluates to a value, the corresponding code within the block is executed. The important thing is that, unlike elseif, after executing one block of code, PowerShell will continue to check the rest of the conditions, unless otherwise noted. If none of the values ​​match, PowerShell will execute the code in the default block. To stop iterating over conditions in a switch statement, use the break keyword at the end of the code block, as shown in Listing 4.7.



Listing 4.7. Using the break keyword in a switch statement



switch () {
    {
      # 
      break
   }
----
      
      





The break keyword allows you to make conditions in a switch statement mutually exclusive. Let's go back to our example with five servers and the same file with different paths. You know that the server you are working with can only have one value (that is, it cannot be named both SRV1 and SRV2 at the same time), so you need to use break statements. Your script should look something like the one shown in Listing 4.8.



Listing 4.8. Checking various servers with a switch statement



$currentServer = $servers[0]
switch ($currentServer) {
   $servers[0] {
      # Check if server is online and get content at SRV1 path.
      break
   }
   $servers[1] {
      ## Check if server is online and get content at SRV2 path.
      break
   }
   $servers[2] {
      ## Check if server is online and get content at SRV3 path.
      break
   }
----
      
      





You can rewrite this code using only if and elseif statements (and I really recommend you try this!). Either way, whichever method you choose, you need the same structure for every server in the list, which means your script is going to be quite long - just imagine testing five hundred servers instead of five. In the next section, you will learn how to get rid of this problem by using one of the most fundamental control flow structures, a loop.



Using loops



There is a good rule of thumb for computer work: don't repeat yourself (DRY). If you find yourself doing the same job, chances are there is a way to automate it. It's the same with coding: if you use the same lines over and over, there is probably a better solution.



One way to avoid repetitions is to use loops. A loop allows you to repeatedly execute code until some specified condition changes. The stop condition can start the loop a specified number of times, either until some boolean value changes, or define the loop to run indefinitely. We will call each pass of the loop an iteration.



PowerShell offers five types of loops: foreach, for, do / while, do / until, and while. In this section, we will discuss each type of loop, highlight their unique features, and highlight the best situations to use them.



About the Author



Adam Bertram is a seasoned IT professional and internet business expert with 20 years of experience. Entrepreneur, IT Influencer, Microsoft MVP, Blogger, Training Manager, and Content Marketing Author, working with many IT companies. Adam also founded the popular TechSnips platform for developing the skills of IT professionals (techsnips.io).



More details about the book can be found on the website of the publishing house

» Table of contents

» Excerpt



For Habitors, a 25% discount on coupon - PowerShell



Upon payment for the paper version of the book, an e-book is sent to the e-mail.



All Articles