PowerShell 101: Control Flow and Conditional Logic
When automating tasks across multiple servers, writing efficient code to handle repetitive actions can save significant time and reduce the potential for errors. In PowerShell, scaffolding code for handling tasks, including server checks and file management, ensures you can manage multiple servers seamlessly.
This tutorial explores key strategies for building and organizing PowerShell scripts that allow you to efficiently perform tasks across multiple servers using arrays, conditional logic, and robust error handling.
Scaffolding Code for Multiple Tasks
Scaffolding out code is important when dealing with multiple tasks, such as checking servers and reading the same file on them. This process means writing the code that acts on a single item first.
In this command, `Get-Content` is used, along with the UNC path for a single server and the file name. This command won’t work as is, but it forms the core of what we need.
## Step 1: Scaffold the command you need to run against each server Get-Content -Path "\\servername\c$\App_configuration.txt"
Notice all you see are comments in this demo script with regions. Regions allow you to collapse and expand code sections to make the code easier to read and organize. You can expand and collapse regions since VS Code recognizes regions from the PowerShell extension.
Storing and Referencing Servers in an Array
Since we’ll be processing many servers, an array can store all of them. When dealing with similar items, using an array is a good practice.
## Since there are multiple servers involved, defining them in an array is always the best way $servers = @('localhost','SRV2','SRV3','SRV4','SRV5')
You can now reference each server in the array.
## Now you can reference each element in the array instead of referencing different server name variables. ## This will come in handy when we loop later 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"
Checking Server Connectivity and Using Conditional Logic
With rough code to read the file, the next requirement is to check if the server is online. The `Test-Connection` cmdlet performs this task by pinging the server.
Using the `Quiet` parameter makes `Test-Connection` return a Boolean True or False if PowerShell can ping the server. The `Count` parameter limits the attempts to one.
Test the code for each “state” — in this case, pingable or not.
## Come up with the code to handle the next requirement Test-Connection -ComputerName 127.0.0.1 -Quiet -Count 1 Test-Connection -ComputerName 111.111.111.111 -Quiet -Count 1
Now that both requirements are met, it’s time to understand conditional logic.
Start with equality comparisons using the `-eq` operator, which compares a reference value on the left with a comparison value on the right.
## Testing equality comparisons 1 –eq 1
1 equals 1, right? PowerShell confirms this by returning `$true`.
To check if a value is NOT equal to something, use the `-ne` operator.
## Testing the inequality comparisons 1 -ne 2
Next, add the `if` statement, a common construct in scripting.
This example returns a string if the defined condition is `$true`. PowerShell executes the code ONLY if the condition is true.
## Check if 1 equals 1 and output a message if true if (1 -eq 1) { 'Duh. 1 equals 1' }
Optionally, support the other condition and take action if it occurs.
## Check if 1 equals 2 and output the appropriate message if (1 -eq 2) { 'True' } else { 'Yep, not true' }
Applying Conditional Logic to Server Checks
Apply this to our current situation. Since `Test-Connection` already returns a `$true` or `$false` value using the `Quiet` parameter, use that as a condition in the if statement.
This code attempts to ping the first element in the `$servers` array.
If it returns `$true`, it runs the code to get the configuration file’s contents. If not, it returns a message indicating otherwise.
## Check if the server is online and read its configuration file, ## otherwise, report it's offline if (Test-Connection -ComputerName $servers[0] -Quiet -Count 1) { Get-Content -Path "\\$($servers[0])\c$\App_configuration.txt" } else { Write-Host "The server $($servers[0]) is offline!" }
Even better, use the `Write-Error` cmdlet to return an error object, highlighted in red, making it obvious what happened.
## Check if the second server is online and read its configuration file, ## otherwise, log an error if (Test-Connection -ComputerName $servers[1] -Quiet -Count 1) { Get-Content -Path "\\$($servers[1])\c$\App_configuration.txt" } else { Write-Error "The server $($servers[1]) is offline!" }
The `-not` operator can create the opposite logic by negating the result.
## # Check if the second server is offline using -not and log an error, ## otherwise, read its configuration file if (-not (Test-Connection -ComputerName $servers[1] -Quiet -Count 1)) { Write-Error "The server $($servers[1]) is offline!" } else { Get-Content -Path "\\$($servers[1])\c$\App_configuration.txt" }
Handling Multiple States
Consider another state: the server could be online, but the file might not exist. This approach is iterative development — creating code as needed rather than trying to anticipate all scenarios in advance.
To account for this third state, use the `-and` operator. This operator tells PowerShell to run `Test-Connection`.
If the server is online, it checks for file existence with the `Test-Path` cmdlet, which returns `$true` or `$false`.
## Check if the server is online -and if the configuration file exists, ## otherwise, log an error if ((Test-Connection -ComputerName $servers[0] -Quiet -Count 1) -and (Test-Path "\\$($servers[0])\c$\App_configuration.txt")) { Get-Content -Path "\\$($servers[0])\c$\App_configuration.txt" } else { Write-Error "The server $($servers[0]) is offline or the app configuration file doesn't exist!" }
Perhaps you wish to exclude one of the servers from the array temporarily. If so, create code to exclude it instead of removing it, which can be easily removed later using an `elseif` construct.
## Exclude the specified server, otherwise, check if the server is online ## and the configuration file exists $excludeThisServer = 'localhost' if ($servers[0] -eq $excludeThisServer) { Write-Host "Excluding the $($servers[0]) server temporarily." } elseif ((Test-Connection -ComputerName $servers[0] -Quiet -Count 1) -and (Test-Path "\\$($servers[0])\c$\App_configuration.txt")) { Get-Content -Path "\\$($servers[0])\c$\App_configuration.txt" } else { Write-Error "The server $($servers[0]) is offline or the app configuration file doesn't exist!" }
Streamlining Conditional Logic with `switch` Statements
You can chain multiple `elseif` constructs together, but a switch statement is typically better if there are more than 1-2.
The `switch` statement defines a single condition and cleanly checks for various output values for that condition. Each condition has a set of curly braces, and a `default` construct matches if none of the other conditions are met.
## The switch Statement ## Structure switch (expression) { outputvalue { # Do something with code here. } outputvalue { # Do something with code here. } default { # Stuff to do if no matches were found } }
The `break` keyword stops the switch when a match is found; otherwise, it processes every item even after a match is made.
Notice how both returned information. The `break` keyword stops that.
$currentServer = $servers[0] switch ($currentServer) { 'localhost' { Write-Host "The current server value is $_" break } 'SRV1' { Write-Host "The current server value is $_" break } 'SRV2' { Write-Host "The current server value is $_" break } default { Write-Host "No matches found for the current server $_" } }
Also, notice the `$_` variable, which represents the output value of the expression inside the switch statement. Once the switch statement finishes executing, `$_` is gone.
Understanding “states”, conditions and controlling code flow is critical in PowerShell and a task you’ll frequently encounter.
switch (1) { 1 { Write-Host "The number is $_" } 2 { Write-Host "The number is $_" } 1 { Write-Host "The number is $_" } default { Write-Host "No number matches for $_" } }
Conclusion
Following this approach, you can streamline complex tasks like checking server connectivity, reading configuration files, and handling exceptions. All these are done in a way that’s both scalable and efficient.
Whether dealing with a handful of servers or a more extensive infrastructure, using arrays, conditional logic, and PowerShell’s built-in cmdlets helps create resilient and maintainable scripts.
With these techniques, you can now handle various server states effectively and respond to errors promptly, ensuring smooth operations in your IT environment.