{"id":2813,"date":"2013-09-28T00:01:00","date_gmt":"2013-09-28T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/09\/28\/weekend-scripter-max-out-powershell-in-a-little-bit-of-timepart-1\/"},"modified":"2013-09-28T00:01:00","modified_gmt":"2013-09-28T00:01:00","slug":"weekend-scripter-max-out-powershell-in-a-little-bit-of-timepart-1","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/weekend-scripter-max-out-powershell-in-a-little-bit-of-timepart-1\/","title":{"rendered":"Weekend Scripter: Max Out PowerShell in a Little Bit of Time&#8212;Part 1"},"content":{"rendered":"<p><strong>Summary<\/strong>: Microsoft PFE, Jason Walker, talks about how to maximize performance from Windows PowerShell in the enterprise.<\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/tags\/jason+walker\/\" target=\"_blank\">Jason Walker<\/a> is our guest blogger this weekend. Today is Part 1 of a 2 part series. Here&rsquo;s Jason&hellip;<\/p>\n<p>I work in an environment with widely dispersed datacenters and I am often tasked with searching for events from multiple servers. It becomes a challenge to quickly query the event logs of 5, 10, or even 20 servers, not to mention 70 servers.<\/p>\n<p>Let us look at our options. Immediately, Windows PowerShell remoting comes to mind. Windows PowerShell jobs is a possibility, and for those developer types, runspaces is an option.<\/p>\n<p>Let&rsquo;s look at remoting first.<\/p>\n<p><strong>Invoke-Command<\/strong> has a <strong>ComputerName<\/strong> parameter, which accepts an array, and a <strong>ThrottleLimit<\/strong> parameter. It&rsquo;s as if <strong>Invoke-Command<\/strong> was made for this type of stuff! Let&rsquo;s see how it works.<\/p>\n<p>I&rsquo;m going to query the System log for event 1074 on five servers. And for instructional purposes, I will specify a <strong>MaxEvents<\/strong> of 1:<\/p>\n<p style=\"padding-left: 30px\">$Servers = &#8220;mail01&#8243;,&#8221;mail02&#8243;,&#8221;mbx01&#8243;,&#8221;mbx02&#8243;,&#8221;mbx03&#8221;<\/p>\n<p style=\"padding-left: 30px\">$Events = Invoke-Command -ComputerName $Servers -ScriptBlock {$sb=@{Logname=&#8217;System&#8217;;Id=1074};Get-WinEvent -FilterHashtable $sb -MaxEvents 1}<\/p>\n<p style=\"padding-left: 30px\">$Events<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1050.wes-9-28-13-1.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1050.wes-9-28-13-1.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>These commands could easily be combined into a one-liner. Using <strong>Invoke-Command<\/strong> is easy and straightforward. When you want to run a cmdlet across multiple machines and the cmdlet&rsquo;s <strong>ComputerName<\/strong> parameter doesn&rsquo;t accept an array, <strong>Invoke-Command<\/strong> easily adds that functionality. We see that <strong>Invoke-Command<\/strong> runs and returns results almost instantly.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2654.wes-9-28-13-2.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2654.wes-9-28-13-2.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>Using <strong>Measure-Command<\/strong> shows that the series of commands took 742 milliseconds. But let&rsquo;s take a closer look at the objects that are returned. If I take the first object that is returned, it looks like I ran <strong>Get-WinEvent<\/strong> without leveraging remoting:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8078.wes-9-28-13-3.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8078.wes-9-28-13-3.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>However, notice the value of the <strong>Properties<\/strong> property. It has depth (nested objects). Let&rsquo;s expand that and see what we get.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5123.wes-9-28-13-4.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5123.wes-9-28-13-4.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>Only the names of the nested objects are returned as strings. Let&rsquo;s run <strong>Get-WinEvent<\/strong> by itself to compare.<\/p>\n<p style=\"padding-left: 30px\">$Event = Get-WinEvent -cn mail01 -FilterHashtable @{Logname=&#8217;System&#8217;;id=1074} -MaxEvents 1<\/p>\n<p style=\"padding-left: 30px\">$Event | select *<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4718.wes-9-28-13-5.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4718.wes-9-28-13-5.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>It looks the same so far. Now let&rsquo;s look at the nested objects in <strong>Properties<\/strong>:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4606.wes-9-28-13-6.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4606.wes-9-28-13-6.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>Now we have values. Awesome!<\/p>\n<p>When using <strong>Get-WinEvent<\/strong>, I always want the values within <strong>Properties<\/strong>, but if I&rsquo;m using a different cmdlet or function, this might not be an issue. For example, let&rsquo;s say I want to know who rebooted my server mail01. All I know is that the server has been rebooted.<\/p>\n<p>I could use <strong>Get-WinEvent<\/strong> and parse through the blob of text that is the <strong>Message<\/strong> property, or I could look at the seventh value contained in the <strong>Properties<\/strong> property. In the previous example, vmicsvc.exe has initiated a shutdown on behalf of NT Authority\\System. So it&rsquo;s safe to say that a user shut down the virtual machine with the Hyper-V management console or by using the <strong>Stop-VM<\/strong> cmdlet. To drive my point home, I can easily get the data I want from the <strong>Properties<\/strong> property versus parsing through a paragraph of text, for example:<\/p>\n<p style=\"padding-left: 30px\">$Event = Get-WinEvent -cn mail01 -FilterHashtable @{Logname=&#8217;System&#8217;;id=1074} -MaxEvents 1<\/p>\n<p style=\"padding-left: 30px\">$Event | Format-Table MachineName,@{name=&#8221;ShutdownByUser&#8221;;Expression={$_.Properties[6].value}}<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1072.wes-9-28-13-7.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1072.wes-9-28-13-7.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>In the previous example, with only two lines of code (which could easily be one line of code), I can report the user name who initiated the shutdown. Super, super easy.<\/p>\n<p>Let&rsquo;s look at using jobs. Jobs are cool because when a cmdlet is run inside a job, the work is being done asynchronously in its own thread. So if you run a cmdlet as a job, the prompt will be instantly returned, and you can continue to Windows PowerShell as the job runs in the background. We could talk about jobs forever (for more information, see <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2012\/12\/31\/using-windows-powershell-jobs.aspx\" target=\"_blank\">Using Windows PowerShell Jobs<\/a>)&mdash;but let&rsquo;s see them in action.<\/p>\n<p style=\"padding-left: 30px\">$Servers = &#8220;mail01&#8243;,&#8221;mail02&#8243;,&#8221;mbx01&#8243;,&#8221;mbx02&#8243;,&#8221;mbx03&#8221;<\/p>\n<p style=\"padding-left: 30px\">Foreach($Server in $Servers){<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Start-Job -ScriptBlock {Get-WinEvent -ComputerName $using:Server -FilterHashtable @{LogName=&#8217;System&#8217;;Id=1074} -MaxEvents 1}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;}<\/p>\n<p style=\"padding-left: 30px\">$EventsUsingJobs = Get-Job | Wait-Job | Receive-Job<\/p>\n<p style=\"padding-left: 30px\">$EventsUsingJobs<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3755.wes-9-28-13-8.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3755.wes-9-28-13-8.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>When using jobs there are a few more steps involved. <strong>Start-Job<\/strong> doesn&rsquo;t have a <strong>ComputerName<\/strong> parameter, so I have to iterate through an array of server names and supply <strong>Get-WinEvent<\/strong> with each of the unique computer names. You can see the jobs being created, and when the jobs are finished, I have to use <strong>Receive-Job<\/strong> to see the results. I will use <strong>Measure-Command<\/strong> to see how long everything took to run.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4718.wes-9-28-13-9.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4718.wes-9-28-13-9.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>By using jobs, it took four seconds to query the five servers. That is considerably longer than using <strong>Invoke-Command<\/strong>, but less than a server a second isn&rsquo;t bad. I mean, I couldn&rsquo;t manually run the command five times in four seconds.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2703.wes-9-28-13-10.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2703.wes-9-28-13-10.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>When we look at the objects returned, again we see that we lose the nested objects. As a work around, I could pipe the results of <strong>Get-WinEvent<\/strong> to <strong>Export-CliXml<\/strong> inside the script block, but the results have to be written to disk and then imported by using <strong>Import-CliXml<\/strong>, which adds more time to task.<\/p>\n<p>Let&rsquo;s use runspaces. Here I wrote a function that I named <strong>Get-AsyncEvent<\/strong> that runs <strong>Get-WinEvent<\/strong> in asynchronous runspaces. Here is what happened when ran it with the exact filters as the previous examples:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4572.wes-9-28-13-11.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4572.wes-9-28-13-11.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>We find that runspaces are significantly faster than using <strong>Invoke-Command<\/strong>, taking less than half the time to run the command. But do we get nested objects back? Let&rsquo;s run the command again and take a look:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/6366.wes-9-28-13-12.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/6366.wes-9-28-13-12.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>Jackpot! Not only are runspaces the fastest solution, but they return objects in their entirety.<\/p>\n<p>Let&rsquo;s recap&#8230;<\/p>\n<ul>\n<li><strong>Invoke-Command<\/strong> is easy and the less complicated to run, but it doesn&rsquo;t return nested objects.<\/li>\n<li><strong>Start-Job<\/strong> takes a little more coding, but is the slowest and it returns no nested objects unless we use <strong>Export-CliXml<\/strong>.<\/li>\n<li>Runspaces are crazy fast and returned the nested objects.<\/li>\n<\/ul>\n<p>But what does it take to leverage runspaces? I can say that runspaces are by far the most complicated multithreading solution. But if you tune in tomorrow, I will explain the basics of using runspaces.<\/p>\n<p>~Jason<\/p>\n<p>Thank you, Jason. Be sure to join us tomorrow as Jason brings you more Windows PowerShell goodness.<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto: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.<\/p>\n<p><strong>Ed Wilson, Microsoft Scripting Guy<\/strong>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Microsoft PFE, Jason Walker, talks about how to maximize performance from Windows PowerShell in the enterprise. Microsoft Scripting Guy, Ed Wilson, is here. Jason Walker is our guest blogger this weekend. Today is Part 1 of a 2 part series. Here&rsquo;s Jason&hellip; I work in an environment with widely dispersed datacenters and I am [&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":[56,338,60,3,4,61,45],"class_list":["post-2813","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-guest-blogger","tag-jason-walker","tag-performance","tag-scripting-guy","tag-scripting-techniques","tag-weekend-scripter","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Microsoft PFE, Jason Walker, talks about how to maximize performance from Windows PowerShell in the enterprise. Microsoft Scripting Guy, Ed Wilson, is here. Jason Walker is our guest blogger this weekend. Today is Part 1 of a 2 part series. Here&rsquo;s Jason&hellip; I work in an environment with widely dispersed datacenters and I am [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/2813","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=2813"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/2813\/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=2813"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=2813"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=2813"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}