{"id":6840,"date":"2019-10-07T14:58:32","date_gmt":"2019-10-07T18:58:32","guid":{"rendered":"https:\/\/jdhitsolutions.com\/blog\/?p=6840"},"modified":"2019-10-07T14:58:40","modified_gmt":"2019-10-07T18:58:40","slug":"making-sense-of-parallel-foreach-object-in-powershell-7","status":"publish","type":"post","link":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/","title":{"rendered":"Making Sense of Parallel ForEach-Object in PowerShell 7"},"content":{"rendered":"<p>By, you've most likely heard about the new -parallel parameter with <a title=\"Read online help for the v7 version of this command\" href=\"https:\/\/docs.microsoft.com\/en-us\/powershell\/module\/Microsoft.PowerShell.Core\/ForEach-Object?view=powershell-7\" target=\"_blank\" rel=\"noopener noreferrer\">ForEach-Object<\/a> in the latest preview of <a title=\"Get PowerShell 7 Preview 4\" href=\"https:\/\/github.com\/PowerShell\/PowerShell\/releases\/tag\/v7.0.0-preview.4\" target=\"_blank\" rel=\"noopener noreferrer\">PowerShell 7<\/a> Personally. I've been waiting for this for a long time. I used to only use PowerShell workflows because it offered a way to run commands in parallel. Having this feature as part of the language is a welcome addition. But this isn't magic and there are real-world consequences when you use it. The -Parallel parameter will spin up a collection of runspaces and run your scriptblock in each one. Running something in parallel does not mean in order.<\/p>\n<p>Here's an example of the new parameter in action.<\/p>\n<p><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/10\/image.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1028\" height=\"713\" title=\"image\" style=\"margin: 0px; display: inline; background-image: none;\" alt=\"image\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/10\/image_thumb.png\" border=\"0\"><\/a><\/p>\n<p>I introduced a random sleep interval to help demonstrate that the commands are running in parallel. The result is written to the pipeline as each parallel operation completes. Let's dig into this a bit deeper.<\/p>\n<h2>Straight Pipeline<\/h2>\n<p>Here is a straight forward pipeline example using <a title=\"Read online help for this command\" href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113300\" target=\"_blank\" rel=\"noopener noreferrer\">ForEach-Object<\/a>.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Measure-Command {\n    1..5000 | ForEach-Object { [math]::Sqrt($_) * 2 }\n}\n<\/pre>\n<p>This took me 34ms to complete. Here's the same command but run using -parallel.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Measure-Command {\n    1..5000 | ForEach-Object -parallel { [math]::Sqrt($_) * 2 }\n}\n<\/pre>\n<p>This took 89 <strong><em>seconds<\/em><\/strong>. Clearly, there is overhead involved when using this parameter.<\/p>\n<p>However, there is potential room for improvement.&nbsp; By default the command spins up 5 runspaces at a time. You can increase or decrease this with the -ThrottleLimit parameter.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Measure-Command {\n    1..5000 | ForEach-Object -parallel { [math]::Sqrt($_) * 2 } -throttlelimit 25\n}\n<\/pre>\n<p>This version finished in almost 5 seconds! By comparison, using the ForEach enumerator:<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Measure-Command {\n    foreach ($x in (1..5000)) { [math]::Sqrt($x) * 2 }\n}\n<\/pre>\n<p>takes about 7 ms.<\/p>\n<h2>Running Scriptblocks<\/h2>\n<p>Let's increase the complexity and do something a bit more practical. Here is a scriptblock that will give me an object showing file usage in a given folder.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">$t = {\n    param ([string]$Path)\n    Get-ChildItem -path $path -recurse -file -erroraction SilentlyContinue |\n        Measure-Object -sum -Property Length -Maximum -Minimum |\n        Select-Object @{Name = \"Computername\"; Expression = { $env:COMPUTERNAME } },\n        @{Name = \"Path\"; Expression = { Convert-Path $path } },\n        Count, Sum, Maximum, Minimum\n}\n\nMeasure-Command {\n    \"c:\\work\", \"c:\\scripts\", \"d:\\temp\", \"C:\\users\\jeff\\Documents\", \"c:\\windows\" | \n    ForEach-Object -process { Invoke-Command -scriptblock $t -argumentlist $_ }\n}\n<\/pre>\n<p>Running this locally on my Windows 10 desktop with 32GB of memory took a little over 34 seconds. Here's the same command using -Parallel.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Measure-Command {\n    \"c:\\work\", \"c:\\scripts\", \"d:\\temp\", \"C:\\users\\jeff\\Documents\", \"c:\\windows\" | ForEach-Object -parallel {\n        $path = $_\n        Get-ChildItem -path $path -recurse -file -erroraction SilentlyContinue |\n            Measure-Object -sum -Property Length -Maximum -Minimum |\n            Select-Object @{Name = \"Computername\"; Expression = { $env:COMPUTERNAME } },\n            @{Name = \"Path\"; Expression = { Convert-Path $path } },\n            Count, Sum, Maximum, Minimum\n\n    } \n}\n<\/pre>\n<p>You should notice a big difference in the syntax. Because each parallel scriptblock is running in a new runspace, it is harder to pass variables like $t. There are ways around this. For me, the easiest solution for the demonstration was to simply copy the scriptblock.<\/p>\n<p>This command took about 50 seconds to run. Although if I bumped the throttle limit to 6. it completed in about 20 seconds.<\/p>\n<h2>Remote Commands<\/h2>\n<p>Where -Parallel begins to make sense is with commands that involve remote computers, or anything that contains some form of variability. Here's a traditional command you might run.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Measure-Command {\n    'srv1', 'srv2', 'win10', 'dom1', 'srv1', 'srv2', 'win10', 'dom1' | ForEach-Object {\n        Get-WinEvent -FilterHashtable @{Logname = \"system\"; Level = 2, 3 } -MaxEvents 100 -ComputerName $_\n    }\n}\n<\/pre>\n<p>I only have a few virtual machines to test with so I'm repeating the command with them. This completed in about 3 seconds. Compared to the -Parallel approach:<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Measure-Command {\n    'srv1', 'srv2', 'win10', 'dom1', 'srv1', 'srv2', 'win10', 'dom1' | ForEach-Object -parallel {\n        Get-WinEvent -FilterHashtable @{Logname = \"system\"; Level = 2, 3 } -MaxEvents 100 -ComputerName $_\n    }\n}\n<\/pre>\n<p>which took just under 1 second. Let's up the sample size.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Measure-Command {\n    $d = ('srv1', 'srv2', 'win10', 'dom1')*5 | \n    ForEach-Object {\n        Get-WinEvent -FilterHashtable @{Logname = \"system\"; Level = 2, 3 }  -ComputerName $_\n    }\n}\n<\/pre>\n<p>This took 9.7 seconds compared to this:<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Measure-Command {\n    $d = ('srv1', 'srv2', 'win10', 'dom1')*5  |\n        ForEach-Object -parallel {\n            Get-WinEvent -FilterHashtable @{Logname = \"system\"; Level = 2, 3 }  -ComputerName $_\n        }\n}\n<\/pre>\n<p>which completed in 3.7 seconds<\/p>\n<h2>PowerShell at Scale<\/h2>\n<p>Let's really see what this looks like at scale. I'll even try to simulate server and network latency.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Measure-Command {\n    $d = ('srv1', 'srv2', 'win10', 'dom1') * 100 | ForEach-Object {\n        Get-WinEvent -FilterHashtable @{Logname = \"system\"; Level = 2, 3 }  -ComputerName $_\n        #simulate network\/server latency\n        Start-Sleep -Seconds (Get-Random -Minimum 1 -Maximum 5)\n    }\n}\n<\/pre>\n<p>On my desktop this took over 18 <strong><em>minutes<\/em><\/strong> to complete, returning almost 41,000 records. Here's the default PowerShell 7 version using -parallel:<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Measure-Command {\n    $d = ('srv1', 'srv2', 'win10', 'dom1') * 100 | ForEach-Object -parallel {\n        Get-WinEvent -FilterHashtable @{Logname = \"system\"; Level = 2, 3 }  -ComputerName $_\n        Start-Sleep -Seconds (Get-Random -Minimum 1 -Maximum 5)\n    }\n}\n<\/pre>\n<p>Now the command completes in 3 minutes 35 seconds.<\/p>\n<p>The default throttle limit value might change. Jeffrey Snover has said he thinks it should be higher and I think I agree. Let's wee what happens when using the default ThrottleLimit value from <a title=\"Read online help for this command\" href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=135225\" target=\"_blank\" rel=\"noopener noreferrer\">Invoke-Command<\/a>.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Measure-Command {\n    $d = ('srv1', 'srv2', 'win10', 'dom1') * 100 | ForEach-Object -parallel {\n        Get-WinEvent -FilterHashtable @{Logname = \"system\"; Level = 2, 3 }  -ComputerName $_\n        Start-Sleep -Seconds (Get-Random -Minimum 1 -Maximum 5)\n    } -throttlelimit 32\n}\n<\/pre>\n<p>This completed in 49 seconds!<\/p>\n<h2>A Workflow Alternative<\/h2>\n<p>For one last comparison here is an admittedly rough workflow that approximates the other examples. I switched to <a title=\"Read online help for this command\" href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113314\" target=\"_blank\" rel=\"noopener noreferrer\">Get-Eventlog<\/a> because i don't think the workflow likes sending the filtering hashtable over a serialize connection.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Workflow GetLogs {\n\n    Param()\n\n    Sequence {\n        $computers = ('srv1.company.pri', 'srv2.company.pri', 'win10.company.pri', 'dom1.company.pri') * 100\n        foreach -parallel ($computer in $computers) {\n\n            inlinescript {\n                Write-Verbose \"$(Get-Date) Processing $using:computer\"\n                Start-Sleep -Seconds (Get-Random -Minimum 1 -Maximum 5)\n                Get-Eventlog -LogName system -EntryType error, Warning -ComputerName $using:computer\n            } #inline\n        } #foreach\n    } #sequence\n} #workflow\n}\n\n$d = getlogs -pscomputername localhost -ErrorAction SilentlyContinue\n<\/pre>\n<p>This is not what PowerShell workflows were intended for, although I expect many people have tried to use them in this fashion. This command completed in 10:05, although it would probably have improved with a higher throttle limit.<\/p>\n<h2>Conclusions<\/h2>\n<p>The only absolute conclusion you can make is that you need to do your own testing to determine if using -parallel makes sense for your command or project. Personally, my take away is that -parallel works best for large scale operations with a lot of variability. You might have comparable results writing your commands to scale with Invoke-Command.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">$computers = ('srv1', 'srv2', 'win10', 'dom1') * 100\n$d = Invoke-Command {\n        Get-WinEvent -FilterHashtable @{Logname = \"system\"; Level = 2, 3 }  \n        Start-Sleep -Seconds (Get-Random -Minimum 1 -Maximum 5)\n} -throttlelimit 32 -ComputerName $computers\n<\/pre>\n<p>Yes, there is overhead as PSSessions have to be setup and torn down. This completed in 1:56 for me which is still acceptable performance and as a bonus, this doesn't require PowerShell 7.<\/p>\n<p>I plan to continue experimenting with this feature and would love to hear about your experiences using it. I believe eventually there will a community consensus on when to use this but for now we have the pleasure of figuring that out!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>By, you&#8217;ve most likely heard about the new -parallel parameter with ForEach-Object in the latest preview of PowerShell 7 Personally. I&#8217;ve been waiting for this for a long time. I used to only use PowerShell workflows because it offered a way to run commands in parallel. Having this feature as part of the language is&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"New from The Lonely Administrator: Making Sense of Parallel ForEach-Object in #PowerShell 7","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[4,610,8],"tags":[270,612,611],"class_list":["post-6840","post","type-post","status-publish","format-standard","hentry","category-powershell","category-powershell-7","category-scripting","tag-foreach","tag-parallel","tag-powershell-7"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Making Sense of Parallel ForEach-Object in PowerShell 7 &#8226; The Lonely Administrator<\/title>\n<meta name=\"description\" content=\"The Foreach-Object -parallel feature in PowerShell 7 is tempting but not magical. Let&#039;s see what makes sense and what doesn&#039;t.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Making Sense of Parallel ForEach-Object in PowerShell 7 &#8226; The Lonely Administrator\" \/>\n<meta property=\"og:description\" content=\"The Foreach-Object -parallel feature in PowerShell 7 is tempting but not magical. Let&#039;s see what makes sense and what doesn&#039;t.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/\" \/>\n<meta property=\"og:site_name\" content=\"The Lonely Administrator\" \/>\n<meta property=\"article:published_time\" content=\"2019-10-07T18:58:32+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-10-07T18:58:40+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/10\/image_thumb.png\" \/>\n<meta name=\"author\" content=\"Jeffery Hicks\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@JeffHicks\" \/>\n<meta name=\"twitter:site\" content=\"@JeffHicks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jeffery Hicks\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6840\\\/making-sense-of-parallel-foreach-object-in-powershell-7\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6840\\\/making-sense-of-parallel-foreach-object-in-powershell-7\\\/\"},\"author\":{\"name\":\"Jeffery Hicks\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\"},\"headline\":\"Making Sense of Parallel ForEach-Object in PowerShell 7\",\"datePublished\":\"2019-10-07T18:58:32+00:00\",\"dateModified\":\"2019-10-07T18:58:40+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6840\\\/making-sense-of-parallel-foreach-object-in-powershell-7\\\/\"},\"wordCount\":730,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\"},\"image\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6840\\\/making-sense-of-parallel-foreach-object-in-powershell-7\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/10\\\/image_thumb.png\",\"keywords\":[\"ForEach\",\"parallel\",\"PowerShell 7\"],\"articleSection\":[\"PowerShell\",\"PowerShell 7\",\"Scripting\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6840\\\/making-sense-of-parallel-foreach-object-in-powershell-7\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6840\\\/making-sense-of-parallel-foreach-object-in-powershell-7\\\/\",\"url\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6840\\\/making-sense-of-parallel-foreach-object-in-powershell-7\\\/\",\"name\":\"Making Sense of Parallel ForEach-Object in PowerShell 7 &#8226; The Lonely Administrator\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6840\\\/making-sense-of-parallel-foreach-object-in-powershell-7\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6840\\\/making-sense-of-parallel-foreach-object-in-powershell-7\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/10\\\/image_thumb.png\",\"datePublished\":\"2019-10-07T18:58:32+00:00\",\"dateModified\":\"2019-10-07T18:58:40+00:00\",\"description\":\"The Foreach-Object -parallel feature in PowerShell 7 is tempting but not magical. Let's see what makes sense and what doesn't.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6840\\\/making-sense-of-parallel-foreach-object-in-powershell-7\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6840\\\/making-sense-of-parallel-foreach-object-in-powershell-7\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6840\\\/making-sense-of-parallel-foreach-object-in-powershell-7\\\/#primaryimage\",\"url\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/10\\\/image_thumb.png\",\"contentUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/10\\\/image_thumb.png\",\"width\":1028,\"height\":713},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6840\\\/making-sense-of-parallel-foreach-object-in-powershell-7\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"PowerShell\",\"item\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/category\\\/powershell\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Making Sense of Parallel ForEach-Object in PowerShell 7\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/\",\"name\":\"The Lonely Administrator\",\"description\":\"Practical Advice for the Automating IT Pro\",\"publisher\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\",\"name\":\"Jeffery Hicks\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\",\"caption\":\"Jeffery Hicks\"},\"logo\":{\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Making Sense of Parallel ForEach-Object in PowerShell 7 &#8226; The Lonely Administrator","description":"The Foreach-Object -parallel feature in PowerShell 7 is tempting but not magical. Let's see what makes sense and what doesn't.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/","og_locale":"en_US","og_type":"article","og_title":"Making Sense of Parallel ForEach-Object in PowerShell 7 &#8226; The Lonely Administrator","og_description":"The Foreach-Object -parallel feature in PowerShell 7 is tempting but not magical. Let's see what makes sense and what doesn't.","og_url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/","og_site_name":"The Lonely Administrator","article_published_time":"2019-10-07T18:58:32+00:00","article_modified_time":"2019-10-07T18:58:40+00:00","og_image":[{"url":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/10\/image_thumb.png","type":"","width":"","height":""}],"author":"Jeffery Hicks","twitter_card":"summary_large_image","twitter_creator":"@JeffHicks","twitter_site":"@JeffHicks","twitter_misc":{"Written by":"Jeffery Hicks","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/#article","isPartOf":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/"},"author":{"name":"Jeffery Hicks","@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9"},"headline":"Making Sense of Parallel ForEach-Object in PowerShell 7","datePublished":"2019-10-07T18:58:32+00:00","dateModified":"2019-10-07T18:58:40+00:00","mainEntityOfPage":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/"},"wordCount":730,"commentCount":1,"publisher":{"@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9"},"image":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/#primaryimage"},"thumbnailUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/10\/image_thumb.png","keywords":["ForEach","parallel","PowerShell 7"],"articleSection":["PowerShell","PowerShell 7","Scripting"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/","url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/","name":"Making Sense of Parallel ForEach-Object in PowerShell 7 &#8226; The Lonely Administrator","isPartOf":{"@id":"https:\/\/jdhitsolutions.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/#primaryimage"},"image":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/#primaryimage"},"thumbnailUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/10\/image_thumb.png","datePublished":"2019-10-07T18:58:32+00:00","dateModified":"2019-10-07T18:58:40+00:00","description":"The Foreach-Object -parallel feature in PowerShell 7 is tempting but not magical. Let's see what makes sense and what doesn't.","breadcrumb":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/#primaryimage","url":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/10\/image_thumb.png","contentUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/10\/image_thumb.png","width":1028,"height":713},{"@type":"BreadcrumbList","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6840\/making-sense-of-parallel-foreach-object-in-powershell-7\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"PowerShell","item":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},{"@type":"ListItem","position":2,"name":"Making Sense of Parallel ForEach-Object in PowerShell 7"}]},{"@type":"WebSite","@id":"https:\/\/jdhitsolutions.com\/blog\/#website","url":"https:\/\/jdhitsolutions.com\/blog\/","name":"The Lonely Administrator","description":"Practical Advice for the Automating IT Pro","publisher":{"@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/jdhitsolutions.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9","name":"Jeffery Hicks","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg","url":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg","caption":"Jeffery Hicks"},"logo":{"@id":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg"}}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":7549,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7549\/building-a-powershell-inventory\/","url_meta":{"origin":6840,"position":0},"title":"Building a PowerShell Inventory","author":"Jeffery Hicks","date":"June 16, 2020","format":false,"excerpt":"A few weeks ago, a new Iron Scripter PowerShell scripting challenge was issued. For this challenge we were asked to write some PowerShell code that we could use to inventory our PowerShell script library.\u00a0 Here's how I approached the problem, which by no means is the only way. Lines of\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/ast-results.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/ast-results.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/ast-results.png?resize=525%2C300&ssl=1 1.5x"},"classes":[]},{"id":7559,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/","url_meta":{"origin":6840,"position":1},"title":"An Expanded PowerShell Scripting Inventory Tool","author":"Jeffery Hicks","date":"June 19, 2020","format":false,"excerpt":"The other day I shared my code that I worked up to solve an Iron Scripter PowerShell challenge. One of the shortcomings was that I didn't address a challenge to include a property that would indicate what file was using a given command. I also felt I could do better\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":3740,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/3740\/infoworld-automate-live-vm-export\/","url_meta":{"origin":6840,"position":2},"title":"InfoWorld: Automate Live VM Export","author":"Jeffery Hicks","date":"March 13, 2014","format":false,"excerpt":"This is kinda cool, but I got published in InfoWorld, in a roundabout manner. J. Peter Bruzzese writes a column for InfoWorld on enterprise Windows. His latest column is about exporting Hyper-V virtual machines using PowerShell. In Windows Server 2012 R2 (and Windows 8.1) you can export a virtual machine\u2026","rel":"","context":"In &quot;Hyper-V&quot;","block_context":{"text":"Hyper-V","link":"https:\/\/jdhitsolutions.com\/blog\/category\/hyper-v\/"},"img":{"alt_text":"infoworld","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2014\/03\/infoworld.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":8185,"url":"https:\/\/jdhitsolutions.com\/blog\/active-directory\/8185\/scaling-the-active-directory-log-search-with-powershell\/","url_meta":{"origin":6840,"position":3},"title":"Scaling the Active Directory Log Search with PowerShell","author":"Jeffery Hicks","date":"February 16, 2021","format":false,"excerpt":"Recently, I posted a demonstration of how to find changes to your Active Directory using PowerShell. This process requires that you search through the Security event log on all your domain controllers. As a few people pointed out, myself included, this has the potential to not scale very well in\u2026","rel":"","context":"In &quot;Active Directory&quot;","block_context":{"text":"Active Directory","link":"https:\/\/jdhitsolutions.com\/blog\/category\/active-directory\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2021\/02\/pexels-shuxuan-cao-4432160.jpg?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":8908,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/8908\/copy-to-multiple-destinations-with-powershell\/","url_meta":{"origin":6840,"position":4},"title":"Copy to Multiple Destinations with PowerShell","author":"Jeffery Hicks","date":"February 22, 2022","format":false,"excerpt":"In honor of today, 2\/22\/2022, I thought I'd share a PowerShell function that allows you to copy files to multiple destinations. If you look at help for Copy-Item, which you should, you'll see that the Destination parameter does not take an array. That's ok. I can fix that. However, I\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"copy files to multiple locations.","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/02\/cp2-1024x144.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/02\/cp2-1024x144.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/02\/cp2-1024x144.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/02\/cp2-1024x144.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":4380,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/4380\/more-fun-getting-powershell-user-groups\/","url_meta":{"origin":6840,"position":5},"title":"More Fun Getting PowerShell User Groups","author":"Jeffery Hicks","date":"April 14, 2015","format":false,"excerpt":"A few days ago I posted a PowerShell function to retrieve information about PowerShell user groups. That function returned basic group information like this. Each group on the site has its own page which is what that Link property is for. So it didn't take much work to use the\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/6840","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/comments?post=6840"}],"version-history":[{"count":0,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/6840\/revisions"}],"wp:attachment":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/media?parent=6840"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/categories?post=6840"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/tags?post=6840"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}