Quantcast
Channel: Scripting Blog
Viewing all 2129 articles
Browse latest View live

PowerTip: Use PowerShell to Find Start Apps

$
0
0

Summary: Learn how to use Windows PowerShell to easily find start apps.

Hey, Scripting Guy! Question How can I use Windows PowerShell to find a particular app amid the clutter of small app icons on my
          Windows 8.1 device?

Hey, Scripting Guy! Answer Use the Get-StartApps cmdlet and filter on the name—
          for example, to find an application that begins with the letter G:

Get-StartApps | where name -like "g*"


Using PowerShell to Migrate DHCP Servers: Part 1

$
0
0

Summary: Microsoft PFE, Ian Farr, talks about using Windows PowerShell to migrate DHCP servers.

Microsoft Scripting Guy, Ed Wilson, is here. Today we welcome back guest blogger, Ian Farr.

Image of slogan

Episode 1:  A New Hope

In a galaxy very, very near to here, when you are not cleaning-up droids, you administer a DHCP infrastructure in Windows Server 2003 with two servers in a split scope configuration. Because extended support for Windows Server 2003 ends on the July 14, 2015, you’ve read about the new features in Windows Server 2012, and you want some DHCP failover goodness! (For more information, see Step-by-Step: Configure DHCP for Failover.)

So…how do you seamlessly migrate your current DHCP infrastructure, combining the split scopes into a load-balanced failover relationship? And how, on a desert-planet-in-a-binary-star-system, can the PowerShell help out?

I’ll answer with an example migration process:

  1. Transfer the configuration from the DHCP servers running Windows 2003 to two newly-prepared DHCP servers running Windows Server 2012.
  2. Compare the DHCP server configuration on the new servers.
  3. Compare the DHCP scope settings on the DHCP servers running Windows Server 2012.
  4. Configure a load-balanced failover relationship between the DHCP servers running Windows Server 2012.
  5. Activate the two servers running Windows Server 2012 in Active Directory.

Image of flow chart

Figure 1  Migration overview

Windows PowerShell is what gives scripters their power. You must learn the ways of the PowerShell if you are to implement a DHCP infrastructure in Windows Server 2012. Let’s begin our scripter training by looking at the first two migration steps…

Image of capsule

Step 1: Migration tools

The migration tools you are looking for are the Windows PowerShell-based Windows Server Migration Tools. They are awesome! Need I say more? Well, yes…

On each of the servers running Windows Server 2003, the Windows Server Migration Tools capture the DHCP configuration as separate images. Each image is then imported to the corresponding DHCP server running Windows Server 2012. No need to worry about transferring from a 32-bit host to a 64-bit host. Apart from some initial configuration, it’s as simple as that!

Here’s how you list the features available for export on the source servers:

Get-SmigServerFeature

For example:

Image of command output

Here’s a sample export command:

Export-SmigServerSetting -FeatureID "DHCP" -Path "c:\smig_export_dhcp" -Verbose

And here’s a sample import command for the target servers:

Import-SmigServerSetting -FeatureID "DHCP" -Path "c:\smig_import_dhcp" -Verbose -Force

Note  Add the DHCP server role to the servers running Windows Server 2012 before you import the migration image to ensure that the management tools are present.

For more information about the initial configuration of the Windows Server Migration Tools, see Install, Use, and Remove Windows Server Migration Tools.

Step 2: DHCP server comparison script

Are you a little short on DHCP server comparison script? On with the scripter training…

Before you add DHCP host servers to a failover relationship, it’s good practice to ensure that the server settings are consistent. I wrote a script to produce an Excel report that highlights discrepancies between two DHCP servers. You can download it here: Compare DHCP Server Settings with PowerShell DHCP Cmdlets.

The script performs a number of checks, and the results of each test are written to a separate worksheet.

  • Matching objects are presented side by side in yellow and turquoise for ease of comparison

Image of worksheet

  • For matching objects, specific property discrepancies have the property name highlighted in red.
  • Objects that are unique to one DHCP server are highlighted in green (for DHCP server 1) or in orange (for DHCP server 2). These entries appear at the end of the relevant worksheet.

How do we generate the report content? Use Windows PowerShell, young scriptwalker!

DHCP server module for Windows PowerShell

There are 103 cmdlets in the DHCP server module for Windows PowerShell, which was introduced with Windows Server 2012 and Windows PowerShell 3.0. The script uses 10 of these cmdlets to jump to hyperspace to compare server settings:

  • Get-DhcpServerAuditLog
  • Get-DhcpServerDatabase
  • Get-DhcpServerSetting
  • Get-DhcpServerVersion
  • Get-DhcpServerv4Class
  • Get-DhcpServerv4DnsSetting
  • Get-DhcpServerv4FilterList
  • Get-DhcpServerv4OptionDefinition
  • Get-DhcpServerv4OptionValue
  • Get-DhcpServerv4Policy

The Compare_DHCP_Server_Settings.ps1 script has a “control panel” (as shown in Figure 2) to determine which checks are included when the script is executed.

Image of control panel

Figure 2  Control panel used by script

Hmmm, if only my script control panel looked more like the one in Figure 3!

Image of circuit board

Figure 3  Dream control panel

Where was I? Oh, yes…

Scripter training. Each of the included checks is executed against the two DHCP server names that were passed as script parameters.

The checks are part of an array that is looped through $Checks. A Switch statement is used to evaluate each iterated check. Why? Some of the cmdlets return a single object; some cmdlets return multiple objects. A different function is used to perform the object comparison in each of these scenarios.

            ForEach ($Check in $Checks) {

    Switch ($Check) {

        "DhcpServerAuditLog" {$Single = $True}

        "DhcpServerv4Class" {$Single = $False; $ComparisonProperty = "Name"}

Where a cmdlet returns a single object, $Single is set to True. Where a cmdlet returns multiple objects, $Single is False, and a common comparison property for the resultant objects is defined (more about this later).

With the current check matched by Switch, $Single is tested to define the function to call. The test also determines what parameter values get passed to the function by a technique called splatting. If you’ve not seen splatting before, it’s used to pass parameter values as a single entity (use Get-Help to find more about splatting).

In the script, the splatted parameter values are defined in a hash table.

                 If ($Single) {

        #Splatted parameters

        $Parameters = @{

             DhcpCmdlet = "Get-$Check"

            DhcpServer1 = $DhcpServer1

            DhcpServer2 = $DhcpServer2

            SheetNumber = $i

            SheetName = $Check

        }   #End of $Parameters

        #Target function

        $Function = "Compare-SingleObject"

    }   #End of If ($Single)...

Where $Single is false, an additional property is added to the splatted parameters. This is the $ComparisonProperty mentioned earlier:

                 Else {

        #Splatted parameters

        $Parameters = @{

            DhcpCmdlet = "Get-$Check"

            DhcpServer1 = $DhcpServer1

            DhcpServer2 = $DhcpServer2

            SheetNumber = $i

            SheetName = $Check

            ComparisonProperty = $ComparisonProperty

         }   #End of $Parameters

        #Target function

        $Function = "Compare-MultipleObjects"

    }   #End of Else ($Single)...

With $Parameters and $Function defined, the comparison check is executed:

&$Function @Parameters

There are two things to note here. The first is the use of the call operator, &, to invoke the stored function. Without this, Compare-SingleObject or Compare-MultipleObjects are returned as strings. The second is that $Parameters becomes @Parameters. @ means splat!

The Compare-SingleObject function calls the current check, $DhcpCmdlet, against each DHCP server and stores the results in a variable:

   $Output1 = &$DhcpCmdlet -ComputerName $DhcpServer1

   $Output2 = &$DhcpCmdlet -ComputerName $DhcpServer2

A list of property names for the resultant object type is then retrieved:

$Properties = ($Output1[0] | Get-Member -Type Properties).Name 

Each property from each server is compared:

ForEach ($Property in $Properties) {

$Result = Compare-Object -ReferenceObject $Output1 -DifferenceObject $Output2 -IncludeEqual -Property $Property

If they don’t match, the property name in the Excel report is marked as red:

If ($Result.SideIndicator -ne "==") {

            $ExcelSheet.Cells.Item($j,1).Interior.ColorIndex = 3

       }   #End of If ($Result.SideIndicator -ne "==")

Compare-MultipleObjects

The Compare-MultipleObejcts function takes an extra parameter—a comparison property for the DHCP cmdlet executed. Here’s how the function works…

As with the Compare-SingleObject function, the check is executed against both servers, and a list of property names for the resultant objects are stored in a variable. This time the returned array of objects are compared with the aid of the comparison property. It is important that the selected $ComparisonProperty always have a value.

$Comparison = Compare-Object -ReferenceObject $Output1 -DifferenceObject $Output2 -IncludeEqual -Property $ComparisonProperty

Now, we need to deal with the fact that some objects might be on both servers, some objects on only the first DHCP server, and some objects on only the second DHCP server.

To check for objects present on both servers, test for the equals indicator, ==, from the comparison results:

If ($Comparison.SideIndicator -Contains "==") {

Where objects are present on both servers, write the details to a $Results variable, and continue to use the comparison property as the common reference:

$Results = $Comparison | Where-Object {$_.SideIndicator -eq "=="} | Select $ComparisonProperty

Loop through each mutual object and rerun the current check again against a specific iteration of $Result.$ComparisonProperty on both servers:

ForEach ($Result in $Results) {

                $Value1 = &$DhcpCmdlet $Result.$ComparisonProperty -ComputerName $DhcpServer1

                $Value2 = &$DhcpCmdlet $Result.$ComparisonProperty -ComputerName $DhcpServer2

Then, as with Compare-SingleObject, loop through each of the object’s properties and compare the value from each server. Highlight discrepancies in red, for example:

Image of worksheet

Next, check for objects that are present only on the first DHCP server. Test for the left side indicator (<=):

If ($Comparison.SideIndicator -Contains "<=") {

Run the current DHCP cmdlet against the first DHCP server:

            $Value1 = &$DhcpCmdlet $Result.$ComparisonProperty -ComputerName $DhcpServer1

Loop through the properties and write them to the report. Highlight the Excel cells in green:

            $ExcelSheet.Cells.Item($k,2).Interior.ColorIndex = 43

Image of worksheet

Finally, the check for objects that are present on only the second DHCP server has almost identical script to the first server check, except you are testing for the right-side indicator:

            If ($Comparison.SideIndicator -Contains "=>") {

Loop through the properties, and write them to the report. Highlight the Excel cells in orange:

$ExcelSheet.Cells.Item($j,3).Interior.ColorIndex = 44

Image of worksheet 

Upon completion, you’ll have an Excel spreadsheet in the same folder as the script, which is saved in the following format:

<YearMonthDayHourMinuteSecond>_<DhcpServer1>_<DhcpServer2>_DHCP_Server_Comparison.xls

Analyse the report and perform appropriate configuration changes to bring the servers in line. Pay particular attention to classes, option definitions, and option values. And please let me know if you spot a two meter-wide exhaust port!

Well, that’s it for Episode 1. Sadly, there are no huge explosions or fancy award ceremonies to finish with. However, our DHCP data from our server running Windows Server 2003 is now seamlessly migrated to our DHCP servers running Windows Server 2012, and we’ve compared and corrected any errant settings. In Episode 2, we’ll complete our scripter training and the migration process.

May the PowerShell be with you!

~Ian

Thanks, Ian! Our DHCP migration lesson will continue tomorrow with Ian returning to conclude his series.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell to Display DHCP Addresses

$
0
0

Summary: Use Windows PowerShell to display DHCP addresses in Windows 8.

Hey, Scripting Guy! Question How can I use Windows PowerShell on my computer running Windows 8 to find IPV4 addresses
          that are supplied by DHCP?

Hey, Scripting Guy! Answer Use the Get-NetIPAddress function, and specify that the prefix origin is DHCP.
          You can also specify that the address is IPV4, and use other options to help identify what you seek,
          for example:

Get-NetIPAddress -AddressState preferred -AddressFamily ipv4 -Type unicast -PrefixOrigin dhcp

Using PowerShell to Migrate DHCP Servers: Part 2

$
0
0

Summary: Microsoft PFE, Ian Farr, continues his quest to use Windows PowerShell to migrate DHCP servers.

Microsoft Scripting Guy, Ed Wilson, is here. Ian Farr is back today. You should read the first part of this series before getting into this one: Using PowerShell to Migrate DHCP Servers: Part  1.

Image of slogan

Episode 2: Return of the Scripter

A scripter’s strength flows from the PowerShell. In Episode 1: A New Hope, we began our scripter training. We imported the configuration from a split-scope DHCP infrastructure on computers running Windows Server 2003 to two DHCP servers running Windows Server 2012, with the aid of the Windows PowerShell-based Windows Server Migration Tools.

We also ran a script to compare the servers running Windows Server 2012, and a report identified any configuration discrepancies. Where appropriate, these were manually corrected. So we completed Steps 1 and 2 of our example migration process:

  1. Transfer the configuration from the DHCP servers running Windows 2003 to two newly-prepared DHCP servers running Windows Server 2012.
  2. Compare the DHCP server configuration on the new servers.
  3. Compare the DHCP scope settings on the DHCP servers running Windows Server 2012.
  4. Configure a load-balanced failover relationship between the DHCP servers running Windows Server 2012.
  5. Activate the two servers running Windows Server 2012 in Active Directory.

Let’s pick-up the training at Step 3…

Step 3: Another comparison script

You’d expect the scope settings in a DHCP split-scope setup to be consistent. Experience tells me that assumptions (and light sabers) are dangerous things, so I wrote another comparison script… you might even call it a sister script!

The PowerShell runs strong in my family. You can download the script here: Compare DHCP Scope Settings with PowerShell DHCP Cmdlets.

This time, the Excel output is different. Rather than a worksheet per check, all the scope related checks are found on the second worksheet. Again, matching objects are presented side-by-side in yellow and turquoise for ease of comparison. Again, specific property discrepancies will have the property name highlighted in red.

Image of worksheet

In this example, as you’d expect with a split-scope setup, the exclusion ranges are different. There’s also a discrepancy with the scope start range. We’ll look at how to correct this in Step 4, where we set up the failover relationship.

Windows PowerShell is similar to the server settings script. There’s a Compare-Multiple objects function and a “control panel” to configure the checks executed by the DHCP cmdlets.

Note  Leave all control panel checks in in place to support migration Step 4.

We start by using the Get-DhcpServerv4Scope cmdlet to obtain a list of scope IDs for both servers:

$DhcpServer1Scopes = (Get-DhcpServerv4Scope -ComputerName $DhcpServer1).ScopeID.IPAddressToString

$DhcpServer2Scopes = (Get-DhcpServerv4Scope -ComputerName $DhcpServer2).ScopeID.IPAddressToString

The checks and the scope IDs are then fed into the Execute-DhcpScopeChecks function:

$DhcpServer1CustomScopeInfo = Execute-DhcpScopeChecks $Checks $DhcpServer1 $DhcpServer1Scopes

$DhcpServer2CustomScopeInfo = Execute-DhcpScopeChecks $Checks $DhcpServer2 $DhcpServer2Scopes

This loops through each scope ID, creating a custom Window PowerShell object for the current scope:

$DhcpScopeInfo = New-Object -TypeName PSObject -Property @{ScopeID = $DhcpServerScope}

For the current scope, each supplied check is executed:

ForEach ($Check in $Checks) {    

            #Create a variable for the check (cmdlet) syntax

            $Command = "Get-$Check"

            #Execute the check

            $CheckResult = &$Command -ScopeId $DHCPServerScope -ComputerName $DhcpServer

A Switch statement then filters on the current check and uses the results to build an ordered hash table. This is added to our $DhcpScopeInfo object. For example, here’s the script for populating the DhcpServerv4Scope hash table:

             Switch ($Check) {               

                ##Process the DhcpServerv4Scope check

                "DhcpServerv4Scope" {

                    #Define properties to be added to the custom scope object

                    $Properties = [Ordered]@{

                        Name = $CheckResult.Name

                        SubnetMask = $CheckResult.SubnetMask.IPAddressToString

                        ScopeStartRange = $CheckResult.StartRange.IPAddressToString

                        ScopeEndRange = $CheckResult.EndRange.IPAddressToString

                        Description = $CheckResult.Description

                        State = $CheckResult.State

                        Type = $CheckResult.Type

                            }   #End of $Properties...                                

                    #Add the new property set to the custom scope object

                    $DhcpScopeInfo | Add-Member -NotePropertyMembers $Properties

                }   #End of "DhcpServerv4Scope"...

At the end of the $Checks loop, the object that contains the results from all the checks for the current scope is added to an array:

[Array]$TotalScopes += $DhcpScopeInfo

After all the scopes are processed, $TotalScopes is returned to the script scope and stored in $DhcpServer1CustomScopeInfo for the first DHCP server, or in $DhcpServer2CustomScopeInfo for the second DHCP server.

With $DhcpServer1CustomScopeInfo and $DhcpServer1CustomScopeInfo populated, we call Compare-MultipleObjects (see Episode 1 for more information).

$Discrepancies = Compare-MultipleObjects $DhcpServer1 $DhcpServer2 $DhcpServer1CustomScopeInfo $DhcpServer2CustomScopeInfo

We now have a comparison report for the two sets of scopes. What to do with the information? Read on…

Step 4: Configure DHCP Failover

You cannot escape your destiny. You must configure a DHCP failover relationship. In this section, we’ll discuss using the Excel report and a final script to set up a load-balanced DHCP server failover relationship. You can execute the discussed commands separately; however, using the script gives you error checking, script logging, progress bars, and much less typing!

Our only hope…

You can download the script here: Configure DHCP Failover Load Balanced Relationship with PowerShell DHCP Cmdlets.

There’s a little pre-execution work…

The Excel output from the scope comparison script should be assessed. If no discrepancies are reported, you’re good to run the script. Ignoring the exclusion ranges, if there’s red, you’ll need to review and perhaps update settings.

The script uses the Excel report and the values in column 2 of sheet 2 (the yellow column) as the “good” desired scope configuration, so you’ll need to make corrections here. Make sure that you stick to the original formatting. This is the most time consuming task in the whole migration process.

Also, look out for scope types that are configured as BootP or Both. You cannot add these to a failover relationship, and any attempt to do so will fail. If they are left in the spreadsheet, the script will convert them to type DHCP, which may be undesirable. The following is an example command to assess the number of BootP and Both scopes:

Get-DHCPServerv4Scope -ComputerName HALODC01 | Where-Object {$_.Type -ne "DhcP"}

Important  If there are BootP or Both scopes that you want to keep, remove these from the spreadsheet, and make sure that three rows are left between the remaining scopes.

One last point…

To avoid the possibility of DNS records for DHCP reservations being removed, the DHCP servers running Windows Server 2012 should remain unauthorized in Active Directory until the last stage of the migration process.

Right! To the script! Here’s the flow of Windows PowerShell:

  1. Create log file
  2. Export DHCP configuration
  3. Convert XLS to CSV
  4. Convert CSV to custom Windows PowerShell objects
  5. Remove scopes
  6. Check remaining scopes
  7. Create new scopes
  8. Create failover relationship
  9. Add scopes to failover relationship
  10. Close log file

Now, the interesting bits…

Export DHCP configuration

Here, we backup each server’s configuration. You’ll notice the export is written to an XML file:

Export-DhcpServer -ComputerName $DhcpServer1 -File "$($ReportBase)_$($DhcpServer1)_DHCP_Export.xml"

Upon completion, a backup file for each server will be in the script’s execution directory.

Convert XLS to CSV

In the Convert-XlsToCsv function, the Excel com object edits and saves the .xls file so we’re left with the contents of column 2, sheet 2.

Of interest here is the $Script:ConfigCSV variable. The $Script: prefix means the data stored in the ConfigCSV variable will be available in the script scope, which, in this instance, is the calling the parent scope.

Note  These scopes have nothing to do with DHCP scopes!

Making the ConfigCSV variable available in the script scope could also be achieved by using New-Variable cmdlet with the Scope parameter. A value of 1 or Script could be used in our failover configuration script. If there was a grandfather scope or a great-grandfather scope, respective values of 2 or 3 could be used to make the variable available at those levels. Powerful stuff! For more information, take a look at about_Scopes with Get-Help.

Convert CSV to custom Windows PowerShell objects

The newly converted CSV file is imported and header information is added with the Header parameter of Import-CSV. This prevents the first line of our data from being used as the header.

$CSV = Import-CSV $ConfigCSV -Header PROPERTY,VALUE

The CSV file line count is obtained with $TotalLineCount. This is used as part of the terminating condition of a Do/Until loop. A counter is also established by using $LineCount . The loop runs until the $LineCount variable is greater than the $TotalLineCount variable. An exact number of CSV lines is processed in a block, which allows each scope and its configuration options to be added to a unique custom Windows PowerShell object. Each scope object is then added to an array of scope objects. Here’s what this section of script looks like:

    #Obtain the number of lines in the CSV file

    $TotalLineCount = $CSV.Count

    #Create a counter for the number of lines processed

    $LineCount = 1

        <#Create custom objects for each scope listed in the CSV file...

        Use Do / Until to process blocks of lines until $TotalLineCount value is reached#>

        Do {   

            #Create a custom object containing the details of each scope

            $Scope = [PSCustomObject]@{

                $CSV[$LineCount -1].PROPERTY = $CSV[$LineCount -1].VALUE           #ScopeID

                $CSV[($LineCount)].PROPERTY = $CSV[($LineCount)].VALUE             #Name

                $CSV[($LineCount + 1)].PROPERTY = $CSV[($LineCount + 1)].VALUE     #SubnetMask

                $CSV[($LineCount + 2)].PROPERTY = $CSV[($LineCount + 2)].VALUE     #ScopeStartRange

                $CSV[($LineCount + 3)].PROPERTY = $CSV[($LineCount + 3)].VALUE     #ScopeEndRange

                $CSV[($LineCount + 9)].PROPERTY = $CSV[($LineCount + 9)].VALUE     #OptionId

                $CSV[($LineCount + 11)].PROPERTY = $CSV[($LineCount + 11)].VALUE   #OptionValue

                $CSV[($LineCount + 12)].PROPERTY = $CSV[($LineCount + 12)].VALUE   #ReservationName

                $CSV[($LineCount + 13)].PROPERTY = $CSV[($LineCount + 13)].VALUE   #ReservationIPAddress

                $CSV[($LineCount + 14)].PROPERTY = $CSV[($LineCount + 14)].VALUE   #ReservationClientId

                $CSV[($LineCount + 15)].PROPERTY = $CSV[($LineCount + 15)].VALUE   #ReservationType

            }   #End of $Scope

            #Add the custom object to an array of custom objects

            [Array]$ScopeObjects += $Scope

            #Increment the line count to accommodate the predefined format of the CSV file

            $LineCount += 20

        }   #End of Do

        #Define the condition the loop runs until

        Until ($LineCount -gt $TotalLineCount)

The $LineCount variable is manipulated to target specific scope information in the $CSV array—that is, $CSV[($LineCount + 12)] equates to ReservationName values. Notice the Property and Value headers are used in the PSCustomObject to construct Key/Value pairs. Finally, $LineCount is incremented by 20 to move to the next block of scope information from the CSV. Not particularly elegant—but like a blaster, it gets the job done.

Note  Removal of lines from the scope sections of the .xls file breaks this logic.

Remove scopes

Next, we call the Remove-Scopes function for each server, passing the array of scopes and configuration options. Each custom scope object is processed as part of a ForEach loop:

Remove-DhcpServerv4Scope -ComputerName $DhcpServer1 -ScopeId $ScopeObject.ScopeID –Force

Check remaining scopes

Unlike the intelligence on the operational status of a new armoured space station, I’m thorough. So, I’ve included a function that checks for scopes left on the servers. The Get-RemainingScopes function runs the Get-DhcpServerv4Scope cmdlet and writes the results to the script log:

$RemainingScopes = Get-DhcpServerv4Scope -ComputerName $DhcpServer1 

Intermission and a short interlude…

DV: How’s your Wookie steak, my lord?

Big E: Chewy, my friend…

The old ones are the best!

Create new scopes

Ah, now for the Create-Scope function. This has a lot of parameters, one for each of the configurable Scope properties:

  • Scope ID
  • Name
  • Subnet Mask
  • Scope Start Range
  • Scope End Range
  • Option ID
  • Option Value
  • Reservation Name
  • Reservation IP Address
  • Reservation Client ID
  • Reservation Type

In the main body of the script, we loop through the array of custom scope objects, and for each object, we populate a set of parameters to splat into the Create-Scope function:

ForEach ($ScopeObject in $ScopeObjects) {

        $Properties = [Ordered]@{

            ScopeID = $ScopeObject.ScopeID

            Name = $ScopeObject.Name

            SubnetMask = $ScopeObject.SubnetMask

            ScopeStartRange = $ScopeObject.ScopeStartRange

            ScopeEndRange = $ScopeObject.ScopeEndRange

            OptionID = $ScopeObject.OptionID

            OptionValue = $ScopeObject.OptionValue

            ReservationName = $ScopeObject.ReservationName

            ReservationIPAddress = $ScopeObject.ReservationIPAddress

            ReservationClientID = $ScopeObject.ReservationClientID

            ReservationType = $ScopeObject.ReservationType

        }   #End of $Properties

$CreateScope = Create-Scope @Properties

Inside the Create-Scope function, we use Add-DhcpServerv4Scope to process the Name, SubnetMask, ScopeStartRange, and ScopeEndRange function parameters:

Add-DhcpServerv4Scope -ComputerName $DhcpServer1 -Name "$Name"

   -SubnetMask "$SubnetMask"

   -StartRange "$ScopeStartRange"

   -EndRange "$ScopeEndRange"

With the scope created, we can now configure any additional scope settings.

First, scope options…

Scope options are made up of an Option ID and an Option Value. The complete list of IDs for a scope is stored as a comma-separated list of values in the current custom scope object:

Image of command output

This string is easily split and tidied up:

            $OptionID = $OptionID -Replace " ", "" -Split ","

For example:

Image of list

The option values are also stored as a single string. This time we have to deal with the fact that some option values are a list of items also separated by a comma, and quotation marks wrap these values. See the highlighted example:

Image of command output

The Split operator accepts a Regular Expression. This particular expression ensures that we split on commas that are NOT found between quotation marks:

            $OptionValue = $OptionValue -Split ',(?=(?:[^"]|"[^"]*")*$)' -Replace "`"", "" 

We then remove the quotation marks with the Replace operator and end up with an array of strings, for example:

Image of command output

Each option ID is looped through and created with Set-DhcpServerv4OptionValue. The counter, $IdCounter , is used to ensure that we associate the correct value string with the correct ID. The option value sequence matches that of the option ID sequence—that is, the first ID corresponds to the first value and so on.

            ForEach ($ID in $OptionID) {

$CurrentOptionValue = ($OptionValue[$IdCounter] -Split ",").TrimStart()

       Set-DhcpServerv4OptionValue -ComputerName $DhcpServer1 -OptionId $ID -ScopeId $ScopeID
       -Value $CurrentOptionValue

The same process is used to create reservations with the aid of the Add-DhcpServerv4Reservation cmdlet. Each reservation is made up of four elements:

  • Reservation Name
  • Reservation IP Address
  • Reservation Client ID
  • Reservation Type

Add-DhcpServerv4Reservation -ComputerName $DhcpServer1 -ScopeId $ScopeID `

                         -Name $Reservation -IPAddress $ReservationIPAddress[$ResCounter] `

                         -ClientId $ReservationClientID[$ResCounter] -Type $ReservationType[$ResCounter]

Create failover relationship

With the scopes in place, we can create the failover relationship. The Create-FailoverRelationship function is called with the first scope from the scope objects array:

Create-FailoverRelationship -FirstScope $ScopeObjects[0].ScopeID

  -FailoverRelationship "$($DhcpServer1)-$($DhcpServer2)-Failover"

The function uses the Add-DhcpServerv4Failover cmdlet to create a load-balanced relationship with Auto State Transition enabled:

     Add-DhcpServerv4Failover -Name $FailoverRelationship -ScopeID $FirstScope -ComputerName $DhcpServer1 `

                     -PartnerServer $DhcpServer2 -AutoStateTransition $True `

                     -SharedSecret $SharedSecret

To create a hot standby relationship, add the ServerRole parameter and specify Active or Standby. In Windows PowerShell 4.0, the Set-DhcpServerv4Failover cmdlet has a Mode parameter to easily switch between HotStandby and LoadBalance failover configurations.

Add scopes to failover relationship

Now to add our scopes to the failover relationship with another function. We’ve already used the first scope from the array to create the relationship, but it needs the Skip parameter of Select-Object to execute:

$ScopeObjects = $ScopeObjects | Select-Object -Skip 1

Next, add each scope with the aid of a ForEach loop and the Add-DhcpServerv4FailoverScope cmdlet:

     ForEach ($ScopeObject in $ScopeObjects) {

        Add-DhcpServerv4FailoverScope -Name $FailoverRelationship -ComputerName $DhcpServer1
       -ScopeId $ScopeObject.ScopeID

And, that’s it for the last of the three scripts. Please remember to check this script’s log before moving onto Step 5 of the migration process. The log is found in the execution directory. You’ll need SMS Trace or CM Trace to view it properly. (For more information, see Log-ScriptEvent Function).

Step 5: Activate the DHCP shield generator

When you’ve investigated and corrected any red from the script log, you’re ready to activate the new DHCP platform in Active Directory. To begin, shuttle down to a forest moon, enter the secret entrance to a data center bunker, and then power down those DHCP servers running Windows Server 2003.

Note  Being able to quickly power on the servers running Windows Server 2003 provides a simple roll-back option.

Next, register the new DHCP servers in Active Directory. Here’s an example command:

            Add-DhcpServerInDc –DnsName FORCEDC01.contoso.com –IPAddress 10.100.10.50

Now, test, test, and test again. When you are happy, deactivate the DHCP servers running Windows Server 2003 in Active Directory and decommission them. Here, you will witness the final destruction of your Windows Server 2003 DHCP Split-Scope alliance…

Epilogue

Some might take issue with the episode numbering. Others may comment on a supposedly missing episode:

Photo

I’m hoping that my example automation of a tricky DHCP migration will quiet any grumbling. We’ve used Windows PowerShell to seamlessly update a split-scope platform on DHCP servers running Windows Server 2003 to a resilient, load-balanced DHCP service on servers running Windows Server 2012. As part of the process, DHCP server and scope settings were reviewed and aligned. For a moment, the galaxy was a better place. Now for fireworks and dubious dancing!

Oh…there’s also some bonus material: The new platform can be managed by the PowerShell too! Man, life is good.

Remember, the PowerShell will be with you, always.

~Ian

Thank you, Ian, for once again sharing your time and knowledge. This has been an excellent two-part series. Join us tomorrow for more cool Windows PowerShell stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

PowerTip: Use PowerShell to Count Hops to Server

$
0
0

Summary: Use Windows PowerShell to count hops to a server.

Hey, Scripting Guy! Question On my computer running running Windows 8.1, how can I use Windows PowerShell to verify
          the number of hops to my server?

Hey, Scripting Guy! Answer Use the Test-NetConnection function and the –TraceRoute parameter,
          specify the server name, choose the TracerOut property, and get the count:

(Test-NetConnection -TraceRoute -ComputerName "www.contoso.com").traceroute.count 

Apply Common PowerShell Concepts to Configuration Manager

$
0
0

Summary: Microsoft PFE, Heath Stewart, talks about applying common Windows PowerShell concepts to Configuration Manager.

Microsoft Scripting Guy, Ed Wilson, is here. Today we welcome guest blogger, Heath Lawson. Heath is a System Center premier field engineer based in Kansas City, Missouri. He often posts about System Center-related scripting and automation in his blog, HeLaw's Blog: System Center...Tying it all together!

Heath Lawson here, checking in for the first of three posts. My background is heavy in Configuration Manager, although I love all aspects of automation and scripting, so the opportunity to write guest posts here is amazing! This series is intended to help Configuration Manager folks jump into scripting and automation by helping them gain an understanding of how common Windows PowerShell concepts can apply to Configuration Manager.

There are three posts to this series, the first will cover how to interact with the Windows PowerShell support starting with Configuration Manager 2012 Service Pack 1, the second is an advanced look at using cmdlets to get work done, and the third is about taking care of business without using the native Windows PowerShell cmdlets. These posts will assume that you have a very basic understanding of Windows PowerShell; and if you don’t, you’re in the right spot because the Scripting Guys have some fantastic resources here to get you up to speed.

System Center Configuration Manager 2012 introduced Windows PowerShell support starting with Service Pack 1. Each cumulative update typically finds new or updated Windows PowerShell functionality, although the differences can be subtle. The most current version is System Center 2012 R2, so all of the examples here will be shown with the System Center 2012 R2 versions of the cmdlets. Understand that there may be differences if you’re not using System Center 2012 R2.

The goal of this post is to take a common action, and then create a new collection and set a few properties…all with Windows PowerShell. I like to show and tell when it comes to scripting, so we’ll jump into the lab and get started. We can easily do that that within the Configuration Manager console by clicking the upper-left corner and launching the Windows PowerShell session.

Image of menu

After we click the menu item that is highlighted, we’ll be presented with a Windows PowerShell session with the Configuration Manager module loaded:

Image of screen

What is P35?

Now, we have our Windows PowerShell session, but why is it referencing P35? Well, for the astute readers out there, you’ll notice in the top screenshot, that is my site code for Configuration Manager. The reason for this is that the Configuration Manager module registers itself as a Windows PowerShell provider and creates a Windows PowerShell drive.

It’s just a Windows PowerShell drive

A little background on the Configuration Manager PowerShell module…

First, all of the Windows PowerShell functionality in Configuration Manager is contained within the ConfigurationManager.psd1module, which is located in the <Install Path>\AdminConsole\Bin\i386 directory. Additionally, the module references the site code of where the console has connected. This is important to understand for automation scenarios because we have to fire up the console as the user running Windows PowerShell at least once to make sure we reference our site code via the provider.

Now that we have our Windows PowerShell session loaded and we understand that our functionality is going to come from the Configuration Manager module, let’s get started creating our collection. First, we need to understand which cmdlet to run to create a new collection. To find that, we’ll run a quick command to find all of the commands in our Configuration Manager module that have a name that contains Collection:

Get-Command -Module ConfigurationManager | Where-Object {$_.Name -like '*Collection*'}

Image of command output

A new Configuration Manager collection

Well, this seems great. In true Windows PowerShell verb-noun form, we can see that there is a New-CMDeviceCollection cmdlet available! Also, from this view, you’ll notice all of the Configuration Manager cmdlets are easily identifiable because the noun portion of the cmdlets is prefixed with CM.

One of the great things about Windows PowerShell is that it contains plenty of built-in documentation, and that documentation updates on a regular basis. One point here, the Configuration Manager module ships with older documentation. You can issue the Update-Help cmdlet to update your Configuration Manager documentation. With that out of the way, let’s get more information on the New-CMDeviceCollection cmdlet and see what we need to do to use it to create a collection for us.

Get-Help New-CMDeviceCollection

Image of command output

As we can see in this image, New-CMDeviceCollection gives us a few options, such as name and refresh type. Collections in Configuration Manager 2012 must be limited to another collection, so we know we’re going to have to specify the limiting collection and a collection name. As we can see in this image, it calls that out for us.

Optionally, for those who feel like experimenting, we can use the Tab key to auto-complete for us in Windows PowerShell. One of my common short cuts is to simply type the cmdlet and then “-“. From there, press the Tab key and you’ll see it auto-complete the parameters that are available for the cmdlet. Cool, huh?

Anyway, back to it…

Let’s create a new collection! We’re going to stack together the previous information and put together a single line of Windows PowerShell that will create a collection:

New-CMDeviceCollection –Name “All Windows Server 2012 R2 Devices” –LimitingCollectionName “All Systems”

Image of command output

With that, we have a Configuration Manager collection!

In this section, we learned how to look at the cmdlets that are available within the Configuration Manager module and how to get additional Help about a cmdlet, and we were able to create a collection using nothing but one line of Windows PowerShell. There are 560 Windows PowerShell cmdlets available with Configuration Manager 2012 R2, so I encourage you to get to know them and understand how they can make administration simpler.

~Heath

Thank you, Heath, for writing this series and sharing. I am looking forward to Part 2.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Find All PowerShell Providers

$
0
0

Summary: Use Windows PowerShell to find providers on your system.

Hey, Scripting Guy! Question How can I use Windows PowerShell to find all the Windows PowerShell providers on my system
          and what drives might be exposed?

Hey, Scripting Guy! Answer Use the Get-PSProvider cmdlet.

Use PowerShell to Set Configuration Manager Properties

$
0
0

Summary: Microsoft PFE, Heath Lawson, talks about using Windows PowerShell to set advanced properties in Configuration Manager.

Microsoft Scripting Guy, Ed Wilson, is here. Back today is Heath Lawson. You may want to read the first post of this series before you begin this post: Apply Common PowerShell Concepts to Configuration Manager.

Greetings! Heath here. I’m back with another post in the series about Windows PowerShell for the System Center Configuration Manager Admin. In the last post, we explored using the native Windows PowerShell cmdlets for Configuration Manager to create a device collection. We saw that we can use a combination of Get-Command and Get-Help to explore our way around the Configuration Manager PowerShell module and ultimately create a new Configuration Manager collection.

In this section, we’re going to turn it up to 11 and set some more advanced properties to show how we can interact with our Configuration Manager objects at a deeper level. As an example, we’re going to look at how we can loop in other cmdlets, as well as outside data to create a more realistic device collection.

Explore the collection object

Let’s get started in the lab. We’re going to start where we left off in the last section—using New-CMDeviceCollection to create a new collection. The difference here is that we are going to create an object that is based on the output of that cmdlet, and I’ll show you why.

$CMCollection = New-CMDeviceCollection –Name “All Windows Server 2012 R2 Devices” –LimitingCollectionName “All Systems”

Wait? What happened? We didn’t get the output that we had last time?

Well, here we created a new object ($CMCollection) to hold the resulting output in the form of an object from our cmdlet. Similarly, we could have used Get-CMDeviceCollection to get an existing collection. We can then use the Get-Member cmdlet to find out where all that went:

$CMCollection | Get-Member

Image of command output

Awesome! We can see several things of interest here. Get-Member simply lists all the properties and methods of our object, so we can see what we’re working with. Properties are just that—elements that reflect specific information about an object. Methods are the “verbs” (so to speak), and they allow us to call specific behavior from an object.

Now, we’re going to change a few properties of this collection. Super simple to do, we just need to reference the properties of our object, and set them accordingly:

$CMCollection.Comment = “This collection contains all Windows Server 2012 R2 clients”

We’ve updated the collection comments. Or have we? When I check the properties, it doesn’t show in the console. Why is this? Well, at this point we’ve committed the changes to our instance of the object without committing them via the SMS Provider to the database. To do that, we need to use the Put method to commit the changes:

$CMCollection.Put()

Cool! You might be wondering two things. First, what other methods are available, and second, how can I check this without peeking in the console? Well, I’m glad you asked. We can use the Get() method to update the instance of the object we are working with and show the changes!

$CMCollection.Get()

$CMCollection.Comment

Image of command output

By using this methodology, we can see that we can set almost any property that is available on this object, including properties that are not available when we initially create the new collection, as you can see in the previous example.

Although we used a collection in this section, this same information will hold true on all of the Configuration Manager cmdlets. As an example, being able to set deployment properties on a software updates deployment or on deployment type information is now available.

In the past two posts, I’ve introduced how to incorporate the Configuration Manager cmdlets into your day-to-day administrative work in addition to automation scenarios. In the next and last post, I’ll introduce how to work with items directly via the SMS Provider in Configuration Manager!

~Heath

Thank you, Heath, for this post. Looking forward to Part 3 tomorrow.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 


PowerTip: Find Network Adapters for all Virtual Machines

$
0
0

Summary: Use Windows PowerShell to find the network adapter names for all virtual machines.

Hey, Scripting Guy! Question How can I use Windows PowerShell on my laptop running Windows 8.1 to find the name
          of the network adapter that is used by each of my virtual machines?

Hey, Scripting Guy! Answer Use the Get-VM cmdlet to retrieve all of the virtual machines,
          and pipe the result to the Get-VMNetworkAdapter:

Get-Vm | Get-VMNetworkAdapter

Note: This command must be run with Admin rights.

Use WMI and PowerShell to Create Collection Query Rules

$
0
0

Summary: Microsoft PFE, Heath Lawson, talks about using Windows PowerShell and WMI to create collection query rules.

Microsoft Scripting Guy, Ed Wilson, is here. Heath Lawson is back with us today for Part 3 in his series. You can catch up by reading Part 1 and Part 2:

Howdy y’all! Heath here. This is the last in a series of three posts about Windows PowerShell for System Center Configuration Manager Administrators.

This session is going to be a bit different because we’re not going to use the built-in Configuration Manager cmdlets.

“But Heath,” you say, “I know nothing else. These Configuration Manager cmdlets you showed us in the last session are priceless, and I can’t imagine my life without them!”

I know how you feel, and fear not, we’re going to dig in a bit, and I’ll show you a few methods (no pun intended) that are even more powerful than the built-in Configuration Manager cmdlets!

Let’s start with some background knowledge. Configuration Manager is heavily reliant on Windows Management Instrumentation (WMI) to allow interaction with Configuration Manager data. On the client side, almost all Configuration Manager information (such as policies and content locations) are stored in WMI.

On the server side of the house, we make extensive use of WMI via a special role called the SMS Provider. The purpose of the SMS Provide is to interact with the database and translate between WMI Query Language (WQL) and SQL. All of our security is enforced by the SMS Provider, and it’s where all of our consoles connect to perform administrative functions. Starting in Configuration Manager 2012, we can have multiple SMS Providers per site, which is useful if we are making heavy use of the SMS Provider through automation.

Functionally, the SMS Provider organizes objects into WMI classes, with each class representing a different type of information for Configuration Manager. In Configuration Manager, there are classes for collections, software update groups, individual PCs, task sequence steps—you name it, and there is a class for it.

One really handy tip for folks looking to gain a deeper understanding of Configuration Manager is to look at SMSProv.log. This log will give us a good idea about the WMI query we’re executing and what classes and methods are used for various operations within the console.

The SMS Provider is comprised of a single primary name space that we want to connect to: root\sms\site_<sitecode>. In the following examples, you’ll see that I am using site P35 again. If you’re following along in your lab, make sure that you change the examples to match your site code. You’ll also see that I have a server name specified, and you’ll want to change that to reflect your lab server’s name.

I’d like to pause for a second and provide an awesome link that most folks I talk with aren’t aware exists: System Center 2012 R2 Configuration Manager SDK. Why is the SDK important? Well, without the previous knowledge, most folks don’t know that most of our publically provided code examples make use of the documented WMI calls that are available via the SDK, so understanding how to interact with different objects is hugely important!

Using native WMI to create collection query rules

In this session, we’re going to use native WMI to perform the same steps as last time, except we’re going to go a step further and add a collection query rule this time. Sure, we could use New-CMDeviceCollection, Add-CMDeviceCollectionQueryMembershipRule, and New-CMSchedule to set it, but where’s the fun in that?

Let’s get started. I’m going to introduce something you may not have used in the past: type accelerators. A type accelerator is a “shortcut” of sorts to a .NET managed class. This means that instead of having to do a bunch of funky coding, I can get access directly in Windows PowerShell to .NET WMI methods. Let’s give it a go:

$CMCollection = ([WMIClass]”\\LDCM01\root\sms\site_P35:SMS_Collection”).CreateInstance()

That might look like a funky bit of Windows PowerShell, but allow me to break it down…

First, we’re creating a new object of the WMIClass type: NewCMCollection. We are then assigning a new instance of the SMS_Collection class. So, I just created a new object that represents a new collection. Easy, huh?

Now, if we pull all the properties and methods by using Get-Member, we can see that much more is exposed than we saw last time when we used New-CMDeviceCollection:

Image of command output

So, very much like the last module, we see a number of properties are available for us to configure. However, you’ll see that we have additional methods available. These will change from WMI class to WMI class, but this should give you an idea of how Configuration Manager exposes common functionality.

A great resource for all of this information is the Configuration Manager SDK. If we take a look at SMS_Collection Server WMI Class, we can see the same data documented and ready for consumption.

So, like in the last example, we are going to set some properties on our new collection. Let’s give it a name and a comment:

$CMCollection.Name = “All Windows 2012 R2 Devices”

$CMCollection.Comment = “This collection contains all Windows Server 2012 R2 Devices”

$CMCollection.LimitToCollectionID = “SMS00001”

$CMCollection.RefreshType = 2

Image of command output

Before we get too much further, we need to commit the work we’ve done. Like the previous example, we’ll call the Put() method to commit our new collection:

$CMCollection.Put()

Now that we’ve done that, let’s dive a bit further. What good is a collection without any members? To get resources in this collection, we’ll add a Membership rule…

But wait! We need to create a rule to pass it. In this case, it is asking for an object of the SMS_CollectionRule class. Let’s create one using our new-found knowledge:

$CMRule = ([WMIClass]”\\LDCM01\root\sms\site_P35:SMS_CollectionRuleQuery”).CreateInstance()

$CMRule.QueryExpression=”select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_OPERATING_SYSTEM on SMS_G_System_OPERATING_SYSTEM.ResourceId = SMS_R_System.ResourceId where SMS_G_System_OPERATING_SYSTEM.Caption = `"Microsoft Windows Server 2012 R2 Datacenter`"”

$CMRule.RuleName = “Windows Server 2012”

Now, I have a CollectionRuleQuery object that represents a query-based collection rule. With this, I can set a few properties, namely the QueryExpression, QueryID, and RuleName properties:

Image of command output

With my new found query rule, I can add it to the collection:

$CMCollection.AddMembershipRule($CMRule)

Great! Now, for the final step. We’ll set a refresh schedule so the query updates on a recurring schedule. Schedule tokens are used in several places in Configuration Manager, such as collections and maintenance windows. To set a refresh schedule, we need to create a schedule token:

$CMSchedule = ([WMIClass]"\\LDCM01\Root\sms\site_P35:SMS_ST_RecurInterval").CreateInstance()

$CMSchedule.DaySpan = “1”

$CMSchedule.StartTime = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime((Get-Date).ToString())

Image of command output

Now that I have a schedule token, I can use it to set the RefreshSchedule property:

$CMCollection.RefreshSchedule=$CMSchedule

Like in previous examples,  I need to commit these changes one last time by calling the Put() method.

$CMCollection.Put()

Image of command output

As you can see, we now have a collection with a query-based rule and a refresh schedule:

Image of menu

Cool, huh?

In this session, we created a collection with a query rule and a refresh schedule. You can easily adapt this to set other options or create maintenance windows…the sky is the limit.

As these sessions are coming to a close, my intent was to give each of you the knowledge to more effectively script and automate against Configuration Manager. Hopefully I’ve held up my end of the bargain, and please let me know in the following Comments box!

~Heath

Thanks for an engaging series, Heath!

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Search All WMI Namespaces with PowerShell

$
0
0

Summary: Learn to look for WMI classes in all WMI namespaces by using Windows PowerShell.

Hey, Scripting Guy! Question How can I use Windows PowerShell to search all WMI namespaces so that
          I can find all WMI classes that are related to batteries?

Hey, Scripting Guy! Answer Use the Get-WmiObject cmdlet, start at the Root namespace, and make sure to use the –recurse switch:

Get-WMIObject -Namespace root -ClassName *battery* -list -recurse -EA 0

Weekend Scripter: Create Entries for Birthdays from Outlook Contacts List

$
0
0

Summary: Use Windows PowerShell to access your Outlook Contacts list and create calendar entries for birthdays.

Honorary Scripting Guy, Sean Kearney here. And today is my birthday!

You would think that being an IT person with Microsoft Outlook and other resources in front of me, it would be very easy to track birthdays. You would THINK that.

But I’m…let’s say…absent minded.

Now the interesting thing is that in many cases, I will track a lot of the birthdays in my Contacts when I create them within Outlook. For example, here is one for a fake person called Mr. Smith:

Image of calendar entry

As you can see I have his birthday stored away with his Contact details so that I should be able to at least remember he has some type of birthday. But of course, we have a snag…that is that I’m lazy.

If I look at my calendar, we’ll see a small glitch. On this most important day of days for Dr. Smith—while he’s floating up in his big spinning space saucer—as I glance at my calendar, I notice something missing. His birthday is not entered.

Image of calendar

“Odd…” I think to myself because Outlook 2013 will automatically populate the calendar entry for birthdays. But then I realize that this contact was created a long time ago, and so his birthday was not self-populated by Outlook.

There are a few ways to resolve this.

  1. Type in the calendar entry directly.
  2. Edit the birthday manually and hope that it works to repopulate it.
  3. Let Windows PowerShell find the birthday in the Contacts list and build the calendar entry for me.

Because I think there is more than one birthday that might be missing, and I happen to like playing with Windows PowerShell on a Saturday, well I’ll go the Windows PowerShell route.

So our first challenge: Connect to Outlook with Windows PowerShell:

$Outlook=NEW-OBJECT –comobject Outlook.Application

Well, that was easy. So next we want to get our list of contacts in Outlook. We do this by accessing the Contacts list, which is a default folder type within Outlook.

To access special folders we access the GetSpecialFolder method in the Outlook ComObject. Contacts are DefaultFolder type #10.

$Outlook.session.GetDefaultFolder(10)

But this is just the folder as an object. We need to access the individual items. This is done by literally tacking on items as a property:

$Outlook.session.GetDefaultFolder(10).items

Displaying it on the screen, of course, is only cosmetic. We need to store this away so we can access the properties directly:

$Contacts=$Outlook.session.GetDefaultFolder(10).items

Now if we execute Get-Member against this object, we’ll see that there are many useful properties. We want to see if we can read the birthday date and maybe something with the name details.

$Contacts | GET-MEMBER –membertype Property *Birth*,*Name*

In the following image, we can see lots of Name properties, and the birthday date is clearly accessible. For our purposes, I would like to access only the Display Name and the Birthday. I can use these details to build a birthday in the calendar:

Image of command output

$Contacts | Select-object Fullname,Birthday

But wait, some people might not have a birthday entered! I check the contact information for Doctor Smith’s handler, Will Robinson. For some reason, I do not have his birthday recorded, so I can see what a blank birthday looks like:

Image of command output

So blank dates for the birthday will be represented somewhere far, far, far in the future— January 1 in the year 4501! About the same time Dr. Smith will learn to behave.

So let’s put this into Windows PowerShell and store the information away:

$BirthdayInfo=$Contacts | Select-Object FullName,Birthday | Where { $_.Birthday –ne ([datetime]”1/1/4501”) }

Good! Now that we have the needed information, we’ll want to populate the entries in the calendar. Let’s create a new calendar item. This is done by accessing the CreateItem() method in Outlook and specifying Item Type # 1 for appointments:

$NewEntry=$Outlook.CreateItem(1)

We need to supply some information for our calendar entry, specifically the Date it is to occur and some Subject details:

$NewEntry.Subject=”John Q Smith Birthday”

$NewEntry.Start=[datetime]”5/3/1921”

I would like this to appear as a recurring appointment, so we’ll need to access the Recurrence property of the calendar item. This is actually done in a weird way. We need to get the recurrence pattern first:

$Recur=$NewEntry.GetRecurrencePattern()

When we have the recurrence pattern as an object, we need to supply it some information. First we have to supply some special values. It’s an all-day event, which is 1,440 minutes. Our interval is once every 12 months (unless you know of somebody who has a birthday more often than that):

$Recur.Duration=1440

$Recur.Interval=12

Now we specify that it’s a yearly occurrence and that it never ends:

$Recur.RecurrenceType=5

$Recur.Noenddate=$TRUE

When we have this information set, we need only save the calendar item:

$NewEntry.save()

Now we pull all of this together into a simple loop to have the computer do the work for us:

# Connect to Outlook

$Outlook=NEW-OBJECT –comobject Outlook.Application

$Contacts=$Outlook.session.GetDefaultFolder(10).items

# Get only Contacts with a valid Birthday

$BirthdayInfo=$Contacts | Select-Object FullName,Birthday | Where { $_.Birthday –ne ([datetime]”1/1/4501”) }

# Step through the list of contacts and create Birthdays in the Calendar

Foreach ($Entry in $BirthdayInfo)

{

# Create New Calendar Item

$NewEntry=$Outlook.CreateItem(1)

# Populate Calendar Details

$NewEntry.Subject=$Entry.Fullname+”’s Birthday”

$NewEntry.Start=$Entry.Birthday

# Access Recurrence pattern

$Recur=$NewEntry.GetRecurrencePattern()

# Setup Recurrence Patterns

$Recur.Duration=1440

$Recur.Interval=12

$Recur.RecurrenceType=5

$Recur.Noenddate=$TRUE

# Save the Calendar entry

$NewEntry.save()

}

And now we have a way to go through all of my contacts and NOT forget all of their birthdays! So much easier than type…type type…type type type…type…*GrOooooAAAAaaaannn!*

Now back to a little birthday cake and a toast to any of you who are using Windows PowerShell. The best present of all for an IT pro!

By the way if you’re wondering about a birthday tune, download the Windows PowerShell Popcorn Tune from the TechNet Gallery, and replace the $MusicString contents with these lines:

# Our 'Sheet Music' - 5 characters define each note and beat.

# First three are the note, the last pair are the beat

$MusicString=@"

BGN80BGN80CAN40BGN40CCN40CBN20BGN80BGN80CAN40BGN40CDN40CCN20BGN80BGN80CGN20CEN20CCN20CBN20CAN10CFN80CFN80CEN40CCN40CDN40CCN10

"@

I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send an email to the Scripting Guys at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, remember that the Power of Shell is in You.

Sean Kearney, Windows PowerShell MVP and Honorary Scripting Guy 

PowerTip: List Outlook Contacts with PowerShell

$
0
0

Summary: Use Windows PowerShell to list contacts in Microsoft Outlook.

Hey, Scripting Guy! Question How can I use Windows PowerShell to programmatically connect to Outlook to get the Contacts list?

Hey, Scripting Guy! Answer Connect by using ComObject, and select the objects you need:

$Outlook=NEW-OBJECT –comobject Outlook.Application

$Contacts=$Outlook.session.GetDefaultFolder(10).items

$Contacts | Format-Table FullName,MobileTelephoneNumber,Email1Address

PowerShell Jobs Week: Introduction to PowerShell Jobs

$
0
0

Summary: Richard Siddaway introduces you to Windows PowerShell jobs.

Hey, Scripting Guy! Question Hey, Scripting Guy! I’ve just starting learning Windows PowerShell, and I have some long running tasks to perform. What’s the best way of running these tasks?

—LJ

Hey, Scripting Guy! Answer Hello LJ,

Honorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy. LJ, you need to use Windows PowerShell jobs for your long running tasks. Jobs are an area of Windows PowerShell that I feel is neglected. The headline piece of functionality in Windows PowerShell 2.0 was remoting. In many respects, Windows PowerShell jobs are just as important and beneficial, to your automation efforts as remoting has been.

This is the first of a series of posts that, hopefully, will shine the spotlight on Windows PowerShell jobs, remind people of their capabilities, and encourage their greater adoption. The full series comprises:

  1. Introduction to PowerShell jobs (this post)
  2. WMI and CIM Jobs
  3. Remote Jobs
  4. Scheduled Jobs
  5. Workflow Jobs
  6. Job Processes
  7. Jobs in the Enterprise

This introductory post will cover the basics of using Windows PowerShell jobs.

Jobs were introduced in Windows PowerShell 2.0 and helped to solve a problem inherent in the command-line tools. That is, if you start a long running task, your prompt is unavailable until the task finishes. As an example of a long running task, think of this simple Windows PowerShell command:

Get-ChildItem -Path c:\ -Recurse

If you run the command, you will get a full directory list of your C: drive. That will take some time. As an aside, if you are experimenting with performance counters in Windows PowerShell, running this command in a couple of consoles is a good way to give you CPU a work out.

Back when I was using VBScript, I would have multiple command prompts open—each running a script.  That can get confusing and annoying.  With Windows PowerShell, you can also run multiple consoles or instances of the Windows PowerShell ISE. But a better approach is to use a Windows PowerShell job.

To create a Windows PowerShell job, simply put your code into the script block of the Start-Job cmdlet:

Start-Job -ScriptBlock {Get-ChildItem -Path c:\ -Recurse}

The cmdlet will return information about the job as shown in the following screenshot.

Image of command output

Windows PowerShell will assign a name and numeric ID. You can see that the job type is a Background job (job types are explained in the Help file about_Job_Details), and that it’s running on the local machine. The final column shows (at least some of) the command that is running.

Note  The numeric IDs that you see on your machine may vary from those displayed in this post. The important point is the relationship between the jobs and their IDs.

You can check on the progress of your jobs using Get-Job:

Image of command output

The same information is presented as when the job started.

You can use the job name or ID to pull back information on an individual job:

Get-Job -Id 4

Get-Job -Name Job4

Both commands return the same information about your job. It is interesting to use the –IncludeChildJob parameter, as shown in the next screenshot.

Image of command output

The job with an ID of 4 is the data you’ve been seeing all along. Now a job with an ID of 5 has suddenly appeared. Actually, it’s been there all the time because Windows PowerShell jobs created through Start-Job always consist of a parent job and a child job. The child job does the actual work. If you were running the job against a number of remote machines by using Invoke-Command and its –AsJob parameter, you would get one child job per remote machine.

When you manage jobs, anything you do to the parent job is automatically applied to any child jobs. Removing or stopping the parent job performs the same action on the child jobs. Getting the results of the parent job means you get the results of all the child jobs. As you will see later, you can access the child jobs directly to retrieve their data.

If you start another job as shown here…

Image of command output

…you’ll see that it is given an ID of 6. Windows PowerShell automatically assigns the next ID number, taking child job IDs into account. This can cause confusion to people new to Windows PowerShell jobs.

When a job has completed, you can retrieve the data stored in the job.

Image of command output

You can see that the job consists of a parent job (ID 6) and a child job (ID 7). You can use Receive-Job to get the data from the job. I added the –Keep parameter to prevent the default action of the data being deleted from the job. In a simple job, as in the example, you can access the data through the parent or child jobs:

Receive-Job -Id 6 -Keep

Receive-Job -Id 7 –Keep

When you have multiple child jobs, its usually easier to access the child jobs in turn:

$jobs = Get-Job -Name Job6 | select -ExpandProperty ChildJobs

foreach ($job in $jobs){Receive-Job -Job $job -Keep}

You’ve seen some of the cmdlets that are associated with Windows PowerShell jobs. The full set is:

  • Get-Job
    Gets Windows PowerShell background jobs that are running in the current session
  • Receive-Job
    Gets the results of the Windows PowerShell background jobs in the current session
  • Remove-Job
    Deletes a Windows PowerShell background job
  • Resume-Job
    Restarts a suspended job
  • Start-Job
    Starts a Windows PowerShell background job
  • Stop-Job
    Stops a Windows PowerShell background job
  • Suspend-Job
    Temporarily stops workflow jobs
  • Wait-Job
    Suppresses the command prompt until one or all of the Windows PowerShell background jobs that are running in the session are complete

Suspend-Job and Resume-Job only apply to workflow jobs. You’ll learn more about them on day 5.

Each cmdlet has an associated Help file (remember that you can use Update-Help in Windows PowerShell 4.0 and Windows PowerShell 3.0). There is also an extensive set of about files:

  • about_Jobs
  • about_Job_Details
  • about_Remote_Jobs
  • about_Scheduled_Jobs
  • about_Scheduled_Jobs_Advanced
  • about_Scheduled_Jobs_Basics
  • about_Scheduled_Jobs_Troubleshooting

One important point to note is that the simple Windows PowerShell jobs that you’ve seen so far are isolated to the Windows PowerShell session in which you started the job. If you close the session that contains the jobs, you will lose the jobs and your results unless you’ve saved the data to disk.

Another way to work with jobs is to create a variable that holds the job object:

Image of command output

You create the variable when you start the job:

$myjob = Start-Job -ScriptBlock {Get-ChildItem }

Notice in the screenshot that there is no output as the job starts. You can use the variable to display job information or any of the techniques you’ve seen already:

Get-Job -Id 10

Get-Job -Name Job10

You can also do this:

$myjob | Get-Job

The variable can be used directly when retrieving job data:

Receive-Job -Job $myjob –Keep

Many people like to name their jobs rather than allowing Windows PowerShell to name them:

Start-Job -ScriptBlock {Get-ChildItem } -Name myjob

Get-Job -Name myjob

Receive-Job -Name myjob -Keep

You can see the start and end times of your jobs:

Image of command output

If you want to see the execution time, you need to do a little bit of work:

Get-Job | select PSBeginTime, PSEndTime, @{N='ExecutionTime'; E={$_.PSEndTime - $_.PSBeginTime}}

The execution time is calculated by subtracting the begin time from the end time, which results in a timespan object:

Image of command output

Notice that the first job run, which was Get-ChildItem, runs recursively against the C: drive, and it took over 52 minutes to run. That’s a long time to have your Windows PowerShell prompt locked up by a single task. Use a job and you can carry on working.

You should always aim to clean up your Windows PowerShell environment and remove any lingering artifacts such as jobs, remoting, or CIM sessions before shutting Windows PowerShell. In the case of jobs, they will be removed for you if you don’t do it, but I think that is a sloppy approach. Especially when you can remove all old jobs in one pass:

Get-Job | Remove-Job

Or you can remove individual jobs:

Get-Job -Id 12  | Remove-Job

Remember that Name can also be used as an identifier.

Alternatively, you can filter on job completion:

Get-Job -State Completed | Remove-Job

You need to ensure that your jobs have all completed before you delete them and close Windows PowerShell:

Get-Job | where State -ne 'Completed'

One common mistake when people start working with jobs is to forget to retrieve the data before deleting the job. Try to make using jobs a process: Run the job, get the data, delete the job. That way, you clean up as you work with the jobs, and you don’t forget to retrieve your data.

That’s it for today. Tomorrow you’ll learn about WMI and CIM jobs. Bye for now.

~Richard

Thanks, Richard.

Richard Siddaway is based out of the UK, and he spends his time automating anything and everything, for Kelway, Ltd. A six-year Windows PowerShell MVP, Richard is a prolific blogger, mainly about Windows PowerShell (Richard Siddaway's Blog: Of PowerShell and Other Things) and a frequent speaker at user groups and Windows PowerShell conferences. He has written a number of Windows PowerShell books: PowerShell in Practice; PowerShell and WMI, PowerShell in Depth (co-author); PowerShell Dive (co-editor), and he is currently finishing Learn Active Directory Management in a Month of Lunches, which features lots of Windows PowerShell. All of the books are available from Manning Publications.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Control Job Starts with PowerShell

$
0
0

Summary: Learn how to use Windows PowerShell to control which jobs start when.

Hey, Scripting Guy! Question How can I use Windows PowerShell to complete a group of jobs before I start more jobs?  

Hey, Scripting Guy! Answer Use the Start-Job cmdlet, for example, to start 3 long running processes:

Start-Job {<long running process 1>}

Start-Job {<long running process 2>}

Start-Job {<long running process 3>}

Then use Wait-Job to stop further processing. It works at the prompt, or even better, in a script:

Get-Job | Wait-Job

After your jobs have completed, you can run more jobs:

Start-Job {<long running process 4>}

Start-Job {<long running process 5>}


PowerShell Jobs Week: WMI and CIM Jobs

$
0
0

Summary: Richard Siddaway introduces you to WMI and CIM jobs in Windows PowerShell.

Honorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy. This is the second in a series of posts that, hopefully, will shine the spotlight on Windows PowerShell jobs, remind people of their capabilities, and encourage their greater adoption. The full series comprises:

  1. Introduction to PowerShell Jobs
  2. WMI and CIM Jobs (this post)
  3. Remote Jobs
  4. Scheduled Jobs
  5. Workflow Jobs
  6. Job Processes
  7. Jobs in the Enterprise

Last time, you saw how to use the Windows PowerShell job cmdlets. But there is another way to start jobs. Many cmdlets have an –AsJob parameter, for instance:

Get-WmiObject [-Class] <string> [[-Property] <string[]>] [-Filter <string>] [-Amended] [-DirectRead] [-AsJob]

[-Impersonation <ImpersonationLevel>] [-Authentication <AuthenticationLevel>] [-Locale <string>]

[-EnableAllPrivileges] [-Authority <string>] [-Credential <pscredential>] [-ThrottleLimit <int>]

[-ComputerName<string[]>] [-Namespace <string>] [<CommonParameters>]

You would normally use Get-WmiObject like this:

£> Get-WmiObject -Class Win32_ComputerSystem

Domain              : WORKGROUP

Manufacturer        : Microsoft Corporation

Model               : Surface Pro 2

Name                : RSSURFACEPRO2

PrimaryOwnerName    : richard

TotalPhysicalMemory : 8506142720

The –AsJob parameter is used to turn this into a Windows PowerShell job:

Image of command output

-AsJob is a switch parameter. It doesn’t allow you to configure the job name or any of the other aspects you saw when using Start-Job. One thing to notice is the PSJobTypeName (it is WmiJob). The command is being run as a Windows PowerShell job, but it runs in a different process to Windows PowerShell jobs that are initiated with Start-Job. You’ll learn more about the way different jobs run when we look at job processes in Part 6 of this series.

If you are running a mixture of job types, for instance WMI jobs and Background jobs, you can use the job type in your searches. Unfortunately, Get-Job doesn’t have a parameter to facilitate this search, so you have to fall back on Where-Object:

Get-Job | where PSJobTypeName -eq 'WmiJob'

Get-Job | where PSJobTypeName -eq 'BackgroundJob'

Alternatively, you may want to see all of the jobs, but have them sorted by type:

Get-Job | sort PSJobTypeName

This will display the jobs by type. But ideally, I’d like them displayed in historical order within type. This requires you to add an additional property to the sort:

Get-Job | sort PSJobTypeName, Id

Thinking about it, this would be a great addition to Get-Job. You could create a proxy function for Get-Job that added two parameters:  ByType and ByTypeHistorical, which would display the data in the appropriate sort order. That would make an interesting post for some time in the future.

After you’ve started your WMI-based job, you can manage the job with the standard Windows PowerShell job cmdlets.

Image of command output

Get-Job will display the jobs that exist on your system. Receive-Job will retrieve the data from the job, and Remove-Job will delete the job for you.

The WMI cmdlets are frequently used to work with remote machines, it’s one of their great attractions. What happens when you combine the –AsJob and –ComputerName parameters? The job runs on the local machine, but it uses the –ComputerName parameter to access the remote machine. As far as Windows PowerShell is concerned, it’s just another job.

If you specify multiple computers to Get-WmiObject and run the command as a job, Windows PowerShell will run one child job per computer. There is a limit on the number of child jobs that can run simultaneously. By default 32 child jobs can be run at one time. This number can be modified by using the –ThrottleLimit parameter.

You may be tempted to increase the number of simultaneous operations to match the maximum number of machines that you want to access. This isn’t necessarily a good idea because each child job will run in its own process. You could end up consuming all of the client’s resources, which will cause a failure. I would recommend that if you decide to increase the number of simultaneous operations, you slowly ramp up the–ThrottleLimit setting while closely monitoring the resources that are being consumed on the machine.

So far, you’ve only seen Get-WmiObject use the –AsJob parameter. The other WMI cmdlets also have this parameter:

  PS C:\Scripts> Get-Help *wmi* -Parameter AsJob

Name

----

Get-WmiObject

Invoke-WmiMethod

Remove-WmiObject

Set-WmiInstance

Register-WmiEvent doesn’t have an –AsJob parameter. This makes sense because jobs don’t run in your current Windows PowerShell session—they use a separate process. You want to register the event in your current process, not in some random other process.

$proc = Get-WmiObject -Class Win32_Process -Filter "Name = 'notepad.exe'"

Remove-WmiObject -InputObject $proc  -AsJob

The object is removed immediately. There isn’t any data to be retrieved from the job.

A similar process can be used for Invoke-WmiMethod:

$proc = Get-WmiObject -Class Win32_Process -Filter "Name = 'notepad.exe'"

Invoke-WmiMethod -InputObject $proc -Name Terminate –AsJob

This time, there is data returned in the job:

PS C:\Scripts> Receive-Job -Id 14 -Keep

__GENUS          : 2

__CLASS          : __PARAMETERS

__SUPERCLASS     :

__DYNASTY        : __PARAMETERS

__RELPATH        :

__PROPERTY_COUNT : 1

__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

ReturnValue      : 0

PSComputerName   :

In this case, WMI returns the standard data from a method invocation. ReturnValue is the important property. A value of zero indicates success, and any other value indicates the method call failed.

Windows PowerShell 3.0 saw the introduction of a new API for accessing WMI. One of the outcomes of this was the introduction of the CIM cmdlets:

PS C:\Scripts> Get-Command *Cim* -CommandType cmdlet | select Name

Name

----

Get-CimAssociatedInstance

Get-CimClass

Get-CimInstance

Get-CimSession

Invoke-CimMethod

New-CimInstance

New-CimSession

New-CimSessionOption

Register-CimIndicationEvent

Remove-CimInstance

Remove-CimSession

Set-CimInstance

Get-CimInstance is analogous to Get-WmiObject, and it is used in a similar manner:

Get-CimInstance -ClassName Win32_ComputerSystem

However, if you look at the syntax of Get-CimInstance, there is one glaring omission. There isn’t any –AsJob parameter:

PS C:\Scripts> Get-Command Get-CimInstance -Syntax

Get-CimInstance [-ClassName] <string> [-ComputerName <string[]>] [-KeyOnly] [-Namespace <string>]

[-OperationTimeoutSec <uint32>] [-QueryDialect <string>] [-Shallow] [-Filter <string>] [-Property <string[]>]

[<CommonParameters>]

Get-CimInstance -CimSession <CimSession[]> -Query <string> [-ResourceUri <uri>] [-Namespace <string>]

[-OperationTimeoutSec <uint32>] [-QueryDialect <string>] [-Shallow] [<CommonParameters>]

Get-CimInstance [-InputObject] <ciminstance> -CimSession <CimSession[]> [-ResourceUri <uri>] [-OperationTimeoutSec <uint32>] [<CommonParameters>]

Get-CimInstance [-ClassName] <string> -CimSession <CimSession[]> [-KeyOnly] [-Namespace <string>]

[-OperationTimeoutSec <uint32>] [-QueryDialect <string>] [-Shallow] [-Filter <string>] [-Property <string[]>]

[<CommonParameters>]

Get-CimInstance -CimSession <CimSession[]> -ResourceUri <uri> [-KeyOnly] [-Namespace <string>] [-OperationTimeoutSec <uint32>] [-Shallow] [-Filter <string>] [-Property <string[]>] [<CommonParameters>]

Get-CimInstance [-InputObject] <ciminstance> [-ResourceUri <uri>] [-ComputerName <string[]>] [-OperationTimeoutSec <uint32>] [<CommonParameters>]

Get-CimInstance -Query <string> [-ResourceUri <uri>] [-ComputerName <string[]>] [-Namespace <string>]

[-OperationTimeoutSec <uint32>] [-QueryDialect <string>] [-Shallow] [<CommonParameters>]

Get-CimInstance -ResourceUri <uri> [-ComputerName <string[]>] [-KeyOnly] [-Namespace <string>] [-OperationTimeoutSec <uint32>] [-Shallow] [-Filter <string>] [-Property <string[]>] [<CommonParameters>]

If you want to use the CIM cmdlets in a job, you need to initiate the job with Start-Job:

Start-Job  -ScriptBlock {Get-CimInstance -ClassName Win32_ComputerSystem}

The standard job cmdlets are used to manage the job, for instance:

PS C:\Scripts> Receive-Job -Id 16 -Keep | fl 

Domain              : WORKGROUP

Manufacturer        : Microsoft Corporation

Model               : Surface 2

Name                : RSSURFACE2

PrimaryOwnerName    : Richard

TotalPhysicalMemory : 2094624768

The other cmdlets, such as Invoke-CimMethod can also be run as jobs by using Start-Job.

The fact that the CIM cmdlets don’t have an –AsJob parameter isn’t a major issue. You simply need to remember to use the job cmdlets when you want to run the CIM cmdlets as jobs. However, it gets confusing when you discover the CDXML cmdlets.

CDXML (cmdlets over objects) is a way to create Windows PowerShell functionality by taking a WMI class, wrapping it in some XML, and saving it on your module path. At its simplest, a CDXML module will look like this:

<?xml version='1.0' encoding='utf-8'?>
<PowerShellMetadata xmlns='http://schemas.microsoft.com/cmdlets-over-objects/2009/11'>
  <Class ClassName='ROOT\cimv2\Win32_NetworkAdapterConfiguration'>
    <Version>1.0</Version>
    <DefaultNoun>NetworkAdapterConfiguration</DefaultNoun>

    <InstanceCmdlets>
      <GetCmdletParameters DefaultCmdletParameterSet='DefaultSet'>
             
      </GetCmdletParameters>
    </InstanceCmdlets> 
  </Class>
 
</PowerShellMetadata>

The class in use is Win32_NetworkAdapterConfiguration. Notice that the namespace and class name are used.  You also need to add a default noun. In this case, it is NetworkAdapterConfiguration.

Save the file as NetworkAdapterConfiguration.cdxml (or whatever you want to call it). You can then load it as:

Import-Module .\NetworkAdapterConfiguration.cdxml

It will auto-load if it’s on your module path. The cmdlet is then available for use:

Get-NetworkAdapterConfiguration

You can add search parameters and use the Win32_NetworkAdapterConfiguration methods to create additional cmdlets.

If you look at the syntax of your new cmdlet, you’ll see this:

£> Get-Command Get-NetworkAdapterConfiguration -Syntax 

Get-NetworkAdapterConfiguration [-CimSession <CimSession[]>] [-ThrottleLimit <int>] [-AsJob] [<CommonParameters>]

You get -CimSessions and –AsJob parameters without any work.

£>  Get-NetworkAdapterConfiguration -AsJob 

Id     Name            PSJobTypeName   State         HasMoreData     Location

--     ----            -------------   -----         -----------     --------

6      CimJob3         CimJob          Running       True            RSSURFACEPRO2

Notice the job type is now CimJob rather than the WmiJob we saw earlier. Get-Job and the other job cmdlets work as normal on these jobs.

You may be wondering why I’ve discussed CDXML because it’s a fairly exotic topic in the Windows PowerShell world. The important point is that over 60% of the Windows PowerShell functionality since Windows Server 2012 and Windows 8 is CDXML based. If you examine the C:\Windows\System32\WindowsPowerShell\v1.0\Modules subfolders, you will find many .cdxml files. Knowing that you can use these cmdlets directly in jobs will save you some work.

That’s it for today. Tomorrow you’ll learn about running jobs on remote computers. Bye for now.

~Richard

Thanks for writing this interesting series, Richard.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell to Keep Job Data

$
0
0

Summary: Learn how to use Windows PowerShell to keep job data available for future use.

Hey, Scripting Guy! Question How can I keep my job data?

Hey, Scripting Guy! AnswerOne of the frustrating things about using the Window PowerShell job cmdlets is that if you’re not careful,
          you lose the data in your job when you access the job. The following command returns the job’s data,
          but it also deletes that data:

Receive-Job -Id 8

If you want the data available, use the –Keep parameter:

Receive-Job -Id 10 –Keep

Using –Keep is a good habit to get into. It’ll save you having to rerun a bunch of jobs to regenerate the data.  

PowerShell Jobs Week: Remote Jobs

$
0
0

Summary: Richard Siddaway introduces you to Windows PowerShell jobs on remote machines.

Honorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy. This is the third in a series of posts that, hopefully, will shine the spotlight on Windows PowerShell jobs, remind people of their capabilities, and encourage their greater adoption. The full series comprises:

  1. Introduction to PowerShell Jobs
  2. WMI and CIM Jobs
  3. Remote Jobs (this post)
  4. Scheduled Jobs
  5. Jobs and Workflows
  6. Job Processes
  7. Jobs in the Enterprise

So far, you’ve seen how to use the core Job cmdlets and the –AsJob parameter, and we discovered the interesting aspects of the CIM cmdlets and CDXML. One of biggest selling points for Windows PowerShell is the way it enables you to administer remote machines. You have a number of options:

  • Cmdlets with the –ComputerName parameter
  • Invoke-Command
  • Interactive remote sessions
  • Windows PowerShell remoting sessions
  • Workflows
  • CIM sessions
  • WSMAN cmdlets
  • Desired State Configuration

All of these options enable you to perform at least some amount of administration on the remote machine. But how do Windows PowerShell jobs fit into this framework? Start-Job doesn’t have a –ComputerName parameter. A couple of techniques immediately come to mind. Is there any difference, for instance, between the following two commands?

Start-Job -ScriptBlock {Get-Process -ComputerName server03}

Invoke-Command -ScriptBlock {Get-Process} -ComputerName server03 -AsJob

And what do they actually do?

They both start a job. They both call Get-Process. And they both run against a remote machine called server03.

In reality, though, there is a difference. The difference is about where the job runs and how you get to the data.

Image of command output

Start-Job starts a normal background job. The remote connection is handled within the job script block. The script block runs as a separate Windows PowerShell process (more on that in Part 6 of the series). The remote connection is managed by Get-Process, so it is destroyed when the data is returned. Notice that the location says localhost, which means that the job is running locally.

In the second job, you’re using Invoke-Command to run the command and control the connection to the remote machine. Invoke-Command uses standard Windows PowerShell remoting for connectivity to remote machines. The –AsJob parameter brings the Windows PowerShell job engine into play. Notice that this time the PSJobTypeName is RemoteJob and the location is server03 (the remote machine).

The big difference is where the job runs. By using Start-Job, it runs locally and the code in the script block handles the connectivity. By using Invoke-Command, the job runs on the remote machine, but the results come back to the local machine.

Irrespective of how you started the job, you can use the standard job cmdlets with these jobs.

Image of command output

Get-Job works as you would expect. You can use Receive-Job in exactly the same way on both jobs.

There are a number of broad scenarios for running jobs on remote machines:

Option 1: Run a normal job and perform the remote connectivity inside the job’s script block. This is demonstrated in the first of the examples you’ve seen. 

Option 2: Start an interactive session to a remote server. If you start a job in the interactive session, you have exactly the same experience as if you were running on the local machine, but all actions are performed on the remote machine.

Option 3: Run the background job on a remote machine and have the data returned to the local machine. This is demonstrated by the second of the examples you’ve seen.

Option 4: Run the background job on the remote machine and keep the results on the remote machine. This very similar to Option 2, but you are using a Windows PowerShell remote session rather than an interactive session.

You’ve seen examples of Options 1 and 3 (although, I’ll provide another example later that uses a remote session). So let’s look at running jobs in an interactive session.

Image of command output

Interactive remote sessions have to be one of the coolest Windows PowerShell features ever. Use Enter-PSSession to open the session. If you already have a session open referenced by the variable $sess, you can enter that session:

$sess = New-PSSession –ComputerName server03

Enter-PSSession -Session $sess

When you enter the session, your prompt changes to include the remote machine name. In this case changing from

£>

   to

[server03]: PS C:\Users\richard\Documents>

You can then use Start-Job and the other job cmdlets as you would on a local machine. Notice that the location is localhost and the job type is BackgroundJob. Also notice that the first process returned is different when compared to the previous examples. This is because of the interactive session.

Each time you create a new interactive session, the job ID counters reset. Notice that the job starts with an ID of 1 rather than the first job having an ID of 2 when running locally.

When you exit the session, it is destroyed if you created it with Enter-PSSession. If you are using an existing session, it will remain available for use until you explicitly destroy it.

The other option that you haven’t see yet is Option 4—running the job on the remote machine and keeping the data on the remote machine.

Image of command output

As with any remoting work, if you are going to send multiple commands to the same machine, create a remote session. It’s much more efficient compared to creating and tearing down multiple connections. You also need the continuity of the session to ensure that you can access the job when it has completed.

$sess = New-PSSession -ComputerName server03

With the session in place, you can use it with Invoke-Command to send the Start-Job command to the remote machine.

Invoke-Command -Session $sess -ScriptBlock {Start-Job -ScriptBlock {Get-Process}}

You will get back the job start information, which may be confusing because the job is supposed to be running on the remote machine. Remember that Invoke-Command always returns any response from an issued command.

You can easily test the state of any jobs on your local machine by using:

Get-Job

In this example there were no local jobs.

Next, test that the job has completed:

Invoke-Command -Session $sess -ScriptBlock {Get-Job}

You may need to repeat the test a number of times if it’s a long running job. After the job has completed, you can retrieve the data:

Invoke-Command -Session $sess -ScriptBlock {Receive-Job -Id 1 -keep | select -First 1}

At the end of your work, always clean up and remove unwanted jobs and the remote sessions.

Invoke-Command -Session $sess -ScriptBlock {Get-Job | Remove-Job}

$sess | Remove-PSSession

You were promised a look at using Invoke-Command –AsJob (Option 3) through a Windows PowerShell remoting session.

Image of command output

Create the session and use the session with Invoke-Command and the –AsJob parameter. The job will run on the remote machine, but the results will be returned to the local machine as shown in the previous image. Access the results and manage the job as any other job on your local machine.

So, one question remains…

Should we bring back the data to the local machine or keep the data on the remote machine?

That’s Options 1-3 versus Option 4. As usual, the answer is, “That it depends.”

If you are performing some kind of fan-out administration task, you want the data and information about job success or failure brought back to the local machine. If you can’t do this, you’re making more work for yourself in collecting the data.

I’d reserve the option for keeping the data on the remote machine for situations where the data may be confidential, and it would be more secure if it was left on the remote machine until you need it.

That’s it for today. Tomorrow you’ll learn about creating and using scheduled jobs on remote computers. Bye for now.

~Richard

Thanks again, Richard!

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell to Find Job Help

$
0
0

Summary: Learn how to find more Help about the Windows PowerShell job engine.

Hey, Scripting Guy! Question How can I find out more about Windows PowerShell jobs?

Hey, Scripting Guy! Answer One of the best ways to discover anything about Windows PowerShell is to read the Help files
          and work through the examples that are presented. Each of the cmdlets has its own Help file, for instance:

Get-Help Start-Job –ShowWindow

There are also a set of about_ files:

£> doc about*job* | select name

 

Name

----

about_Jobs

about_Job_Details

about_Remote_Jobs

about_Scheduled_Jobs

about_Scheduled_Jobs_Advanced

about_Scheduled_Jobs_Basics

about_Scheduled_Jobs_Troubleshooting

PowerShell Jobs Week: Scheduled Jobs

$
0
0

Summary: Richard Siddaway introduces you to scheduled PowerShell jobs.

Honorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy. This is the fourth in a series of posts that, hopefully, will shine the spotlight on Windows PowerShell jobs, remind people of their capabilities, and encourage their greater adoption. The full series comprises:

  1. Introduction to PowerShell Jobs
  2. WMI and CIM Jobs
  3. Remote Jobs
  4. Scheduled Jobs (this post)
  5. Jobs and Workflows
  6. Job Processes
  7. Jobs in the Enterprise

So far, you’ve discovered the core cmdlets for working with Windows PowerShell jobs, investigated using WMI and CIM cmdlets in jobs, and learned how to run jobs on remote machines. Today we go off on a little bit of a tangent and discover how scheduled jobs work.

Windows PowerShell 3.0 introduced two modules that can be confused:

  • PSScheduledJob
  • ScheduledTasks

The ScheduledTasks module is for working with the Task Scheduler. It is a CDXML (WMI-based) module that is only found in Windows 8.1, Windows 8, Windows Server 2012 R2, and Windows 2012. It is not part of the WMF 4.0 or WMF 3.0 download that is available for legacy versions of Windows.

The PSScheduledJob module is a binary module that is part of the WMF 4.0 and WMF 3.0 downloads, and it is natively installed in Windows 8.1, Windows 8, Windows Server 2012 R2, and Windows 2012.

Having cleared up that bit of confusion, the next question is, “What’s a scheduled job?”

The easiest way to answer that is to quote from the Help file: “Windows PowerShell scheduled jobs are a useful hybrid of Windows PowerShell background jobs and Task Scheduler tasks.”

In addition to the cmdlet Help files, there is also a set of about_ files:

  • about_Scheduled_Jobs
  • about_Scheduled_Jobs_Advanced
  • about_Scheduled_Jobs_Basics
  • about_Scheduled_Jobs_Troubleshootingd

I would strongly recommend that you read the information in these files.

Scheduled jobs are like normal jobs in that they run asynchronously in the background, and they can be managed by using the core Windows PowerShell job cmdlets. They also have an aspect of Task Scheduler tasks because they can be saved to disk and run to a schedule or on an ad hoc basis like any other scheduled task.

Now that you know what they are, how do you work with them?

The first thing to do with any new module is to have a look at the cmdlets it contains:

     Get-Command -Module PSScheduledJob | sort noun, verb

  • Add-JobTrigger
  • Disable-JobTrigger
  • Enable-JobTrigger
  • Get-JobTrigger
  • New-JobTrigger
  • Remove-JobTrigger
  • Set-JobTrigger
  • Disable-ScheduledJob
  • Enable-ScheduledJob
  • Get-ScheduledJob
  • Register-ScheduledJob
  • Set-ScheduledJob
  • Unregister-ScheduledJob
  • Get-ScheduledJobOption
  • New-ScheduledJobOption
  • Set-ScheduledJobOption

There are three groups of cmdlets for working with JobTriggers, ScheduledJobs, and ScheduledJobOptions. Let’s work through these by creating a new scheduled job.

Image of command output

Start by creating a new trigger for the scheduled job. You can add triggers to the job after it’s created:

$trigger = New-JobTrigger -Daily -At "19:15"

This trigger sets the scheduled job to start at 19:15 (7:15pm) every day. The usual possibilities with scheduled tasks are available so you can have jobs that run on particular days, at logon or startup, every N days, on specific days of the week, or every N weeks.

There are a number of options you can use with your scheduled job. In this case, the job will run with elevated privileges, and it will continue even if the machine goes on battery power:

$option = New-ScheduledJobOption -RunElevated –ContinueIfGoingOnBattery

The final part is to register the job:

Register-ScheduledJob -Name HSG1 -ScriptBlock {Get-Process} -Trigger $trigger -ScheduledJobOption $option

You need to provide a name, a script block that you want the job to run, and the trigger and options. When your job registers, you will see the type of output shown in the previous screenshot.

There is a clue to one important point that you need to remember in the title bar of the Windows PowerShell console shown in the previous screenshot. You have to be running Windows PowerShell with elevated privileges when you create scheduled jobs.

Your scheduled jobs can be found in the Task Scheduler under:

\Microsoft\Windows\PowerShell\ScheduledJobs.

If you want to see the results of your scheduled job immediately as a test, you have two options. First, you can add the
–RunNow parameter when you use Register-ScheduledJob. This will run your scheduled job once. This parameter is only available in Windows PowerShell 4.0.

Alternatively, if you’ve set your scheduled job to run sometime in the future, you can test the job by using Start-Job.

Image of command output

Start-Job is used with the –Definition name parameter. This causes the scheduled job to be run as a standard job. But note the job type: PSScheduledJob. The results will be subject to the standard rules for Windows PowerShell jobs rather than being stored on disk as happens with scheduled jobs.

The results of scheduled jobs are persisted to disk. You can view the scheduled jobs that have run, but there are a couple of caveats:

  1. You can only see the jobs that you create and run in your context due to the way scheduled jobs and output is stored in your profile.
  2. You have to explicitly import the PSScheduledJob module into your session to be able to work with scheduled job results.

You can use the job name to discover the completed jobs, as shown here:

Image of command output

You can also use the job type:

Get-Job | where PSJobTypeName -eq PSScheduledJob

You can fetch the data in the job by using Receive-Job:

Receive-Job -Id 6

Notice that I didn’t use the –Keep parameter. This means that the HasMoreData property is set to False:

£> Get-Job -Id 6 | Format-List Id, Name, PSJobTypeName, State, HasMoreData, Location, Command

Id            : 6

Name          : HSG1

PSJobTypeName : PSScheduledJob

State         : Completed

HasMoreData   : False

Location      : localhost

Command       : Get-Process

I had to define the properties in Format-List because Get-Job displays all of the data by default in list view. However, if you open another Windows PowerShell session, import the PSScheduledJob module again, and try Get-Job, you will see that your scheduled job appears to have miraculously regained its data. That’s because the data isn’t actually removed from the job, and when you get the jobs from the store in your new session (for example: C:\Users\Richard\AppData\Local\Microsoft\Windows\PowerShell\ScheduledJobs\HSG1\Output), the information is refreshed.

It’s still advisable to use the –Keep parameter so that you can look at the data again in the same session, if required.

You can see how long your jobs took:

£> Get-Job -Name HSG1 | Format-Table Name, PSBeginTime, PSEndTime -AutoSize

Name PSBeginTime         PSEndTime

---- -----------         ---------

HSG1 25/02/2014 21:00:23 25/02/2014 21:00:25

HSG1 25/02/2014 21:09:56 25/02/2014 21:09:58

HSG1 25/02/2014 21:10:03 25/02/2014 21:10:05

Pick one of the jobs, and drill down into the definition:

£> Get-Job -Id 6 | select -ExpandProperty Definition | Format-List *

InvocationInfo         : Microsoft.PowerShell.ScheduledJob.ScheduledJobInvocationInfo

Definition             : System.Management.Automation.JobDefinition

Options                : Microsoft.PowerShell.ScheduledJob.ScheduledJobOptions

Credential             :

JobTriggers            : {1}

Id                     : 5

GlobalId               : 409993f2-1990-43f8-acf1-3012a89cbde4

Name                   : HSG1

Command                : Get-Process

ExecutionHistoryLength : 32

Enabled                : True

PSExecutionPath        : powershell.exe

PSExecutionArgs        : -NoLogo -NonInteractive -WindowStyle Hidden -Command "Import-Module PSScheduledJob; $jobDef =

                         [Microsoft.PowerShell.ScheduledJob.ScheduledJobDefinition]::LoadFromStore('HSG1',

                         'C:\Users\Richard\AppData\Local\Microsoft\Windows\PowerShell\ScheduledJobs'); $jobDef.Run()"

The last part is interesting, and it shows how the scheduled job is actually run.

By default, Windows PowerShell will keep 32 instances of a scheduled job’s results. When that number is exceeded, it will start deleting the oldest jobs. You can see the number of results that will be kept:

£> Get-ScheduledJob -Name HSG1 | select  -ExpandProperty ExecutionHistoryLength

32

The number can be altered as required:

£> Get-ScheduledJob -Name HSG1 | Set-ScheduledJob -MaxResultCount 50

£> Get-ScheduledJob -Name HSG1 | select  -ExpandProperty ExecutionHistoryLength

50

To remove results:

Get-ScheduledJob -Name HSG1 | Set-ScheduledJob –ClearExecutionHistory

Alternatively, you can use Remove-Job as with other job types.

To delete the scheduled job, you need to unregister it:

Get-ScheduledJob -Name ProcessJob | Unregister-ScheduledJob

If you want a complete clean sweep of the scheduled jobs, use this:

Get-ScheduledJob  | Unregister-ScheduledJob

Scheduled jobs fall between scheduled tasks and Windows PowerShell jobs. Which should you use? It depends on what you are trying to achieve. The great advantage of scheduled jobs is that they automatically call Windows PowerShell for you, and you have the job cmdlets to manage the results. It is especially useful that the data is persisted to disk.

That’s it for today. Tomorrow you’ll learn about using jobs with your Windows PowerShell workflows. Bye for now.

~Richard

Thanks, Richard. I’m looking forward to the remainder of your series.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Viewing all 2129 articles
Browse latest View live