Building a Web Report in PowerShell, use the -Force Luke
Tags: powershell, tutorial, webdev, devops
Published: 2021-10-07 22:03:32

---
title: Building a Web Report in PowerShell, use the -Force Luke
published: true
description: An article describing how to create web reports using PowerShell
tags: powershell, tutorial, webdev, devops
series: powershell-tutorials
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r9i2e1tsy8ejgc23h3s9.jpg
---

> TLDR; The idea of this article is to show how to build a web report. I will show the usage of several commands that you can connect that does all the heavy lifting for you

## The scenario - create a report from some remote data

Ok, here you are, you are looking to read data from one place and present that as a web report. The data is remote, you need to fetch it somehow, you also probably need to think about how to convert the incoming data and lastly create that web report. If you are a developer, you probably think that oh ok, this is probably a few moving parts, 10-20 lines of code. But you've heard of PowerShell, it's supposed to be powerful, do a lot of heavy lifting, so why not give it a spin.

## The steps we will take

To achieve our task, we need to plan it out. Carry it out sequentially, and who knows, maybe this is something we can reuse? So what steps:

1. **Fetch the data**. Need to grab the data somehow
2. **Convert**. Lets assume this data comes in some type of format and we most likely need to convert it some other form.
3. **Present**. So we fetched the data, massaged it into a suitable format, now what? Now, we want to present it as a web report, HTML, CSS etc.

## Getting to work

We have a game plan. Now let's see if we can find the commands we need. A good starting point is the [Microsoft.PowerShell.Utility section](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/?view=powershell-7.1). In this section, there are tons of commands that does a lot of heavy lifting for you. 

### Grabbing the data

First things first, we need to grab some data remotely, so what's our options?

- [Invoke-WebRequest](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-webrequest?view=powershell-7.1) this seems to let us call a URL, send a body, credentials etc, seems promising.
- [Invoke-RestMethod](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-restmethod?view=powershell-7.1). What about this one, how is it different? This sentence right here:

   > PowerShell formats the response based to the data type. For an RSS or ATOM feed, PowerShell returns the Item or Entry XML nodes. For JavaScript Object Notation (JSON) or XML, PowerShell converts, or deserializes, the content into [PSCustomObject] objects.

   It takes a JSON response and turns that into a PSCustomObject, nice. Well let's see with our other commands before we make a decision.

### Presenting the data

This is the last thing we need to do but we need to understand if there's a command that helps us with report creation and most importantly, what input it takes. I think we found it:

- [ConvertTo-Html](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertto-html?view=powershell-7.1) Converts .NET objects into HTML that can be displayed in a Web browser.

   Yea, that reminds us of something, `Invoke-RestMethod`. Why? Cause `Invoke-RestMethod` produces custom objects, i.e .NET objects.

How do we save the report to a file though, so we can store that somewhere and let it be hosted by a web server? Oh, here's an example, pipe it to `Out-File`, like so `ConvertTo-Html | Out-File aliases.htm`

### Overview of the solution

So we have a theory on how to do this:

1. Call `Invoke-RestMethod`.
2. Followed by `ConvertTo-Html`.
3. Followed by `Out-File`.

### Build the solution

Seems almost too easy. Ah well, let's give it whirl. First things first, lets choose a data source, SWAPI, the Star Wars API, cause use the `-Force`, am I right? :)

1. Start `pwsh` in the console.
1. Create a file _web-report.ps1_
1. Add the following code:
  
   ```powershell
   Invoke-RestMethod -URI https://swapi.dev/api/people/1/ |
     ConvertTo-Html |
       Out-File report.htm
   ```

1. Run `./web-report.ps1` (or `.\web-report.ps1` for Windows folks)

   It created a _report.htm_ file for us. Ok, let's have a look:

   ```html
   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title>HTML TABLE</title>
    </head><body>
    <table>
    <colgroup><col/><col/><col/><col/><col/><col/><col/><col/><col/><col/><col/><col/><col/><col/><col/><col/></colgroup>
    <tr><th>name</th><th>height</th><th>mass</th><th>hair_color</th><th>skin_color</th><th>eye_color</th><th>birth_year</th><th>gender</th><th>homeworld</th><th>films</th><th>species</th><th>vehicles</th><th>starships</th><th>created</th><th>edited</th><th>url</th></tr>
    <tr><td>Luke Skywalker</td><td>172</td><td>77</td><td>blond</td><td>fair</td><td>blue</td><td>19BBY</td><td>male</td><td>https://swapi.dev/api/planets/1/</td><td>System.Object[]</td><td>System.Object[]</td><td>System.Object[]</td><td>System.Object[]</td><td>09/12/2014 13:50:51</td><td>20/12/2014 21:17:56</td><td>https://swapi.dev/api/people/1/</td></tr>
    </table>
    </body></html>
   ```

1. Show this file in a browser with `Invoke_Item`:
 
   ```powershell
   Invoke-Item ./report.htm
   ```

   This should start up a browser and you should see something like:

   ![Browser with report](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xj22t64ur5dkmb4o3l9v.png)  

Ok, you could be done here, or we can make it more flexible. We don't like hardcoded values, right? RIGHT?

### Making it flexible

I thought so, now, let's add some parameters for, URL, and report name.

1. Add the following code to the top part of our script _web-report.ps1_:

   ```powershell
   Param(
      [String] $URL = "https://swapi.dev/api/people/1/",
      [String] $Report = "report.htm"
    )
   ```

1. Lets invoke it again, this time with a new URL "https://swapi.dev/api/people/2/":

   ```powershell
   ./web-report.ps1 -URL https://swapi.dev/api/people/2/
   ```

1. Lets check the response in _report.htm_. :

   ```html
   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title>HTML TABLE</title>
    </head><body>
    <table>
    <colgroup><col/><col/><col/><col/><col/><col/><col/><col/><col/><col/><col/><col/><col/><col/><col/><col/></colgroup>
    <tr><th>name</th><th>height</th><th>mass</th><th>hair_color</th><th>skin_color</th><th>eye_color</th><th>birth_year</th><th>gender</th><th>homeworld</th><th>films</th><th>species</th><th>vehicles</th><th>starships</th><th>created</th><th>edited</th><th>url</th></tr>
    <tr><td>C-3PO</td><td>167</td><td>75</td><td>n/a</td><td>gold</td><td>yellow</td><td>112BBY</td><td>n/a</td><td>https://swapi.dev/api/planets/1/</td><td>System.Object[]</td><td>System.Object[]</td><td>System.Object[]</td><td>System.Object[]</td><td>10/12/2014 15:10:51</td><td>20/12/2014 21:17:50</td><td>https://swapi.dev/api/people/2/</td></tr>
    </table>
    </body></html>
   ```

   This time we have `C-3PO`, yup, definitely not Luke, it seems to be working :)
  
### Improve the response

So, so far we had a ton of columns coming back, maybe we just need a few fields from the response, like `name`, `planet` and `height`. Yea let's do that, and justt pick what we need from the response:

1. Let's add `Select-Object` like so:

   ```powershell
   Select-Object name, age, planet |
   ```  

   with the full code looking like so:

   ```powershell
   Param(
      [String] $URL = "https://swapi.dev/api/people/1/",
      [String] $Report = "report.htm"
    )
    Invoke-RestMethod -URI $URL |
      ConvertTo-Html -Property name, height, homeworld |
        Out-File $Report
   ```

1. Let's invoke it again:

   ```powershell
   ./web-report.ps1 -URL https://swapi.dev/api/people/1/
   ```

   and our report now looks like:

   ```html
   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title>HTML TABLE</title>
    </head><body>
    <table>
    <colgroup><col/><col/><col/></colgroup>
    <tr><th>name</th><th>height</th><th>homeworld</th></tr>
    <tr><td>Luke Skywalker</td><td>172</td><td>https://swapi.dev/api/planets/1/</td></tr>
    </table>
    </body></html>
   ```

   Much better :) In fact, reading up a bit, we can just use -Property on `ConvertTo-Html`, so we get:

   ```powershell
   Invoke-RestMethod -URI $URL |
      ConvertTo-Html -Property name, height, homeworld |
        Out-File $Report
   ```

### Make it pretty

In all honesty, this report is bad, no colors, no nothing. Surely, we must be able to pass a CSS file to `ConvertTo-Html`?

Ah yes, looking through the docs there's the parameter `-CssUri` that takes a file path. Let's create a CSS file then.

1. Create `report.css` and add the following CSS:

   ```css
   table {
      border: solid 1px black;
      padding: 10px;
      border-collapse: collapse;
    }
    
    tr:nth-child(even) {background: #CCC}
    tr:nth-child(odd) {background: #FFF}
   ``` 

1. Update the script to take `-CssUri report.css` on `ConvertTo-Html`
1. Run it again, you should see this in the browser:

   ![Table with CSS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4qys7xpntkx9jyep0kbd.png)

## Summary

In summary, we learned that we could use just a few commands, `Invoke-RestMethod`, `ConvertTo-Html` and `Out-File` and boom, we've created ourselves a report.

Full code:

```powershell
Param(
  [String] $URL = "https://swapi.dev/api/people/1/",
  [String] $Report = "report.htm"
)
Invoke-RestMethod -URI $URL |
  ConvertTo-Html -CssUri report.css -Title "Web report" -Property name, height, homeworld |
    Out-File $Report
```