Summary: Richard Siddaway looks at how you can use Windows PowerShell jobs in your enterprise.
Honorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy. This is the seventh, and last, post in a series that, hopefully, will shine the spotlight on Windows PowerShell jobs, remind people of their capabilities, and encourage their greater adoption. The full series comprises:
- Introduction to PowerShell Jobs
- WMI and CIM Jobs
- Remote Jobs
- Scheduled Jobs
- Jobs and Workflows
- Job Processes
- Jobs in the Enterprise (this post)
The focus on this series has been on Windows PowerShell jobs, including learning about the core job cmdlets, using the
–AsJob parameter, running jobs on remote machines, and figuring out how scheduled jobs work. Today we’ll wrap up the series by looking at how you can use jobs in your enterprise. These are the type of scenarios where a Windows PowerShell job is the ideal answer.
One of the big questions you need to be able to answer is why you should use a Windows PowerShell job to accomplish a given task. The following criteria can be used to determine if a job is a suitable way to tackle the issue:
- Task is long running
- Task runs on or against multiple machines simultaneously
- Task is asynchronous
- Task can run in parallel (simultaneously running the same tasks on a number of machines)
- Number of simultaneous running tasks need to be controlled
If your task meets any of these criteria, it is suitable for running as a job. Of course, the task doesn’t have to be run as a job. You could achieve many of these tasks with scripts or workflows, but Windows PowerShell jobs are a viable answer.
A common task in the enterprise is creating or modifying a registry key across multiple machines. It’s so common that we usually include this task in at least one event in the Scripting Games. Working with the registry is a task for WMI. (You didn’t think I wouldn’t also bring it into this post, did you?)
All of the machines in my test environment are running Windows Server 2012 R2 or Windows Server 2012, so I can use the CIM cmdlets rather than the WMI cmdlets. In this example you’ll add a key and a value to the registry.
Adding the key involves these steps:
$hklm = [uint32]2147483650
$key = "SOFTWARE\HSGjobDEMO"
Invoke-CimMethod –ClassName StdRegProv -MethodName CreateKey -Arguments @{hDefKey = $hklm; sSubKeyName = $key}
You need these extra steps to add a value:
$value = "AreYouThere"
$data = "Yes"
Invoke-CimMethod –ClassName StdRegProv -MethodName SetStringValue -Arguments @{hDefKey = $hklm; sValue = $data; sValueName = $value; sSubKeyName = $key}
I like the –Arguments parameter in the Invoke-CimMethod cmdlet. It’s a hash table of parameter names and values, which means that there isn’t any confusion about what you are trying to do. That raises the question, “How can we find the parameter names?”
That’s where Get-CimClass comes in handy:
$class = Get-CimClass -ClassName StdRegProv
$class.CimClassMethods
This enables you to discover the available methods. You can find the parameters for individual methods like this:
$class.CimClassMethods["CreateKey"].Parameters
$class.CimClassMethods["SetStringValue"].Parameters
Get-CimClass is an excellent tool for investigating WMI classes. If you haven’t tested it, I strongly recommend that you do so very soon.
Your task is to run this against a number of remote machines. We’re assuming that Windows PowerShell remoting is enabled throughout your environment. So what’s the best way to run this script against remote machines? You have two main choices:
- Use Invoke-Command for its remoting capabilities together with the –AsJob parameter.
- Use the –ComputerName parameter on Invoke-CimMethod. You will have to manage the computer names that are being passed to Invoke-CimMethod.
I prefer the first approach because it’s less work. Also Invoke-Command is designed to manage the remoting aspects. You will need to put your code into a script block:
$sb =
{
$hklm = [uint32]2147483650
$key = "SOFTWARE\HSGjobDEMO"
Invoke-CimMethod –ClassName StdRegProv -MethodName CreateKey -Arguments @{hDefKey = $hklm; sSubKeyName = $key}
$value = "AreYouThere"
$data = "Yes"
Invoke-CimMethod –ClassName StdRegProv -MethodName SetStringValue -Arguments @{hDefKey = $hklm; sValue = $data; sValueName = $value; sSubKeyName = $key}
}
The next question you need to answer is where is your list of remote machines coming from? Are you going to supply a text file, use a list that you typed by hand, or expect your script to extract the names from Active Directory?
For this exercise, we’ll assume that you have the list of machines in a text file called computers.txt—one machine name per line. I like using text file-based lists because you can maintain a number of files with different sets of computer names, and you can reuse it to group your processing into batches.
You can then use Invoke-Command like this:
Invoke-Command -ScriptBlock $sb -ComputerName (Get-Content computers.txt) -AsJob
One job will be created per computer with a maximum of 32 jobs running simultaneously (remember the –ThrottleLimit parameter).
Note I’ve not tested if the remote machine is contactable, and I haven’t put any error checking into the script. This is deliberate to save space because in many production quality scripts the actual payload code that does the work is a small fraction of the overall script.
You can split jobs into two very broad groups:
- Jobs that return data that you need to look at.
- Jobs that only interest you as successful or failed.
This task actually falls into the first category because you need to look at the returned code from the WMI class methods to determine if the job succeeded. That could be a painful and tedious operation if you have to look at the results of hundreds of jobs.
You need to be able to easily filter where the processing failed. One way is to cause the job to fail if the WMI method doesn’t succeed. You can always tell when a WMI method fails because the return value is non-zero. If you get a non-zero return value, then throw an error, and the job will fail.
Try this test to see what happens when errors are thrown inside jobs:
$sb =
{
param ([boolean]$test)
if ($test) {get-process}
else {throw "wobbly"}
}
Start-Job -ScriptBlock $sb -ArgumentList $true
Start-Job -ScriptBlock $sb -ArgumentList $false
The first job will complete and the second will fail.
Your job now looks like this:
$sb =
{
$hklm = [uint32]2147483650
$key = "SOFTWARE\HSGjobDEMO"
$ret =Invoke-CimMethod –ClassName StdRegProv -MethodName CreateKey -Arguments @{hDefKey = $hklm; sSubKeyName = $key}
if ($ret.ReturnValue -ne 0)
{
Throw "Create Key failed. ReturnValue: $($ret.ReturnValue)"
}
$value = "AreYouThere"
$data = "Yes"
$ret =Invoke-CimMethod –ClassName StdRegProv -MethodName SetStringValue -Arguments @{hDefKey = $hklm; sValue = $data; sValueName = $value; sSubKeyName = $key}
if ($ret.ReturnValue -ne 0)
{
Throw "Set Value failed. ReturnValue: $($ret.ReturnValue)"
}
}
Invoke-Command -ScriptBlock $sb -ComputerName (Get-Content compuetrs.txt) -AsJob
With other non-WMI, actions, you can use Try/Catch blocks to achieve similar results. The important point is that you throw an error when something goes wrong.
Get-Job can easily filter on the state of the job:
Get-Job -State Failed
Get-Job -State Completed
If the job completed, that’s fine. If it didn’t (and hopefully, that’s the minority), you need to do some investigation.
As a final thought, in my workflow series, I showed you how to perform the same task by using workflows: PowerShell Workflows: Design Considerations. One of the great strengths of Windows PowerShell is that there are usually multiple viable ways to solve a task.
This isn’t the end of Windows PowerShell jobs as a topic. The Windows PowerShell eventing engine uses jobs when an action script block is defined. In Windows PowerShell 4.0, Desired State Configuration (DSC) will use jobs for certain tasks. Windows PowerShell jobs are here to stay, and I expect them to appear in more situations in the future.
That’s it for today and for the series. My hope is that you’ve discovered that Windows PowerShell jobs, in their variety of guises, are a powerful tool that can help you perform your long-running admin tasks in an efficient manner.
Bye for now. I’ll be back later in the year with another series that dives into the depths of another Windows PowerShell topic.
~Richard
Richard Siddaway is based out of the UK and spends his time automating anything, and everything, for 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.
Thanks for another great 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