{"id":3899,"date":"2013-04-01T11:59:00","date_gmt":"2013-04-01T11:59:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/04\/01\/working-with-html-fragments-and-files\/"},"modified":"2013-04-01T11:59:00","modified_gmt":"2013-04-01T11:59:00","slug":"working-with-html-fragments-and-files","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/working-with-html-fragments-and-files\/","title":{"rendered":"Working with HTML Fragments and Files"},"content":{"rendered":"<p><strong style=\"font-size: 12px\">Summary<\/strong><span style=\"font-size: 12px\">: Windows PowerShell MVPs, Don Jones, Richard Siddaway, and Jeffrey Hicks share excerpts from their new book<\/span><em style=\"font-size: 12px\">.<\/em><\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. This week we will not have our usual <strong>PowerTip<\/strong>. Instead we have excerpts from seven books from Manning Press. In addition, each blog will have a special code for 50% off the book being excerpted that day. Remember that the code is valid only for the day the excerpt is posted. The coupon code is also valid for a second book from the Manning collection.<\/p>\n<p>Today, the excerpt is from <a href=\"http:\/\/www.manning.com\/jones2\/\" target=\"_blank\">PowerShell in Depth<\/a><span style=\"text-decoration: underline\"><br \/> <\/span>&nbsp;&nbsp;&nbsp;&nbsp; By Don Jones, Richard Siddaway, and Jeffery Hicks<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7140.hsg-4-1-13-1.jpg\"><img decoding=\"async\" title=\"Photo of book cover\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7140.hsg-4-1-13-1.jpg\" alt=\"Photo of book cover\" \/><\/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&mdash;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.<\/p>\n<p class=\"Body1\" align=\"left\">Let&rsquo;s begin this blog with an example of what we think is a poor report-generating technique. We see code like this, sadly more often than we would like. Most of the time, the IT pro doesn&rsquo;t know any better, and is simply perpetuating techniques from other languages, such as VBScript. Listing 1, which we devoutly hope you will never run, is a very common approach that you&rsquo;ll see less informed administrators use.<\/p>\n<h2>Listing 1: A poorly designed inventory report<\/h2>\n<p class=\"Code\" style=\"padding-left: 30px\">param ($computername)<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Host &#8216;&#8212;&#8212;- COMPUTER INFORMATION &#8212;&#8212;-&#8216;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Host &#8220;Computer Name: $computername&#8221;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">$os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Host &#8221;&nbsp;&nbsp; OS Version: $($os.version)&#8221;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Host &#8221;&nbsp;&nbsp;&nbsp;&nbsp; OS Build: $($os.buildnumber)&#8221;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Host &#8221; Service Pack: $($os.servicepackmajorversion)&#8221;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">$cs = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computername<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Host &#8221;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RAM: $($cs.totalphysicalmemory)&#8221;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Host &#8221; Manufacturer: $($cs.manufacturer)&#8221;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Host &#8221;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Model: $($cd.model)&#8221;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Host &#8221;&nbsp;&nbsp; Processors: $($cs.numberofprocessors)&#8221;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">$bios = Get-WmiObject -Class Win32_BIOS -ComputerName $computername<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Host &#8220;BIOS Serial: $($bios.serialnumber)&#8221;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Host &#8221;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Host &#8216;&#8212;&#8212;- DISK INFORMATION &#8212;&#8212;-&#8216;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Get-WmiObject -Class Win32_LogicalDisk -Comp $computername -Filt &#8216;drivetype=3&#8217; |<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Select-Object @{n=&#8217;Drive&#8217;;e={$_.DeviceID}},<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @{n=&#8217;Size(GB)&#8217;;e={$_.Size \/ 1GB -as [int]}},<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @{n=&#8217;FreeSpace(GB)&#8217;;e={$_.freespace \/ 1GB -as [int]}} |<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Format-Table -AutoSize<\/p>\n<p class=\"Body1\" align=\"left\">This produces a report something like the one shown here.<\/p>\n<p class=\"Body\" align=\"left\"><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4530.hsg-4-1-13-2.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4530.hsg-4-1-13-2.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p class=\"Body1\" align=\"left\">It does the job, we suppose, but Don has a saying that involves angry deities and puppies which he utters whenever he sees a script that outputs pure text like this. First of all, this script can only ever produce output on the screen because it&rsquo;s using <strong>Write-Host<\/strong>. In most cases, if you find yourself using only <strong>Write-Host<\/strong>, you&rsquo;re probably doing it wrong. Wouldn&rsquo;t it be nice to have the option of putting this information into a file or creating an HTML page? Of course, you could achieve that by just changing all of the <strong>Write-Host<\/strong> commands to <strong>Write-Output<\/strong>&mdash;but you still wouldn&rsquo;t be doing things the right way.<\/p>\n<p class=\"Body\" align=\"left\">There are a lot of better ways that you could produce such a report and that&rsquo;s what this blog is all about. First, we&rsquo;d suggest building a function for each block of output that you want to produce, and having that function produce a single object that contains all of the information you need. The more you can modularize, the more you can reuse those blocks of code. Doing so would make that data available for other purposes, not only your report.<\/p>\n<p class=\"Body\" align=\"left\">In our example of a poorly written report, the first section, Computer Information, would be implemented by some function you&rsquo;d write. The Disk Information section is only sharing information from one source, so it&rsquo;s actually not that bad&mdash;but all of those <strong>Write<\/strong> commands just have to go.<\/p>\n<p class=\"Body\" align=\"left\">The trick to our technique lays in the fact that Windows PowerShell&rsquo;s <strong>ConvertTo-HTML<\/strong> cmdlet can be used in two ways, which you&rsquo;ll see if you examine its Help file. The first way produces a complete HTML page, and the second produces only an HTML fragment. That fragment is a table with whatever data you&rsquo;ve fed the cmdlet. We&rsquo;re going to produce each section of our report as a fragment, and then use the cmdlet to produce a complete HTML page that contains all of those fragments.<\/p>\n<h2>Getting the information<\/h2>\n<p class=\"Body1\" align=\"left\">We&rsquo;ll start by ensuring that we can get whatever data we need formed into an object. We&rsquo;ll need one type of object for each section of our report, so if we&rsquo;re sticking with Computer Information and Disk Information, that&rsquo;s two objects.<\/p>\n<p class=\"Body1\" style=\"padding-left: 30px\" align=\"left\"><strong>Note<\/strong>&nbsp;&nbsp;&nbsp;For brevity and clarity, we&rsquo;re going to omit error handling and other niceties in this example. We would add those in a real-world environment.<\/p>\n<p class=\"Body\" align=\"left\"><strong>Get-WmiObject<\/strong> by itself is capable of producing a single object that has all of the disk information we want, so we simply need to create a function to assemble the computer information. Here it is:<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">function Get-CSInfo {<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; param($computername)<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; $os = Get-WmiObject -Class Win32_OperatingSystem `<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; -ComputerName $computername<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; $cs = Get-WmiObject -Class Win32_ComputerSystem `<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; -ComputerName $computername<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; $bios = Get-WmiObject -Class Win32_BIOS `<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; -ComputerName $computername<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; $props = @{&#8216;ComputerName&#8217;=$computername<\/p>\n<p class=\"Code\" 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 class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&#8216;OS Build&#8217;=$os.buildnumber<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&#8216;Service Pack&#8217;=$os.sevicepackmajorversion<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&#8216;RAM&#8217;=$cs.totalphysicalmemory<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&#8216;Processors&#8217;=$cs.numberofprocessors<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&#8216;BIOS Serial&#8217;=$bios.serialnumber}<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; $obj = New-Object -TypeName PSObject -Property $props<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; Write-Output $obj<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">}<\/p>\n<p class=\"Body1\" align=\"left\">The function uses the <strong>Get-WMIObject<\/strong> cmdlet to retrieve information from three WMI classes on the specified computer. We always want to write objects to the pipeline, so we&rsquo;re using <strong>New-Object<\/strong> to write a custom object to the pipeline, and using a hash table of properties culled from the three WMI classes. Normally, we prefer that property names do not have any spaces; but, because we&rsquo;re going to be using this in a larger reporting context, we&rsquo;ll bend the rules a bit.<\/p>\n<h2>Producing an HTML fragment<\/h2>\n<p class=\"Body1\" align=\"left\">Now we can use our newly created <strong>Get-CSInfo<\/strong> function to create an HTML fragment:<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">$frag1 = Get-CSInfo &ndash;computername SERVER2 |<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">ConvertTo-Html -As LIST -Fragment -PreContent &#8216;&lt;h2&gt;Computer Info&lt;\/h2&gt;&#8217; |<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Out-String<\/p>\n<p class=\"Body1\" align=\"left\">This little trick took us a while to figure out, so it&rsquo;s worth examining.<\/p>\n<ol>\n<li>We&rsquo;re saving the final HTML fragment into a variable named <strong>$frag1<\/strong>. That&rsquo;ll let us capture the HTML content and later insert it into the final file.<\/li>\n<li>We&rsquo;re running <strong>Get-CSInfo<\/strong> and giving it the computer name we want to inventory. For right now, we&rsquo;re hardcoding the SERVER2 computer name. We&rsquo;ll change that to a parameter a bit later.<\/li>\n<li>We&rsquo;re asking <strong>ConvertTo-HTML<\/strong> to display this information in a vertical list, rather than in a horizontal table (which is what it would do by default). The list will mimic the layout from the old &ldquo;bad way of doing things&rdquo; report.<\/li>\n<li>We used the <strong>PreContent<\/strong> switch to add a heading to this section of the report. We added the &lt;h2&gt; HTML tags so that the heading will stand out a bit.<\/li>\n<li>The whole thing&mdash;and this was the tricky part&mdash;is piped to <strong>Out-String<\/strong>. You see, <strong>ConvertTo-HTML<\/strong> puts strings, collections of strings&hellip;all kinds of wacky stuff into the pipeline. All of that will cause problems later when we try to assemble the final HTML page. So we&rsquo;re getting <strong>Out-String<\/strong> to resolve everything into plain old strings.<\/li>\n<\/ol>\n<p class=\"Body\" align=\"left\">We can also produce the second fragment. This is a bit easier because we don&rsquo;t need to write our own function first, but the HTML part will look substantially the same. In fact, the only real difference is that we&rsquo;re letting our data be assembled into a table, rather than as a list.<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">$frag2 = Get-WmiObject -Class Win32_LogicalDisk -Filter &#8216;DriveType=3&#8217; `<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -ComputerName SERVER2 |<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Select-Object @{name=&#8217;Drive&#8217;;expression={$_.DeviceID}},<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @{name=&#8217;Size(GB)&#8217;;expresssion={$_.Size \/ 1GB -as [int]}},<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @{name=&#8217;FreeSpace(GB)&#8217;;expression={<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $_.freespace \/ 1GB -as [int]}} |<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">ConvertTo-Html -Fragment -PreContent &#8216;&lt;h2&gt;Disk Info&lt;\/h2&gt;&#8217; |<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Out-String<\/p>\n<p class=\"Body1\" align=\"left\">We now have two HTML fragments, <strong>$frag1<\/strong> and <strong>$frag2<\/strong>, so we&rsquo;re ready to assemble the final page.<\/p>\n<h2>Assembling the final HTML page<\/h2>\n<p class=\"Body1\" align=\"left\">Assembling the final page simply involves adding our two existing fragments&mdash;although, we&rsquo;re 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&rsquo;d like a good tutorial and reference for CSS, check out <a href=\"http:\/\/www.w3schools.com\/css\/\">CSS Tutorial<\/a> at w3schools.com.<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">$head = @&#8217;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&lt;style&gt;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">body { background-color:#dddddd;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-family:Tahoma;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-size:12pt; }<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">td, th { border:1px solid black;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; border-collapse:collapse; }<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">th { color:white;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; background-color:black; }<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">table, tr, td, th { padding: 2px; margin: 0px }<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">table { margin-left:50px; }<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&lt;\/style&gt;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&#8216;@<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">ConvertTo-HTML -head $head -PostContent $frag1,$frag2 `<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">-PreContent &#8220;&lt;h1&gt;Hardware Inventory for SERVER2&lt;\/h1&gt;&#8221;<\/p>\n<p class=\"Body1\" align=\"left\">We&rsquo;ve put that style sheet into the variable <strong>$head<\/strong>, using a <strong>Here-String<\/strong> to type the entire CSS syntax we wanted. That gets passed to the <strong>Head<\/strong> parameter, our HTML fragments to the <strong>PostContent<\/strong> 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 class=\"Body\" align=\"left\">We saved the entire script as C:\\Good.ps1, and ran it like this:<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">.\/good &gt; Report.htm<\/p>\n<p class=\"Body1\" align=\"left\">That directs the output HTML to Report.htm, which is incredibly beautiful and shown here.<\/p>\n<p class=\"Body\" align=\"left\"><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8080.hsg-4-1-13-3.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8080.hsg-4-1-13-3.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p class=\"Body1\" align=\"left\">Okay, maybe it&rsquo;s no work of art, but it&rsquo;s highly functional; and frankly, it looks better than the on-screen-only report we started with in this blog. Listing 2 shows the completed script, where we&rsquo;ve swapped out the hardcoded computer name for a script-wide parameter that defaults to the local host. Notice that we&rsquo;ve also included the <strong>[CmdletBinding()]<\/strong> declaration at the top of the script, enabling the <strong>V<\/strong><strong>erbose<\/strong> parameter. We&rsquo;ve used <strong>Write-Verbose<\/strong> to document what each step of the script is doing.<\/p>\n<h2>Listing 2: An HTML inventory report script<\/h2>\n<p class=\"Code\" style=\"padding-left: 30px\">&lt;#<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">.DESCRIPTION<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Retrieves inventory information and produces HTML<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">.EXAMPLE<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">.\/Good &gt; Report.htm<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">.PARAMETER<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">The name of a computer to query. The default is the local computer.<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">#&gt;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">[CmdletBinding()]<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">param([string]$computername=$env:computername)<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\"># function to get computer system info<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">function Get-CSInfo {<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; param($computername)<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; $cs = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computername<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; $bios = Get-WmiObject -Class Win32_BIOS -ComputerName $computername<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; $props = @{&#8216;ComputerName&#8217;=$computername<\/p>\n<p class=\"Code\" 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 class=\"Code\" 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 class=\"Code\" 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 class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8216;RAM&#8217;=$cs.totalphysicalmemory<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8216;Processors&#8217;=$cs.numberofprocessors<\/p>\n<p class=\"Code\" 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 class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; $obj = New-Object -TypeName PSObject -Property $props<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp; Write-Output $obj<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">}<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Verbose &#8216;Producing computer system info fragment&#8217;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">$frag1 = Get-CSInfo -computername $computername |<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">ConvertTo-Html -As LIST -Fragment -PreContent &#8216;&lt;h2&gt;Computer Info&lt;\/h2&gt;&#8217; |<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Out-String<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Verbose &#8216;Producing disk info fragment&#8217;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">$frag2 = Get-WmiObject -Class Win32_LogicalDisk -Filter &#8216;DriveType=3&#8217; `<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -ComputerName $computername |<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Select-Object @{name=&#8217;Drive&#8217;;expression={$_.DeviceID}},<\/p>\n<p class=\"Code\" 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 class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @{name=&#8217;FreeSpace(GB)&#8217;;expression={$_.freespace \/ 1GB -as [int]}} |<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">ConvertTo-Html -Fragment -PreContent &#8216;&lt;h2&gt;Disk Info&lt;\/h2&gt;&#8217; |<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Out-String<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Verbose &#8216;Defining CSS&#8217;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">$head = @&#8217;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&lt;style&gt;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">body { background-color:#dddddd;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-family:Tahoma;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-size:12pt; }<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">td, th { border:1px solid black;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; border-collapse:collapse; }<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">th { color:white;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; background-color:black; }<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">table, tr, td, th { padding: 2px; margin: 0px }<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">table { margin-left:50px; }<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&lt;\/style&gt;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&#8216;@<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">&nbsp;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Verbose &#8216;Producing final HTML&#8217;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">Write-Verbose &#8216;Pipe this output to a file to save it&#8217;<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">ConvertTo-HTML -head $head -PostContent $frag1,$frag2 `<\/p>\n<p class=\"Code\" style=\"padding-left: 30px\">-PreContent &#8220;&lt;h1&gt;Hardware Inventory for $ComputerName&lt;\/h1&gt;&#8221;<\/p>\n<p class=\"Body1\" style=\"padding-left: 30px\" align=\"left\">&nbsp;<\/p>\n<p class=\"Body1\" style=\"padding-left: 30px\" align=\"left\">Now that&rsquo;s a script you can build upon! And the script is very easy to use.<\/p>\n<p class=\"Body\" style=\"padding-left: 30px\" align=\"left\">PS C:\\&gt; $computer = SERVER01<\/p>\n<p class=\"Body\" style=\"padding-left: 30px\" align=\"left\">PS C:\\&gt; C:\\Scripts\\good.ps1 -computername $computer |<\/p>\n<p class=\"Body\" style=\"padding-left: 30px\" align=\"left\">&gt;&gt; Out-File &#8220;$computer.html&#8221;<\/p>\n<p class=\"Body\" style=\"padding-left: 30px\" align=\"left\">&gt;&gt;&nbsp;<\/p>\n<p class=\"Body\" style=\"padding-left: 30px\" align=\"left\">PS C:\\&gt; Invoke-Item &#8220;$computer.html&#8221;<\/p>\n<p class=\"Body1\" align=\"left\">The script runs, produces an output file for future reference, and displays the report. Keep in mind that our work in building the <strong>Get-CSInfo<\/strong> function is reusable. Because that function outputs an object and not only pure text, you could repurpose it in a variety of places where you might need the same information.<\/p>\n<p class=\"Body\" align=\"left\">To add to this report, you&rsquo;d simply:<\/p>\n<ol>\n<li>Write a command or function that generates a single object that 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 class=\"Body\" align=\"left\">Yes, this report is text. Ultimately, every report will be, because text is what we 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 wasn&rsquo;t as easy to do with our original pure-text report, because the actual working code was so embedded with all of that formatted text.<\/p>\n<p class=\"Body1\" align=\"left\">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 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 the way it needs you to.<\/p>\n<p><strong>Here is the code for the discount offer today at <\/strong><a href=\"http:\/\/www.manning.com\/\" target=\"_blank\">www.manning.com<\/a>: <strong>scriptw1<\/strong><br \/> Valid for 50% off <a href=\"http:\/\/www.manning.com\/jones2\/\" target=\"_blank\">PowerShell in Depth<\/a> and <a href=\"http:\/\/www.manning.com\/stirk\/\" target=\"_blank\">SQL Server DMVs in Action<br \/><\/a><span style=\"font-size: 12px\">Offer valid from April 1, 2013 12:01 AM until April 2 midnight (EST)<\/span><\/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><span style=\"font-size: 12px\">&nbsp;<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Windows PowerShell MVPs, Don Jones, Richard Siddaway, and Jeffrey Hicks share excerpts from their new book. Microsoft Scripting Guy, Ed Wilson, is here. This week we will not have our usual PowerTip. Instead we have excerpts from seven books from Manning Press. In addition, each blog will have a special code for 50% off [&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":[],"class_list":["post-3899","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting"],"acf":[],"blog_post_summary":"<p>Summary: Windows PowerShell MVPs, Don Jones, Richard Siddaway, and Jeffrey Hicks share excerpts from their new book. Microsoft Scripting Guy, Ed Wilson, is here. This week we will not have our usual PowerTip. Instead we have excerpts from seven books from Manning Press. In addition, each blog will have a special code for 50% off [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/3899","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=3899"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/3899\/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=3899"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=3899"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=3899"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}