{"id":110,"date":"2015-04-23T00:00:00","date_gmt":"2013-01-01T00:00:00","guid":{"rendered":"https:\/\/www.sqlhammer.com\/blog\/powershell-start-job"},"modified":"2026-03-18T21:51:45","modified_gmt":"2026-03-18T21:51:45","slug":"powershell-start-job","status":"publish","type":"post","link":"https:\/\/sqlhammer.com\/index.php\/2015\/04\/23\/powershell-start-job\/","title":{"rendered":"PowerShell Start-Job"},"content":{"rendered":"<p>The Start-Job Cmdlet is a fast and extremely simple means of spawning asynchronous threads. Why do we care? Because performing parallel work is faster and our time is important. If I want to inventory the Windows services running on every server in my company, I don\u2019t want to wait for each one to return data, one at a time. Before I move on with the <a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/hh849698.aspx\">Start-Job<\/a> Cmdlet, there are two other notable concurrency methods that I won\u2019t be covering in this post; <a href=\"http:\/\/newsqlblog.com\/2012\/05\/22\/concurrency-in-powershell-multi-threading-with-runspaces\/\">runspaces<\/a>, and <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2012\/12\/26\/powershell-workflows-the-basics.aspx\">work-flows<\/a>.<\/p>\n<p>To demonstrate I will use the Windows services task that I mentioned above. Here is how we can find a list of all of the Windows services running on a single server.<\/p>\n<pre><code class=\"language-\">Get-Service | Where-Object { $_.Status -eq 'Running' }<\/code><\/pre>\n<p>The simplest form of this Cmdlet is to pass in a script block.<\/p>\n<pre><code class=\"language-\">$jobs = @();\n$jobs += Start-Job -ScriptBlock <code>\n{ Get-Service | Where-Object { $_.Status -eq 'Running' } }<\/code><\/pre>\n<p>The Start-Job Cmdlet returns a <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/system.management.automation.job%28v=vs.85%29.aspx\">System.Management.Automation.Job<\/a> object which can be used for receiving the job output, waiting for thread completion, and disposing of the thread.<\/p>\n<pre><code class=\"language-\">#wait for all threads in the array to complete and print their output.\n$jobs | Wait-Job | Receive-Job\n#dispose of your jobs\n$jobs | Remove-Job<\/code><\/pre>\n<p>If you would like to pass in parameters to your script block you can create an argument array. In the script block you\u00a0<strong>must\u00a0<\/strong>use the $arg[n] syntax, as shown.<\/p>\n<pre><code class=\"language-\">$args = @()\n$args += 'Running'\n$jobs += Start-Job -ScriptBlock { Get-Service | Where-Object { $_.Status -eq \"$($args[0])\" } } -ArgumentList $args<\/code><\/pre>\n<p>Finally, I will demonstrate how to initialize the job with a script. This is often used to pre-load functions to be used in the script block. Let us say that I\u2019ve created a function and saved it as a scriptblock.<\/p>\n<pre><code class=\"language-\">[scriptblock]$func = {\nfunction Get-RunningService\n{\n    Get-Service | Where-Object { $_.Status -eq \"Running\" }\n}\n}<\/code><\/pre>\n<p>Then we can create our job like so.<\/p>\n<pre><code class=\"language-\">$jobs += Start-Job -ScriptBlock { Get-RunningService } <\/code>\n    -InitializationScript $func<\/code><\/pre>\n<p>When we bring this all together we have an easy to remember asynchronous process.<\/p>\n<pre><code class=\"language-\">$jobs = @();\n$sb = [scriptblock] { write-host \"args = $($args[0])\"; Invoke-Command -ComputerName ($args[0]) -ScriptBlock { Get-RunningService } }\n\n<p>foreach ($svr in ($servers.Name))\n{\n    $args = @($svr);\n    $jobs += Start-Job -ScriptBlock $sb <code>\n         -InitializationScript $func <\/code>\n         -ArgumentList $args\n}\n$jobs | Wait-Job | Receive-Job\n$jobs | Remove-Job<\/code><\/pre>\n<\/p>\n<hr \/>\n<p><img decoding=\"async\" src=\"\/wp-content\/uploads\/hammer-derik-172x140.png\" alt=\"\" \/><strong>Written by Derik Hammer of <a href=\"http:\/\/www.sqlhammer.com\/\">SQL Hammer<\/a><\/strong><\/p>\n<p>Derik is a data professional focusing on Microsoft SQL Server. His passion focuses around <a href=\"http:\/\/www.sqlhammer.com\/blog\/category\/high-availability\/\">high-availability<\/a>, <a href=\"http:\/\/www.sqlhammer.com\/blog\/category\/general\/disaster-recovery\/\">disaster recovery<\/a>, continuous integration, and automated maintenance. his experience has spanned long-term database administration, consulting, and\u00a0entrepreneurial ventures.<\/p>\n<p>Derik gives the SQL community credit for plugging the gaps in his knowledge when he was a junior DBA and, now that his skills have matured, started <a href=\"http:\/\/www.sqlhammer.com\/\">SQLHammer.com<\/a> as one small way to give back and continue the cycle of shared learning.<\/p>\n<p>Derik is the owner and lead author of SQL Hammer, a <a href=\"http:\/\/www.sqlhammer.com\/\">Microsoft SQL Server resource<\/a>.<\/p>\n<p>For more information, visit <a href=\"http:\/\/www.sqlhammer.com\/\">http:\/\/www.sqlhammer.com<\/a>. Follow Derik on <a href=\"http:\/\/twitter.com\/SQLHammer\">Twitter<\/a> for <strong>SQL tips and chat<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Start-Job Cmdlet is a fast and extremely simple means of spawning asynchronous threads. Why do we care? Because performing parallel work is faster and our time is important. If I want to inventory the Windows services running on every server in my company, I don\u2019t want to wait for each one to return data, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":530,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-110","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/posts\/110","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/comments?post=110"}],"version-history":[{"count":1,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/posts\/110\/revisions"}],"predecessor-version":[{"id":372,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/posts\/110\/revisions\/372"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/media\/530"}],"wp:attachment":[{"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/media?parent=110"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/categories?post=110"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/tags?post=110"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}