Book Image

Windows Server Automation with PowerShell Cookbook - Fourth Edition

By : Thomas Lee
Book Image

Windows Server Automation with PowerShell Cookbook - Fourth Edition

By: Thomas Lee

Overview of this book

With a foreword from PowerShell creator Jeffrey Snover, this heavily updated edition is designed to help you learn how to use PowerShell 7.1 effectively and manage the core roles, features, and services of Windows Server in an enterprise setting. All scripts are compatible with both Window Server 2022 and 2019. This latest edition equips you with over 100 recipes you'll need in day-to-day work, covering a wide range of fundamental and more advanced use cases. We look at how to install and configure PowerShell 7.1, along with useful new features and optimizations, and how the PowerShell compatibility solution bridges the gap to older versions of PowerShell. Topics include using PowerShell to manage networking and DHCP in Windows Server, objects in Active Directory, Hyper-V, and Azure. Debugging is crucial, so the book shows you how to use some powerful tools to diagnose and resolve issues with Windows Server.
Table of Contents (18 chapters)
16
Other Books You May Enjoy
17
Index

Exploring new operators

Operators are symbols or combinations of keystrokes that PowerShell recognizes and assigns some meaning to. PowerShell uses the + operator to mean addition, either arithmetic addition or string addition/concatenation. Most of the PowerShell operators were defined with Windows PowerShell V1.

PowerShell 7 now implements some new operators, including the following:

  • Pipeline chain operators: || and &&
  • Null-coalescing operator: ??
  • Null-coalescing assignment operator: ??=
  • Experimental null conditional member access operators: ?. and ?[]
  • Background processing operator: &
  • Ternary operator: ? <if-true> : <if-false>

You see examples of these operators in this recipe.

Getting ready

This recipe uses SRV1, a Windows Server 2020 host. You have installed and configured PowerShell 7 and VS Code. You run this, and all remaining recipes in this book, in either a PowerShell 7 console or VS Code.

How to do it...

  1. Using PowerShell 7 to check results traditionally
    Write-Output 'Something that succeeds'
    if ($?) {Write-Output 'It worked'}
    
  2. Checking results with the pipeline operator &&
    Write-Output 'Something that succeeds' && Write-Output 'It worked'
    
  3. Using the pipeline chain operator ||
    Write-Output 'Something that succeeds' || 
      Write-Output 'You do not see this message'
    
  4. Defining a simple function
    function Install-CascadiaPLFont{
      Write-Host 'Installing Cascadia PL font...'
    }
    
  5. Using the || operator
    $OldErrorAction        = $ErrorActionPreference
    $ErrorActionPreference = 'SilentlyContinue'
    Get-ChildItem -Path C:\FOO\CASCADIAPL.TTF || 
       Install-CascadiaPLFont
    $ErrorActionPreference = $OldErrorAction
    
  6. Creating a function to test null handling
    Function Test-NCO {
      if ($args -eq '42') {
        Return 'Test-NCO returned a result'
      }
    }
    
  7. Testing null results traditionally
    $Result1 = Test-NCO    # no parameter
    if ($null -eq $Result1) {
        'Function returned no value'
    } else {
        $Result1
    }
    $Result2 = Test-NCO 42  # using a parameter
    if ($null -eq $Result2) { 
        'Function returned no value'
    } else {
        $Result2
    }
    
  8. Testing using the null-coalescing operator ??
    $Result3 =  Test-NCO
    $Result3 ?? 'Function returned no value'
    $Result4 =  Test-NCO 42
    $Result4 ?? 'This is not output, but result is'
    
  9. Demonstrating the null conditional assignment operator
    $Result5 = Test-NCO
    $Result5 ?? 'Result is null'
    $Result5 ??= Test-NCO 42
    $Result5
    
  10. Running a method on a null object traditionally
    $BitService.Stop()
    
  11. Using the null conditional operator for a method
    ${BitService}?.Stop()
    
  12. Testing null property name access
    $x = $null
    ${x}?.Propname
    $x = @{Propname=42}
    ${x}?.Propname
    
  13. Testing array member access of a null object
    $y = $null
    ${y}?[0]
    $y = 1,2,3
    ${y}?[0]
    
  14. Using the background processing operator &
    Get-CimClass -ClassName Win32_Bios &
    
  15. Waiting for the job to complete
    $JobId = (Get-Job | Select-Object -Last 1).Id
    Wait-Job -id $JobId
    
  16. Viewing the output
    $Results = Receive-Job -Id $JobId
    $Results | Format-Table
    
  17. Creating an object without using the ternary operator
    $A = 42; $B = (42,4242) | Get-Random
    $RandomTest = ($true, $false) | Get-Random
    if ($A -eq $B) {
      $Property1 = $true
    } else {
      $Property1 = $false
    }
    if ($RandomTest) {
      $Property2 = "Hello"
    } else {
      $Property2 = "Goodbye"
    }
    [PSCustomObject]@{
      "Property1" = $Property1
      "Property2" = $Property2
    }
    
  18. Creating an object using the ternary operator
    [PSCustomObject]@{
        "Property1" = (($A -eq $B) ? $true : $false)
        "Property2" = (($RandomTest) ? "Hello" : "Goodbye")    
    }
    

How it works...

In step 1, you write output, which succeeds. Then you test the value of $? to determine whether that previous step did, in fact, succeed. The output is as follows:

Figure 2.1: Checking results traditionally

In step 2, you use the && operator to check that a preceding command finished without an error. The output looks like this:

Figure 2.2: Checking results with the pipeline operator

The pipeline chain operator, ||, tells PowerShell to run the commands after the operator if the preceding command fails (in effect, the opposite to &&). In step 3, you see the operator in use, with output like this:

Figure 2.3: Using the pipeline chain operator

In step 4, you define a function. Defining the function produces no output. This function writes output to simulate the installation of the Cascadia Code PL font.

In step 5, you check to see whether the TTF file exists, and if not, you call the Install-CascadiaPLFont function to simulate installing the font. By piping the output from Get-ChildItem to Out-Null, you avoid the actual output from Get-ChildItem, and if the file does not exist, you call the Install-CascadiaPLFont function. The output of this snippet looks like this:

Figure 2.4: Using the || operator and installing the Cascadia font

To illustrate the handling of null results from a function, in step 6, you create a function that either returns nothing (if you call the function with no parameters) or a string value (if you call it specifying a parameter). This function illustrates how you can handle a function that returns null. This step produces no output.

In step 7, you illustrate the traditional handling of a function that returns null. You call the function, first without a parameter, that returns no result and then with a value that does return a value. You then test to see whether the function returned an actual value in each case, which looks like this:

Figure 2.5: Testing null results traditionally

When you use the null-coalescing operator (??) between two operands, the operator returns the value of its left-hand operand if it isn't null; otherwise, it evaluates the right-hand operand and returns the results. In step 8, you call the Test-NCO function and check whether the function returns a value, which looks like this:

Figure 2.6: Testing using the null-coalescing operator

You use the null conditional assignment operator, ??=, to assign a value to a variable if that variable is currently null, as you can see in step 9, the output from which looks like this:

Figure 2.7: Using the null conditional assignment operator

One common issue often seen in the various PowerShell support forums arises when you attempt to invoke a method on an object that is null. You might have used an expression or a command to attempt to return a value (for example, all AD users in the Marin County office) and that returns a null values. In step 10, you attempt to invoke the Stop() method on the $BitService object. Since you have not assigned a value to $BitService, you see the result (an error, You cannot call a method on a null-valued expression). The traditional method of displaying errors looks like this:

Figure 2.8: Running a method on a null object traditionally

By using the null conditional operator, you can run the Stop() method if the $BitService variable is non-null, but skip calling the method if the variable is null. In effect, what you are doing in step 11 is calling the Stop() method if the variable is non-null, and doing nothing otherwise. Because the variable does not have a value, this step does nothing (and produces no output).

When a variable is null, whether due to an error in your scripts or because a command returns a null instead of an actual value, accessing property names can also cause errors. The output of step 12 looks like this:

Figure 2.9: Testing null property name access

You can also encounter issues with null objects when you attempt to access an array member of an object that may or may not exist. In step 13, you attempt to access an array member of an array that does not exist, followed by one that does exist. The output from this step looks like this:

Figure 2.10: Testing array member access of a null object

In step 14, you investigate the use of the background processing operator, &. The idea is that you append this character to the end of a command or script, and PowerShell runs that code in the background. The output from this step looks like this:

Figure 2.11: Using the background processing operator

In step 15, you wait for the job you created in step 14 to complete, which looks like this:

Figure 2.12: Waiting for the job to complete

After the job has completed, in step 16, you receive and display the job's output, which looks like this:

Figure 2.13: Displaying the job's output

In step 17, you create an object using a more traditional approach. This step creates a random value for two properties. Then, you create an object using the values of these two properties. The output from this step looks like this:

Figure 2.14: Creating an object without using the ternary operator

In step 18, you use the new PowerShell 7 ternary operator. This operator tests a condition and runs different code depending on whether the result is true or false. This is very similar to what you saw in step 17, but in a lot fewer lines of code. The output of this step looks like this:

Figure 2.15: Creating an object using the ternary operator

There's more...

In step 4, the function you create simulates the installation of a font if the font does not exist. The font file, CASCADIAPL.TTF, is a TrueType font file for the Cascadia Code Powerline font. This font is the Cascadia Code font you installed in Chapter 1, Installing and Configuring PowerShell 7.1, with the addition of symbols for Powerline. For more details on this font, see https://www.hanselman.com/blog/PatchingTheNewCascadiaCodeToIncludePowerlineGlyphsAndOtherNerdFontsForTheWindowsTerminal.aspx.

In step 5, you simulate the installation of the font if it does not already exist. When you check to test whether the TTF file currently exists, the default setting for $ErrorActionPreference (Continue) means you see an error message if the file does not exist. By default, when Get-ChildItem checks to see whether the file exists, it generates an error message if the file does not exist. One approach to avoiding this error message is to set the value of $ErrorActionPreference to SilentlyContinue and, after ensuring that the font file now exists, set it back to the default value. The syntax is a bit convoluted unless you are familiar with it; this may be another case where not using these operators, and using the Windows PowerShell approach instead, might make the script easier to read and understand.

In steps 11 and 12, you attempt to access a property from an object. The assumption here is that you only want to invoke the method or access a property value if the object exists and you do not care otherwise. Thus, if $BitService has a value, you call the Stop() method, otherwise the code carries on without the script generating errors. This approach is great from the command line, but in production scripts, the approach could mask other underlying issues. As with all PowerShell features, you have to use null handling with due care and attention.

With step 14, you tell PowerShell to run a command as a background job by appending the & character to the command. Using this operator is a more straightforward way to invoke the command as a job than by calling Invoke-Command and specifying the command using the -ScriptBlock or -Script parameters.

In steps 15 and 16, you use Get-Job, Wait-Job, and Receive-Job to wait for the last job run and get the output. One downside to not using Start-Job to create a background job is that you cannot specify a job name. That means using the technique shown in step 15 to obtain the job and job results for the job created in step 14. Using that technique is thus more useful from the command line than in a production script.

In step 17, you create an object using older Windows PowerShell syntax, whereas, in step 18, you use the ternary operator. As with other operators, use this with care, and if you use these operators in production code, make sure you document what you are doing.

In this recipe, you have seen the new operators added to PowerShell 7. Most of them provide a shortcut way to perform some operation or other, particularly at the command line.