{"id":9211,"date":"2012-06-05T00:01:00","date_gmt":"2012-06-05T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2012\/06\/05\/powershell-in-depth-part-2\/"},"modified":"2012-06-05T00:01:00","modified_gmt":"2012-06-05T00:01:00","slug":"powershell-in-depth-part-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/powershell-in-depth-part-2\/","title":{"rendered":"PowerShell in Depth: Part 2"},"content":{"rendered":"<p><b>Summary<\/b>: In today&rsquo;s blog, we continue our excerpt from the upcoming book by Don Jones, Richard Siddaway, and Jeffery Hicks.<\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. Yesterday, we posted <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2012\/06\/04\/powershell-in-depth-part-1.aspx\">PowerShell<b> <\/b>in Depth<b>: <\/b>Part 1<\/a>, an excerpt provided by Candace Gillhoolley from Manning Publications. The book, <a href=\"http:\/\/www.manning.com\/jones2\/\">PowerShell in Depth<\/a>, is written by Don Jones, Richard Siddaway, and Jeffery Hicks.&nbsp; Today we have the conclusion to that blog.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7506.hsg-6-5-12-1.jpg\"><img decoding=\"async\" title=\"Image of book cover\" alt=\"Image of book cover\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7506.hsg-6-5-12-1.jpg\" \/><\/a><\/p>\n<p>There&rsquo;s definitely a trick to creating reports with Windows PowerShell. Windows PowerShell isn&rsquo;t at its best when it&rsquo;s forced to work with text; objects are where it excels. This blog, based on Chapter 33 from <a href=\"http:\/\/www.manning.com\/jones2\/\" target=\"_blank\">PowerShell in Depth<\/a>, focuses on a technique that can produce a nicely formatted HTML report, suitable for emailing to a boss or colleague.<b><\/b><\/p>\n<h2>Assembling the final HTML page<\/h2>\n<p>Assembling the final page simply involves adding our two existing fragments, although we are also going to embed a style sheet. Using cascading style sheet (CSS) language is a bit beyond the scope of this blog, but this example will give you a basic idea of what it can do. This embedded style sheet lets us control the formatting of the HTML page, so that it looks a little nicer. If you would like a good tutorial and reference to CSS, check out this <a href=\"http:\/\/www.w3schools.com\/css\/\" target=\"_blank\">CSS Tutorial<\/a> on the w3schools.com site.<\/p>\n<p style=\"padding-left: 30px\">$head = @&#8217;<\/p>\n<p style=\"padding-left: 30px\">&lt;style&gt;<\/p>\n<p style=\"padding-left: 30px\">body { background-color:#dddddd;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-family:Tahoma;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-size:12pt; }<\/p>\n<p style=\"padding-left: 30px\">td, th { border:1px solid black;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; border-collapse:collapse; }<\/p>\n<p style=\"padding-left: 30px\">th { color:white;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; background-color:black; }<\/p>\n<p style=\"padding-left: 30px\">table, tr, td, th { padding: 2px; margin: 0px }<\/p>\n<p style=\"padding-left: 30px\">table { margin-left:50px; }<\/p>\n<p style=\"padding-left: 30px\">&lt;\/style&gt;<\/p>\n<p style=\"padding-left: 30px\">&#8216;@<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">ConvertTo-HTML -head $head -PostContent $frag1,$frag2 `<\/p>\n<p style=\"padding-left: 30px\">-PreContent &#8220;&lt;h1&gt;Hardware Inventory for SERVER2&lt;\/h1&gt;&#8221;<\/p>\n<p>We have put that style sheet into the <b>$head<\/b> variable by using a here string to type out the entire CSS syntax that we wanted. That gets passed to the <i>Head<\/i> parameter, our HTML fragments to the <i>PostContent<\/i> parameter, and we couldn&rsquo;t resist adding a header for the whole page, where we&rsquo;ve again hardcoded a computer name (SERVER2).<\/p>\n<p>We saved the entire script as C:\\Good.ps1, and ran it like this:<\/p>\n<p style=\"padding-left: 30px\">.\/good &gt; Report.htm<\/p>\n<p>That directs the output HTML to Report.htm. This HTML report consists of multiple HTML fragments, which is incredibly beautiful as shown here:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5078.hsg-6-5-12-2.png\"><img decoding=\"async\" title=\"Image of report\" alt=\"Image of report\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5078.hsg-6-5-12-2.png\" \/><\/a><\/p>\n<p>Okay, maybe it is no work of art, but it is highly functional, and frankly, it looks better than the on-screen-only report we started with in yesterday&rsquo;s blog. List 2 shows the completed script, where we&rsquo;ve swapped out the hard-coded computer name for a script-wide parameter that defaults to the local host. Notice too that we&rsquo;ve included the <b>[CmdletBinding()] <\/b>declaration at the top of the script, which enables the <i>Verbose<\/i> parameter. We have used <b>Write-Verbose<\/b> to document what each step of the script is doing.<\/p>\n<p><b>List 2: An HTML inventory report script<\/b><\/p>\n<p style=\"padding-left: 30px\">&lt;#<\/p>\n<p style=\"padding-left: 30px\">.DESCRIPTION<\/p>\n<p style=\"padding-left: 30px\">Retrieves inventory information and produces HTML<\/p>\n<p style=\"padding-left: 30px\">.EXAMPLE<\/p>\n<p style=\"padding-left: 30px\">.\/Good &gt; Report.htm<\/p>\n<p style=\"padding-left: 30px\">.PARAMETER<\/p>\n<p style=\"padding-left: 30px\">The name of a computer to query. The default is the local computer.<\/p>\n<p style=\"padding-left: 30px\">#&gt;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">[CmdletBinding()]<\/p>\n<p style=\"padding-left: 30px\">param([string]$computername=$env:computername)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\"># function to get computer system info<\/p>\n<p style=\"padding-left: 30px\">function Get-CSInfo {<\/p>\n<p style=\"padding-left: 30px\">&nbsp; param($computername)<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $cs = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computername<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $bios = Get-WmiObject -Class Win32_BIOS -ComputerName $computername<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $props = @{&#8216;ComputerName&#8217;=$computername<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8216;OS Version&#8217;=$os.version<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8216;OS Build&#8217;=$os.buildnumber<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8216;Service Pack&#8217;=$os.sevicepackmajorversion<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8216;RAM&#8217;=$cs.totalphysicalmemory<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8216;Processors&#8217;=$cs.numberofprocessors<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8216;BIOS Serial&#8217;=$bios.serialnumber}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $obj = New-Object -TypeName PSObject -Property $props<\/p>\n<p style=\"padding-left: 30px\">&nbsp; Write-Output $obj<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">Write-Verbose &#8216;Producing computer system info fragment&#8217;<\/p>\n<p style=\"padding-left: 30px\">$frag1 = Get-CSInfo -computername $computername |<\/p>\n<p style=\"padding-left: 30px\">ConvertTo-Html -As LIST -Fragment -PreContent &#8216;&lt;h2&gt;Computer Info&lt;\/h2&gt;&#8217; |<\/p>\n<p style=\"padding-left: 30px\">Out-String<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">Write-Verbose &#8216;Producing disk info fragment&#8217;<\/p>\n<p style=\"padding-left: 30px\">$frag2 = Get-WmiObject -Class Win32_LogicalDisk -Filter &#8216;DriveType=3&#8217; `<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -ComputerName $computername |<\/p>\n<p style=\"padding-left: 30px\">Select-Object @{name=&#8217;Drive&#8217;;expression={$_.DeviceID}},<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @{name=&#8217;Size(GB)&#8217;;expression={$_.Size \/ 1GB -as [int]}},<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @{name=&#8217;FreeSpace(GB)&#8217;;expression={$_.freespace \/ 1GB -as [int]}} |<\/p>\n<p style=\"padding-left: 30px\">ConvertTo-Html -Fragment -PreContent &#8216;&lt;h2&gt;Disk Info&lt;\/h2&gt;&#8217; |<\/p>\n<p style=\"padding-left: 30px\">Out-String<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">Write-Verbose &#8216;Defining CSS&#8217;<\/p>\n<p style=\"padding-left: 30px\">$head = @&#8217;<\/p>\n<p style=\"padding-left: 30px\">&lt;style&gt;<\/p>\n<p style=\"padding-left: 30px\">body { background-color:#dddddd;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-family:Tahoma;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-size:12pt; }<\/p>\n<p style=\"padding-left: 30px\">td, th { border:1px solid black;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; border-collapse:collapse; }<\/p>\n<p style=\"padding-left: 30px\">th { color:white;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; background-color:black; }<\/p>\n<p style=\"padding-left: 30px\">table, tr, td, th { padding: 2px; margin: 0px }<\/p>\n<p style=\"padding-left: 30px\">table { margin-left:50px; }<\/p>\n<p style=\"padding-left: 30px\">&lt;\/style&gt;<\/p>\n<p style=\"padding-left: 30px\">&#8216;@<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">Write-Verbose &#8216;Producing final HTML&#8217;<\/p>\n<p style=\"padding-left: 30px\">Write-Verbose &#8216;Pipe this output to a file to save it&#8217;<\/p>\n<p style=\"padding-left: 30px\">ConvertTo-HTML -head $head -PostContent $frag1,$frag2 `<\/p>\n<p style=\"padding-left: 30px\">-PreContent &#8220;&lt;h1&gt;Hardware Inventory for $ComputerName&lt;\/h1&gt;&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p>Now that&rsquo;s a script you can build upon!&nbsp;Using the script is very easy.<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">PS C:\\&gt; $computer = SERVER01<\/p>\n<p style=\"padding-left: 30px\">PS C:\\&gt; C:\\Scripts\\good.ps1 -computername $computer |<\/p>\n<p style=\"padding-left: 30px\">&gt;&gt; Out-File &#8220;$computer.html&#8221;<\/p>\n<p style=\"padding-left: 30px\">&gt;&gt;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">PS C:\\&gt; Invoke-Item &#8220;$computer.html&#8221;<\/p>\n<p>The script runs, produces an output file for future reference, and displays the report. Keep in mind that our work to build the <b>Get-CSInfo<\/b> function is reusable. Because that function outputs an object, not only pure text, you could repurpose it in a variety of places where you might need the same information.<\/p>\n<p>To add to this report, you would:<\/p>\n<ol>\n<li>Write a command or function that generates a single kind of object, which contains all the information you need for a new report section.<\/li>\n<li>Use that object to produce an HTML fragment, and store it in a variable.<\/li>\n<li>Add that new variable to the list of variables in the script&rsquo;s last command, thus adding the new HTML fragment to the final report.<\/li>\n<li>Sit back and relax.<\/li>\n<\/ol>\n<p>Yes, this report is text. Ultimately, every report will be because text is what humans read. The point of this one is that everything stays as Windows PowerShell-friendly objects until the last possible instance. We let Windows PowerShell, rather than our own fingers, format everything for us. The actual working bits of this script, which retrieve the information we need, could easily be copied and pasted and used elsewhere for other purposes. That was not as easy to do with our original pure-text report because the actual working code was embedded with all of that formatted text.<\/p>\n<p>Building reports is certainly a common need for administrators, and Windows PowerShell is well suited to the task. The trick, we feel, is to produce reports in a way that makes the reports&rsquo; functional code (the bits that retrieve information and so forth) somewhat distinct from the formatting- and the output-creation code. In fact, Windows PowerShell is generally capable of delivering great formatting with very little work on your part, as long as you work it the way it needs you to.<\/p>\n<p>~Don, Richard, and Jeffery<\/p>\n<p>Thank you Candace, Don, Richard, and Jeffery.<\/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><b>Ed Wilson, Microsoft Scripting Guy<\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: In today&rsquo;s blog, we continue our excerpt from the upcoming book by Don Jones, Richard Siddaway, and Jeffery Hicks. Microsoft Scripting Guy, Ed Wilson, is here. Yesterday, we posted PowerShell in Depth: Part 1, an excerpt provided by Candace Gillhoolley from Manning Publications. The book, PowerShell in Depth, is written by Don Jones, Richard [&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":[64,56,65,189,3,45],"class_list":["post-9211","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-don-jones","tag-guest-blogger","tag-jeffery-hicks","tag-richard-siddaway","tag-scripting-guy","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: In today&rsquo;s blog, we continue our excerpt from the upcoming book by Don Jones, Richard Siddaway, and Jeffery Hicks. Microsoft Scripting Guy, Ed Wilson, is here. Yesterday, we posted PowerShell in Depth: Part 1, an excerpt provided by Candace Gillhoolley from Manning Publications. The book, PowerShell in Depth, is written by Don Jones, Richard [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/9211","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=9211"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/9211\/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=9211"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=9211"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=9211"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}