PowerShell Workflows: Restarting the Computer

PowerShell Workflows: Restarting the Computer

  • Comments 10
  • Likes

Summary: Windows PowerShell MVP Richard Siddaway continues with part five of his awesome Windows PowerShell workflow basics articles.

Microsoft Scripting Guy, Ed Wilson, is here. Today, we have the fifth in a series of guest blog posts written by Windows PowerShell MVP and Honorary Scripting Guy Richard Siddaway dealing with Windows PowerShell workflow.

Note   The first article, PowerShell Workflows: The Basics, introduced the basic concepts of Windows PowerShell workflow. The second article, PowerShell Workflows: Restrictions, discussed the restrictions encountered with working with Windows PowerShell workflows. The third article was PowerShell Workflows: Nesting. The fourth article talked about PowerShell Workflows: Job Engine. You should read those articles prior to reading today’s article.

Richard has written a number of guest Hey, Scripting Guy! Blog posts, and he has also written two books on Windows PowerShell. His most written book, PowerShell in Depth, is co-written with fellow MVPs Don Jones and Jeffrey Hicks.

Now, take it away, Richard …

You saw in the last article that Windows PowerShell workflows can survive being stopped and restarted. In certain circumstances, workflows can also survive the Windows PowerShell session being terminated. In this post, you will discover how workflows can survive a reboot.

A computer needs to reboot for a number of activities, for instance:

  • Renaming the computer
  • Joining or leaving a domain
  • Some software installs or updates
  • Triggering of a Chkdsk on a system drive

It is possible to perform these activities as a series of steps separated by a reboot. Workflows enable you to set the process off and forget about it until it has finished the processing-reboot-processing cycle.

Consider the following three scenarios:

  1. Restarting a remote computer against which you are running a workflow.
  2. Restarting the computer running the workflow and manually restarting the workflow.
  3. Restarting the computer running the workflow and automatically restarting the workflow.

The first case is the easiest, so that’s where we’ll start.

Restarting a remote computer in a workflow

To keep the examples simple—so you can concentrate on how the workflows deal with the restarts—the following basic workflow forms the basis of the example:

workflow test-restart {

 param ([string[]]$computernames)

 foreach -parallel ($computer in $computernames) {

   Get-WmiObject -Class Win32_ComputerSystem -PSComputerName $computer

   Get-WmiObject -Class Win32_OperatingSystem -PSComputerName $computer

 }

}

All it does is get the computer system and operating system information via WMI calls. The WMI cmdlets are used rather than the newer CIM cmdlets so that WSMAN versions don’t become a factor. Two remote computers will be used in the test:

  • W12standard – Windows Server 2012 (Windows PowerShell 3.0)
  • WebR201 – Windows Server 2008 R2 (Windows PowerShell 2.0)

The workflow is used like this:

PS> test-restart -computernames w12standard, webr201

In this case, I’m explicitly telling the activities to access a remote computer. This isn’t the only way to achieve this. You’ll learn more about using workflow parameters in the next article.

The remote computers are accessed in parallel with a sequence of two WMI calls applied to each computer. The commands within the foreach block are processed in sequence, but the computers are processed in parallel.

Now you can add the logic to restart the computers.

workflow test-restart {

 param ([string[]]$computernames)

 foreach -parallel ($computer in $computernames) {

   Get-WmiObject -Class Win32_ComputerSystem -PSComputerName $computer

   Restart-Computer -Wait -PSComputerName $computer

   Get-WmiObject -Class Win32_OperatingSystem -PSComputerName $computer

 }

}

The work of restarting the remote computers is done by this line:

Restart-Computer -Wait -PSComputerName $computer

The important parameter is –Wait. It tells the workflow to wait until the remote computer has restarted before progressing to the next command.

If you run this in Windows PowerShell Integrated Scripting Environment (ISE) or the Windows PowerShell console, you will see a progress bar with messages informing you of the stages of the restart. These include:

  • Waiting for restart to begin
  • Verifying computer has restarted
  • Waiting for WMI connectivity
  • Waiting for Windows PowerShell connectivity
  • Waiting for WinRM connectivity

In my example, Windows Server 2012 restarts a lot faster than Windows Server 2008 R2.

In the workflow topic Restarting the Computer in a Workflow, the following code can be found:

Restart-Computer -Wait -PSConnectionRetryCount 4 -PSConnectionRetryInterval 5

Note   The PSConnectionRetryCount and PSConnectionRetryInterval parameters do not appear to be recognized as workflow activity parameters.

You can always run the workflow as a Windows PowerShell job:

PS> test-restart -computernames w12standard, webr201 -AsJob

Id     Name            PSJobTypeName   State         HasMoreData     Location           

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

25     Job25           PSWorkflowJob   Running       True            localhost       

After the job has finished, you can examine the data. All you get is the returned WMI data. The progress messages during the reboots will be supressed.

Restarting the computer running the workflow

Two options exist here:

  • Restart the workflow manually
  • Restart the workflow automatically

Let’s start with a manual restart.

Manual restart of workflow

Running a workflow that reboots the computer it’s running on and then manually restarting the workflow is not an optimal solution—ideally, you want the computer to do all the work. But it involves less coding and is a simpler solution. There are times when quick and simple gets the job done. This approach also forms the basis for starting the workflow automatically.

In this example, I’m going to show how to use the new Rename-Computer cmdlet (workflow activity). Renaming a computer running Windows is one of the examples that requires a reboot.

workflow rename-localsystem {

param (

 [string]$newname

)

Rename-Computer -Newname $newname -Force -Passthru

 

Restart-Computer -Wait

Get-CimInstance -ClassName Win32_ComputerSystem |

Select-Object -ExpandProperty Name |

Set-Content -Path "C:\Scripts\$newname.txt"

}

The workflow takes the new computer name as a parameter. It uses that name to perform the rename, and then uses Restart-Computer with the –Wait parameter. After the reboot, the name of the computer is retrieved and put into a text file of that name.

You can start by manually displaying the current computer name. In this case, it is TEST1NOJOBPARAM. It’s a name the system ended up with from some previous tests.

PS C:\scripts> $env:COMPUTERNAME

TEST1NOJOBPARAM

PS C:\scripts> rename-localsystem -newname W12SUS

You can then run the workflow supplying the new name. Notice that the –AsJob parameter hasn’t been used. The rename occurs and the system reboots.

After the reboot, you can connect to the computer and test for the file.

PS C:\scripts> ls

    Directory: C:\scripts

Mode                LastWriteTime     Length Name                                                                   

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

d----        14/12/2012     18:45            Setup                                                                  

-a---        02/01/2013     11:58        280 rename-localsystem.ps1                                                 

 

The file hasn’t been produced. A suspended job exists, as shown below.

PS C:\scripts> Import-Module PSWorkflow

PS C:\scripts> Get-Job | Format-Table -AutoSize

Id Name PSJobTypeName State     HasMoreData Location  Command          

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

3  Job2 PSWorkflowJob Suspended True        localhost rename-localsystem

Resume the job. The job finishes and the file is produced. As a final test, you can check the environmental variable for the computer name.

PS C:\scripts> ls

    Directory: C:\scripts

Mode                LastWriteTime     Length Name                                                                   

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

d----        14/12/2012     18:45            Setup                                                                  

-a---        02/01/2013     11:58        280 rename-localsystem.ps1                                                 

-a---        02/01/2013     12:24          8 W12SUS.txt                                                             

 

PS C:\scripts> $env:COMPUTERNAME

W12SUS

There will be times when you need to restart the computer, but the workflow terminates at that point. This is a perfectly legitimate scenario, as shown in this example:

workflow add-localsystemtodomain{

 param (

  $domcred,

  [string]$ipv4address

 )

$index = Get-NetIPInterface -AddressFamily IPv4 -Dhcp Enabled |

 Select-Object -ExpandProperty  ifIndex

New-NetIPAddress -InterfaceIndex $index -AddressFamily IPv4 `

 -IPAddress $ipv4address -PrefixLength 24

 Set-DnsClientServerAddress -InterfaceIndex $index -ServerAddresses "10.10.54.201"

 Set-DnsClient -InterfaceIndex $index -ConnectionSpecificSuffix "manticore.org"

Add-Computer -Credential $domcred -DomainName Manticore `

 -OUPath "OU=Security Servers,OU=Servers,DC=Manticore,DC=org" -Force

Restart-Computer 

}

## remove relicit jobs

Get-Job |

where PSJobTypeName -eq "PSWorkflowJob" |

Remove-Job

$cred = Get-Credential

add-localsystemtodomain -domcred $cred -ipv4address "10.10.54.170" -JobName AddToDomain

The new networking cmdlets are used to set the static IP address of the virtual machine’s network adapter. The DNS servers and connection suffix are also set. The workflow adds the computer to the domain and then reboots.

The script cleans up any workflow-related jobs, asks for the credentials used to join the domain, and then fires the workflow. The –Wait parameter isn’t used on Restart-Computer, so a Windows PowerShell job isn’t produced and no further processing occurs.

What about a situation where you want to control the restarting of the workflow, but then let the system take care of it?

Automatic restart of workflow

The online documentation I have seen states that a Windows PowerShell scheduled job can be used to perform this task. I had zero success with that approach, so I switched to using a scheduled task. Windows PowerShell 3.0 introduced a set of cmdlets for working with scheduled tasks. These are WMI-based and are only available on Windows 8 and Windows Server 2012. It would be possible to duplicate this functionality by using the COM interface to the task scheduler, but that is a task for another day.

I went for a very simple approach while testing this because of the problems I was having, but there is enough here to show you the concept. Let’s start with a workflow:

workflow test-restart {

 Get-WmiObject -Class Win32_ComputerSystem | Out-File -FilePath C:\Reports\comp.txt

 Get-ChildItem -Path C:\Reports | Out-File -FilePath C:\dir.txt

 Restart-Computer -Wait

 Get-WmiObject -Class Win32_OperatingSystem | Out-File -FilePath C:\Reports\os.txt

}

The workflow uses Get-WmiObject to get the Win32_ComputerSystem class and creates a file containing the data. Get-ChildItem produces a listing of the folder containing the report. This tests that the first step in the workflow was performed.

Restart-Computer is used with the –Wait parameter to produce a suspended workflow job and the computer reboots.

After the system restarts, the workflow is resumed by a scheduled task, and the last step of producing a file containing data from the Win32_OperatingSystem class is performed.

Next step is to create the scheduled task:

$actionscript = '-NonInteractive -WindowStyle Normal -NoLogo -NoProfile -NoExit -Command "&''c:\reports\test-resume.ps1''"'

$pstart =  "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"

Get-ScheduledTask -TaskName Test | Unregister-ScheduledTask -Confirm:$false

$act = New-ScheduledTaskAction -Execute $pstart -Argument $actionscript

$trig = New-ScheduledTaskTrigger -AtLogOn

Register-ScheduledTask -TaskName Test -Action $act -Trigger $trig -RunLevel Highest

I want my scheduled task to run Windows PowerShell, so I define the starting parameters for Windows PowerShell in the $actionscript variable. $pstart contains the command to start Windows PowerShell.

The next line removes any instances of the scheduled task.

The task action is defined by using New-ScheduledTaskAction. A trigger is defined to control when the task fires. I’ve set this to be when I log on, because I wanted to see the task run. Change –AtLogon to –AtStartUp, if required.

Register the task data to create the task. Ensure that –RunLevel Highest is included, so your task runs with elevated privileges.

The last bit of the puzzle is the script that the scheduled task calls:

Get-ChildItem -Path C:\Reports | Out-File -FilePath C:\Reports\dir.txt

Import-Module PSWorkflow

Get-Job | Resume-Job

This creates another directory listing, imports the workflow module, and then resumes the suspended job. You can filter on the job name if you might have more than one suspended job on the system you are restarting.

After the computer has restated, and you’ve logged on, you’ll see a Windows PowerShell window open and the workflow job will continue.

One oddity is that the timestamp on the os.txt file matches that of the comp.txt file! The os.txt file is definitely produced after the reboot though.

Clean up the job in the usual way and remove the scheduled task

Get-ScheduledTask -TaskName Test | Unregister-ScheduledTask -Confirm:$false

This doesn’t seem to be an elegant solution, but it works. I’ll keep looking at the scheduled job option and blog about it if I get it working.

Gotchas

One very big gotcha that I’ve discovered testing this functionality is that you shouldn’t run workflows through ISE.

According to the documentation, you can run a workflow like this:

workflow test-restart {

 Get-WmiObject -Class Win32_ComputerSystem

 Restart-Computer -Wait

 Get-WmiObject -Class Win32_OperatingSystem

}

If you run it through ISE, you won’t get a suspended job—it’ll carry on to completion. Run it through the console and everything works as expected. Many thanks to Steven Murawski for helping me track that one down.

The other thing to remember is to save any output to disk or you will lose it on reboot.

Conclusion

After working through these scenarios, I’ve come to the conclusion that running the workflows against a remote computer when you need a restart is by far the easiest way to do it.  Rebooting the local computer during a workflow is possible, but it can involve a lot of head-scratching to get it working the way you want.

~Richard

Thank you, Richard—another great job.

Join me 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

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi Richard,

    another awesome continuation of the workflow series!

    Great to know what works and even better to know what doesn't work as expected!!

    Thank you!!! I'm awaiting the next part ( unpatiently ... :-)

    Klaus

  • nice post on powershell workflows restarting the computers thanks for posting.

  • IT IS REALLY VERY HELPFUL FOR ME.I LIKE YOUR POST BECAUSE IT IS VERY USEFUL FOR ME AS WELL. HOPING THE SAME FINEST OPERATE IN THE UP COMING DAYS ALSO. THANK YOU!

  • I’m still learning from you, but I’m trying to achieve my goals. I certainly love reading everything that is posted on your websiteand it helped me as well.keep it up.

  • Hi Richard,

    I would like to restart a number of servers at a specific time of the day, say 5 am.  How can I do that with Restart-Computer?

  • There is something a little troubling about MSFT saying "The online documentation I have seen states that a Windows PowerShell scheduled job can be used to perform this task. I had zero success with that approach, so I switched to using a scheduled task." about their own product.

    I also, am unable to get workflow to resume after reboots, even after trying the documented techniques. What gives?

  • @anon

    THe workflow works.

    Restart-Computer does not require a workflow.  It will give uyou messages telling you why it cannot resume correctly if run outsideof a aworkflow.

    The two most common issues are firewall and no WInRM on remote machine.

    Try it like this:

    Restart-Computer -computer somepc -force -wait -for WMI

    This will cause it to wait only on WMI to detect a reconnect capability.  

  • Thanks ---- I want to have a machine reboot itself and then resume. The documentation's method of using ScheduledJobs (as stated in this post) doesn't work. Running Get-Job inside the scheduled tasks gives no jobs back. Yes I've tried importing the scheduled jobs module. :(