{"id":75391,"date":"2015-11-29T00:01:00","date_gmt":"2015-11-29T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2015\/11\/29\/weekend-scripter-a-look-at-the-poshrsjob-module\/"},"modified":"2019-07-29T15:18:59","modified_gmt":"2019-07-29T23:18:59","slug":"weekend-scripter-a-look-at-the-poshrsjob-module","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/weekend-scripter-a-look-at-the-poshrsjob-module\/","title":{"rendered":"Weekend Scripter: A Look at the PoshRSJob Module"},"content":{"rendered":"<p><b>Summary<\/b>: Boe Prox presents a module for working with runspaces.<\/p>\n<p>Honorary Scripting Guy and Cloud and Datacenter Management MVP, Boe Prox, here today filling in for my good friend, The Scripting Guy. Today I finish up my series about using runspaces in PowerShell by showing you a module that I put together that makes working with runspaces a lot easier.<\/p>\n<p><b>Note<\/b>\u00a0\u00a0\u00a0This is a four-part series that includes the following posts:<\/p>\n<ul>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/scripting\/beginning-use-of-powershell-runspaces\" target=\"_blank\" rel=\"noopener noreferrer\">Beginning Use of PowerShell Runspaces: Part 1<\/a>\u00a0\u00a0\u00a0Begin use with runspaces.<\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/scripting\/beginning-use-of-powershell-runspaces-part-2\/\" target=\"_blank\" rel=\"noopener noreferrer\">Beginning Use of PowerShell Runspaces: Part 2<\/a>\u00a0\u00a0\u00a0Begin use with runspaces.<\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/scripting\/beginning-use-of-powershell-runspaces-part-3\" target=\"_blank\" rel=\"noopener noreferrer\">Beginning Use of PowerShell Runspaces: Part 3<\/a>\u00a0\u00a0Use runspace pools for multithreading.<\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/scripting\/weekend-scripter-a-look-at-the-poshrsjob-module\/\" target=\"_blank\" rel=\"noopener noreferrer\">A Look at the PoshRSJob Module<\/a>\u00a0\u00a0\u00a0Learn about a module for working with runspaces.<\/li>\n<\/ul>\n<p>There are some great scripts out there that provide a tool to work with runspaces, such as <a href=\"https:\/\/github.com\/RamblingCookieMonster\/Invoke-Parallel\" target=\"_blank\" rel=\"noopener noreferrer\">Invoke-Parallel<\/a>. I wanted to come up with something that would provide a familiar approach to spinning off multiple commands in parallel\u2014like what we have with the <b>*-Job<\/b> cmdlets. With that in my mind, I set out to basically copy those cmdlets by using the same naming scheme and parameter names as those cmdlets. But instead of relying on the PSJobs framework, I instead rely on runspaces and runspace pools to provide a way to run commands and other tasks concurrently with the benefits of runspaces.<\/p>\n<p>My module, <b>PoshRSJob<\/b>, does just that, and it is available to download a number of ways. If you have access to the <b>PowerShellGet<\/b> module, you can easily download the module by using <b>Save-Module<\/b>. (This is better for those who want to inspect their downloaded modules before actually installing them.)<\/p>\n<p style=\"margin-left: 30px;\">Save-Module \u2013Name PoshRSJob \u2013Path C:\\ModulesToReview<\/p>\n<p>Or if you want to install it without having to save it first, you can use <b>Install-Module <\/b>instead:<\/p>\n<p style=\"margin-left: 30px;\">Install-Module \u2013Name PoshRSJob<\/p>\n<p>Another option is to head over to my GitHub page,\u00a0<a href=\"https:\/\/github.com\/proxb\/PoshRSJob\" target=\"_blank\" rel=\"noopener noreferrer\">proxb\/PoshRSJob<\/a>,\u00a0and download the zip file. GitHub is where I post my code prior to placing it in the PowerShell Gallery. I am always open to folks who want to make my code better or fix a bug that they may find. so be sure to fork it and submit a pull request if you want to help out.<\/p>\n<p>Now that you have the module available and ready to use, the first thing we should do is look at the available functions that you can use. With the exception of the RS being prepended to each noun, the commands will look pretty familiar to you.<\/p>\n<p style=\"margin-left: 30px;\">Get-Command \u2013Module PoshRSJob<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1_PoshRSJob.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1_PoshRSJob.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>There are a few commands missing (<b>Suspend<\/b>, <b>Resume<\/b>, and <b>Debug<\/b>), but the core commands are here to work with.<\/p>\n<p>I won\u2019t take you through each command and its parameters, but I can assure you that the parameters are almost identical to the ones that you would find using the <b>*-Job<\/b> cmdlets. This is definitely by design because I wanted a familiar approach for running the commands. Even the output of the <b>RSJob<\/b> objects will have a familiar look.<\/p>\n<p>Let\u2019s start with a quick example of how we would use this module. I am going to use a range of numbers and send them to <b>Start-RSJob<\/b> to create a job for each number. One of the most important things that I can stress about doing this is that you should <b>never<\/b> use <b>ForEach<\/b> with <b>Start-RJob<\/b> because it will create a runspace pools for each iteration of <b>ForEach<\/b>.<\/p>\n<p>All of the processing of the objects happens within <b>Start-RSJob<\/b>. You will have to make use of <b>$_<\/b> in your script block to represent each item that is piped to <b>Start-RSJob<\/b>, as shown in the following example:<\/p>\n<p style=\"margin-left: 30px;\">$Test = &#8216;test&#8217;<\/p>\n<p style=\"margin-left: 30px;\">$Something = 1..10<\/p>\n<p style=\"margin-left: 30px;\">1..5|start-rsjob -Name {$_} -ScriptBlock {<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0 [pscustomobject]@{<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Result=($_*2)<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Test=$Using:Test<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Something=$Using:Something<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0 }<\/p>\n<p style=\"margin-left: 30px;\">}<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2_PoshRSJob.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2_PoshRSJob.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>I also have the ability to include the <b>$Using:<\/b> variable, which you may have seen before. This allows you to use a variable from the parent scope within the runspace job so you do not have to worry about messing with a <b>Param()<\/b> block in your script block, and then ensure you have the order correct with the <b>\u2013ArgumentList<\/b> parameter. I will add that you can still do this if you want to because I wanted to keep that familiar functionality available.<\/p>\n<p>Now, to check if they have completed yet:<\/p>\n<p style=\"margin-left: 30px;\">Get-RSJob<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3_PoshRSJob.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3_PoshRSJob.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>Looks like they are not only complete, but they also have data available to retrieve. I can do that by using <b>Receive-RSJob<\/b> and display the output from the <b>RSJobs<\/b>:<\/p>\n<p style=\"margin-left: 30px;\">Get-RSJob | Receive-RSjob<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4_PoshRSJob.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4_PoshRSJob.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>This example isn\u2019t really that impressive, but you can see that all of my jobs completed in the background as runspaces in a runspace pool, and I was able to do all of this by using commands that you are familiar with!<\/p>\n<p>I\u2019ll finish up by removing those <b>RSJobs<\/b> because they are no longer needed\u201d<\/p>\n<p style=\"margin-left: 30px;\">Get-RSJob | Remove-RSJob<\/p>\n<p>The last example might be a little more useful than simply looking at a collection of numbers. The following example also utilizes a custom function of mine that I want to have available within my runspace pool, so it can be used by all of the runspaces.<\/p>\n<p style=\"margin-left: 30px;\">Function Convert-Size {<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0 [cmdletbinding()]<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0 Param (<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [Alias(&#8220;Length&#8221;)]<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [int64]$Size<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0 )<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0 Begin {<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 If (-Not $ConvertSize) {<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Write-Verbose (&#8220;Creating signature from Win32API&#8221;)<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $Signature =\u00a0 @&#8221;<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [DllImport(&#8220;Shlwapi.dll&#8221;, CharSet = CharSet.Auto)]<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 public static extern long StrFormatByteSize( long fileSize, System.Text.StringBuilder buffer, int bufferSize );<\/p>\n<p style=\"margin-left: 30px;\">&#8220;@<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $Global:ConvertSize = Add-Type -Name SizeConverter -MemberDefinition $Signature -PassThru<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Write-Verbose (&#8220;Building buffer for string&#8221;)<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $stringBuilder = New-Object Text.StringBuilder 1024<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0 }<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0 Process {<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Write-Verbose (&#8220;Converting {0} to upper most size&#8221; -f $Size)<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ConvertSize::StrFormatByteSize( $Size, $stringBuilder, $stringBuilder.Capacity ) | Out-Null<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $stringBuilder.ToString()<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0 }<\/p>\n<p style=\"margin-left: 30px;\">}<\/p>\n<p style=\"margin-left: 30px;\">Get-ChildItem $PWD -Directory | Start-RSJob -Name {$_.Name} -ScriptBlock {<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0 [int64]$Bytes = (Get-ChildItem -Path $_.FullName -Force -Recurse | Measure-Object -Property length -Sum).Sum<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0 [pscustomobject]@{<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Name = $_.Name<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Size = Convert-Size -Size $Bytes<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Size_bytes = $Bytes<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0 }<\/p>\n<p style=\"margin-left: 30px;\">\u00a0\u00a0\u00a0 Start-Sleep -Seconds (Get-Random -InputObject (1..5))<\/p>\n<p style=\"margin-left: 30px;\">} -FunctionsToLoad Convert-Size -Throttle 5 | Wait-RSJob -ShowProgress |<\/p>\n<p style=\"margin-left: 30px;\">Receive-RSJob | Sort-Object -Property Size_bytes -Descending<\/p>\n<p style=\"margin-left: 30px;\">Get-RSJob | Remove-RSJob<\/p>\n<p>I also make use of the <b>Wait-RSJob<\/b> function here, which gives me a little progress bar (shown in the following image), so I know the current status of the RSJobs. At the end, I retrieve the data and dispose of the jobs.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5_PoshRSJob.png\"><img decoding=\"async\" title=\"Image of progress bar\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5_PoshRSJob.png\" alt=\"Image of progress bar\" \/><\/a><\/p>\n<p>In addition to being able to import custom functions, you can import modules by using <b>\u2013ModulesToImport<\/b>. You can also work with PSSnapins if you need to import one of those.<\/p>\n<p>If you want to know more about this module, be sure to hit up my GitHub page, <a href=\"https:\/\/github.com\/proxb\/PoshRSJob\" target=\"_blank\" rel=\"noopener noreferrer\">proxb\/PoshRSJob<\/a>. If you want to help make this module even better, be sure to contribute!<\/p>\n<p>I invite you to follow the Scripting Guy on\u00a0<a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\" rel=\"noopener noreferrer\">Twitter<\/a>\u00a0and\u00a0<a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\" rel=\"noopener noreferrer\">Facebook<\/a>. If you have any questions, send email to the Scripting Guy at\u00a0<a href=\"mailto:scripter@microsoft.com\" target=\"_blank\" rel=\"noopener noreferrer\">scripter@microsoft.com<\/a>, or post your questions on the\u00a0<a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\" rel=\"noopener noreferrer\">Official Scripting Guys Forum<\/a>. Until then, see ya!<\/p>\n<p><strong>Boe Prox<\/strong>, Microsoft Cloud and Datacenter Management MVP and Honorary Scripting Guy<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Boe Prox presents a module for working with runspaces. Honorary Scripting Guy and Cloud and Datacenter Management MVP, Boe Prox, here today filling in for my good friend, The Scripting Guy. Today I finish up my series about using runspaces in PowerShell by showing you a module that I put together that makes working [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[162,56,597,650,3,45],"class_list":["post-75391","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-boe-prox","tag-guest-blogger","tag-module","tag-runspace","tag-scripting-guy","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Boe Prox presents a module for working with runspaces. Honorary Scripting Guy and Cloud and Datacenter Management MVP, Boe Prox, here today filling in for my good friend, The Scripting Guy. Today I finish up my series about using runspaces in PowerShell by showing you a module that I put together that makes working [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/75391","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=75391"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/75391\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=75391"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=75391"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=75391"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}