{"id":1862,"date":"2014-03-07T00:01:00","date_gmt":"2014-03-07T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2014\/03\/07\/powershell-jobs-week-job-processes\/"},"modified":"2014-03-07T00:01:00","modified_gmt":"2014-03-07T00:01:00","slug":"powershell-jobs-week-job-processes","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/powershell-jobs-week-job-processes\/","title":{"rendered":"PowerShell Jobs Week: Job Processes"},"content":{"rendered":"<p><b>Summary<\/b>: Richard Siddaway looks at how Windows PowerShell jobs actually run.\nHonorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy. This is the sixth 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:<\/p>\n<ol>\n<li><a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2014\/03\/02\/powershell-jobs-week-introduction-to-powershell-jobs.aspx\" target=\"_blank\">Introduction to PowerShell Jobs<\/a><\/li>\n<li><a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2014\/03\/03\/powershell-jobs-week-wmi-and-cim-jobs.aspx\" target=\"_blank\">WMI and CIM Jobs<\/a><\/li>\n<li><a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2014\/03\/04\/powershell-jobs-week-remote-jobs.aspx\" target=\"_blank\">Remote Jobs<\/a><\/li>\n<li><a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2014\/03\/05\/powershell-jobs-week-scheduled-jobs.aspx\" target=\"_blank\">Scheduled Jobs<\/a><\/li>\n<li><a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2014\/03\/06\/powershell-jobs-week-jobs-and-workflows.aspx\" target=\"_blank\">Jobs and Workflows<\/a><\/li>\n<li>Job Processes (this post)<\/li>\n<li>Jobs in the Enterprise<\/li>\n<\/ol>\n<p>The focus on this series has been on Windows PowerShell jobs, including learning about the core job cmdlets, using the <br \/><b>&ndash;AsJob<\/b> parameter, running jobs on remote machines, and figuring out how scheduled jobs work. Today, we are going to go under the hood a bit more and look at how jobs actually run.\nI&rsquo;ve stated in passing several times in this series that jobs don&rsquo;t run in the context of your current Windows PowerShell session&mdash;whether that&rsquo;s the Windows PowerShell console, ISE, or another Windows PowerShell host. Try this:<\/p>\n<p style=\"margin-left:30px\">$proc = &#8220;p*&#8221;<\/p>\n<p style=\"margin-left:30px\">Get-Process -Name $proc\nThe commands will run and you will see list of processes with names that start with the letter &ldquo;p.&rdquo; In my environment, I got a single instance of the Windows PowerShell process. Now try this:<\/p>\n<p style=\"margin-left:30px\">Start-Job -ScriptBlock {Get-Process -Name $proc}\nYour job will start and apparently complete. However, when you look at the data from the job, you&rsquo;ll get something like this:<\/p>\n<p style=\"margin-left:30px\">&pound;&gt; Receive-Job -Id 4\nCannot validate argument on parameter &#8216;Name&#8217;. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; + CategoryInfo&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : InvalidData: (:) [Get-Process], ParameterBindingValidationException<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetProcessCommand<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; + PSComputerName&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : localhost\nThe error message is telling you in a very roundabout way that the job didn&rsquo;t know anything about the <b>$proc<\/b> variable. If you want your job to be able to use the contents of the <b>$pr<\/b>oc variable, you need to pass it into the job:<\/p>\n<p style=\"margin-left:30px\">Start-Job -ScriptBlock {param ([string]$proc ) Get-Process -Name $proc} -ArgumentList $proc\nA <b>param<\/b> block has been added to the script block defining the <b>proc<\/b> parameter. The <b>&ndash;ArgumentList<\/b> parameter of <b>Start-Job<\/b> is used to pass the parameter. If you need to pass multiple parameters into the script block, list them in the order they are defined. You don&rsquo;t have to use the same name for the variable that is associated with the script block&rsquo;s parameter inside and outside the script block. I&rsquo;ve just done it to reduce confusion.\nThe job will run, and when you use <b>Receive-Job<\/b>, you will receive the expected results. Or are they what you expected?\nWhen I ran <b>Get-Process<\/b> at the beginning of this explanation, I deliberately chose <b>&ldquo;p*&rdquo;<\/b> so that I got the Windows PowerShell process (I&rsquo;m running all of this in a Windows PowerShell console). I got one Windows PowerShell process. When I ran the second job (the one that worked), I got two Windows PowerShell processes. But if I run <b>Get-Process<\/b> now, I&rsquo;ll only see my original Windows PowerShell process.\nThe extra Windows PowerShell process appeared because a standard background job spawns a new, separate Windows PowerShell process in which it runs the script block. That&rsquo;s why the variable we defined couldn&rsquo;t be found when we ran the first job. The work is being done in a separate instance of PowerShell that is brand new and clean.<\/p>\n<p style=\"margin-left:30px\"><b>Note<\/b>&nbsp; Your profile won&rsquo;t be run in the Windows PowerShell instance that the job starts, so you won&rsquo;t have any aliases or functions that you define in your profile. You won&rsquo;t have any snap-ins or modules that your profile loads available either. You&rsquo;ll have to load them within your job&rsquo;s script block.\nYou&rsquo;ve now discovered why you get the Windows PowerShell prompt back as soon as your job starts&mdash;the work is being done in separate process so the Windows PowerShell job engine can give back the prompt.\nDoes that mean that we know how all jobs run? Unfortunately, not! Each job type has its own behavior pattern as far the process, or processes, it requires.\nThe easiest way to dig into this is to open three Windows PowerShell consoles.&nbsp; In the first one, run <b>Get-Process<\/b> and keep the results visible. In the second, run:<\/p>\n<p style=\"margin-left:30px\">Get-WmiObject -Class Cim_datafile &ndash;AsJob\nThat will create a very long running WMI job.\nIn your third Windows PowerShell console, run <b>Get-Process<\/b> again. Compare the results, and you will discover a process called <b>unsecapp<\/b> is now running:<\/p>\n<p style=\"margin-left:30px\">Get-Process unsecapp | fl *\nIt shows the following details. (I&rsquo;m picking out only the interesting bits):<\/p>\n<p style=\"margin-left:30px\">Path&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : C:windowssystem32wbemunsecapp.exe<\/p>\n<p style=\"margin-left:30px\">Company&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Microsoft Corporation<\/p>\n<p style=\"margin-left:30px\">Description&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Sink to receive asynchronous callbacks for WMI client application\nThe wbem folder is where Windows keeps all of the WMI related tools.\nStop and remove the job with:<\/p>\n<p style=\"margin-left:30px\">Get-Job | Stop-Job<\/p>\n<p style=\"margin-left:30px\">Get-Job | Remove-Job\nIf you wait a little while and then run&hellip;<\/p>\n<p style=\"margin-left:30px\">Get-Process u*\n&hellip;You&rsquo;ll see that unsecapp.exe has closed.\nIf you repeat the exercise, but use <b>Get-CimInstance -ClassName CIM_datafile &ndash;AsJob<\/b>, you can discover where CIM jobs run.\nOh. You can&rsquo;t because the CIM cmdlets don&rsquo;t have an<b> &ndash;AsJob <\/b>parameter. If you want to run the CIM cmdlets as a job, you have to use <b>Start-Job<\/b>. If you try that experiment, you&rsquo;ll see that another instance of Windows PowerShell is started for the background job as you would expect.\nCommands that are created by using CDXML get an <b>&ndash;AsJob<\/b> parameter added automatically. Create a simple CDXML module by using the <b>CIM_Datafile<\/b> class:<\/p>\n<p style=\"margin-left:30px\">&lt;?xml version=&#8217;1.0&#8242; encoding=&#8217;utf-8&#8242;?&gt;<\/p>\n<p style=\"margin-left:30px\">&lt;PowerShellMetadata xmlns=&#8217;http:\/\/schemas.microsoft.com\/cmdlets-over-objects\/2009\/11&#8242;&gt;<\/p>\n<p style=\"margin-left:30px\">&nbsp; &lt;Class ClassName=&#8217;ROOTcimv2Cim_DataFile&#8217;&gt;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; &lt;Version&gt;1.0&lt;\/Version&gt;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; &lt;DefaultNoun&gt;CimDataFile&lt;\/DefaultNoun&gt;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; &lt;InstanceCmdlets&gt;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;GetCmdletParameters DefaultCmdletParameterSet=&#8217;DefaultSet&#8217;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/GetCmdletParameters&gt;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; &lt;\/InstanceCmdlets&gt;&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp; &lt;\/Class&gt;&nbsp;<\/p>\n<p style=\"margin-left:30px\">&lt;\/PowerShellMetadata&gt;\nNow save it as datafile.cdxml.\nImport the module and run the command:<\/p>\n<p style=\"margin-left:30px\">Import-Module .datafile.cdxml<\/p>\n<p style=\"margin-left:30px\">Get-CimDataFile &ndash;AsJob\nYou will see a job of type <b>CimJob<\/b> running. Testing the process as you did previously doesn&rsquo;t highlight any obvious new processes. So we have to assume that CIM jobs run in an existing process.\nYou will have a similar experience if you try to track down where workflow jobs run. There is no obvious new process started.\nYou&rsquo;ve seen other job types: <b>RemoteJobs<\/b> and <b>ScheduledJobs<\/b>.\nIf we try our experiment on these, you can simulate a remote job like this:<\/p>\n<p style=\"margin-left:30px\">Invoke-Command -ComputerName $env:COMPUTERNAME -ScriptBlock {while($true){sleep -Seconds 10}} &ndash;AsJob\nLooking at the running processes, you will discover the <b>wsmprovhost<\/b> process:<\/p>\n<p style=\"margin-left:30px\">Get-Process wsmprovhost | fl *\nIt shows these details:<\/p>\n<p style=\"margin-left:30px\">Path&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : C:windowssystem32wsmprovhost.exe<\/p>\n<p style=\"margin-left:30px\">Company&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Microsoft Corporation<\/p>\n<p style=\"margin-left:30px\">Description &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;: Host process for WinRM plug-ins\nThat seems sensible because we&rsquo;re dealing with remoting.\nScheduled jobs are interesting.&nbsp; If you look at the definition of the scheduled task associated with the scheduled job, you&rsquo;ll see:<\/p>\n<p style=\"margin-left:30px\">powershell.exe -NoLogo -NonInteractive -WindowStyle Hidden -Command &#8220;Import-Module PSScheduledJob; $jobDef = [Microsoft.PowerShell.ScheduledJob.ScheduledJobDefinition]::LoadFromStore(&#8216;MyLongJob&#8217;, &#8216;C:UsersRichardAppDataLocalMicrosoftWindowsPowerShellScheduledJobs&#8217;); $jobDef.Run()&#8221;\nThat means you&rsquo;re starting Windows PowerShell and then starting a job from that Windows PowerShell process, which starts another Windows PowerShell process&mdash;for a total of two new Windows PowerShell processes.\nYou now know that a Windows PowerShell job creates at least one new process in which to run. Each job will create a new process. If you start 30 long-running standard background jobs, you&rsquo;ll get 30 new instances of Windows PowerShell. This could have a serious impact on your admin machine, depending on its specification.\nYou need to think about how many jobs you start at once. Cmdlets such as <b>Invoke-Command<\/b> and <b>Get-WmiObject<\/b>, which have an <b>&ndash;AsJob<\/b> parameter, also have a <b>&ndash;ThrottleLimit<\/b> parameter. This accepts an integer value and controls the number of jobs that can be run simultaneously from that command.&nbsp;\nNow run:<\/p>\n<p style=\"margin-left:30px\">Invoke-Command &ndash;ComputerName (get-content computers.txt) &ndash;ScriptBlock {get-process} &ndash;AsJob\nThe computers.txt file contains the names of 200 computers, and you don&rsquo;t want the command starting 200 jobs simultaneously. The <b>&ndash;ThrottleLimit<\/b> parameter defaults to 32, so you&rsquo;ll only get 32 jobs started, and one has to finish before the next one starts.\nBut that&rsquo;s not 32 running jobs in total on the machine. Its 32 running jobs from that one command. If you have jobs started in other Windows PowerShell sessions, or you started long-running jobs before you used <b>Invoke-Command<\/b>, you could have many more jobs running&mdash;all taking resources.\nYou can use the <b>&ndash;ThrottleLimit<\/b> parameter to control the number of jobs that start at one time, but don&rsquo;t make the number too high because your machine will run out of resources and crash.\nThat&rsquo;s it for today. Tomorrow the series concludes with a look at how and when you can use jobs in your enterprise to the best advantage. Bye for now.\n~Richard\nThanks, Richard.\nI invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"http:\/\/blogs.technet.commailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.\n<b>Ed Wilson, Microsoft Scripting Guy<\/b>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Richard Siddaway looks at how Windows PowerShell jobs actually run. Honorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy. This is the sixth in a series of posts that, hopefully, will shine the spotlight on Windows PowerShell jobs, remind people of their capabilities, and encourage their greater [&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":[193,189,4,45],"class_list":["post-1862","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-background-jobs","tag-richard-siddaway","tag-scripting-techniques","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Richard Siddaway looks at how Windows PowerShell jobs actually run. Honorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy. This is the sixth in a series of posts that, hopefully, will shine the spotlight on Windows PowerShell jobs, remind people of their capabilities, and encourage their greater [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/1862","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=1862"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/1862\/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=1862"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=1862"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=1862"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}