Building a Daily Systems Report Email With PowerShell

A combination of PowerShell and HTML can provide the basis of an easily-scanned report on the basic state of a whole group of servers. Sean combined a range of routine health-checks to provide himself a useful range of information about his servers to his in-box.

Introduction

A little while ago, I demonstrated a way to create a fairly simple disk space report using PowerShell that would be e-mailed to you whenever one of your servers started to get low on disk space. This was done to help solve a commonly occurring issue that I had to deal with; ensuring that servers did not get to low on disk space. I quickly realised that there are many other ‘health checks’ that I often find myself performing on various servers in my day-to-day job. This got me thinking: It would be great to have a more complete systems report that would be e-mailed through on a daily basis, detailing common statistics and information which would otherwise be a bit of a hassle to gather manually.

1459-report.png

Part of a report, showing the HTML formatting and piechart

Once this report was built, it could also serve as a base for other ‘modules’; allowing anyone to add their own bit of functionality. With this in mind, I set about to achieve this goal of creating an automated systems report with a bit of PowerShell magic.

What else can we report on or monitor?

With PowerShell, I like to believe that just about anything is possible. I am constantly finding more and more tasks that can be achieved using this wonderful scripting language. The answer is therefore “anything really”.

We are going to build a systems report that will convey useful information for every server in a list of server names. Too often I find myself staring at dull, boring PowerShell reports that have been converted to HTML. Not in this case! We’ll be using the Microsoft Chart Controls for .NET 3.5 to add some fancy charts to this to keep our report interesting (and easier) to read. The script will aim to gather all of this information, and then e-mail it all across to you. To save a bit of time, we’ll be leveraging the disk space report script I wrote about here.

By modifying this Systems Report, you could easily build something that is customized more to your setting – perhaps you could report on other services running on your servers, or modify the Create-PieChart function to build charts for other services or performance data.

Here is a list of items we’ll be reporting on for each server in the list:

  • System Information
    • System Uptime
    • OS
  • Memory (RAM) Free and Used figures (we’ll also put these into a pie chart)
  • Disk Information
    • All disks with space less than X (Threshold specified)
  • System Processes
    • Top 10 Highest Working Set Memory Usage processes
  • Services
    • Any services that are set to “automatic startup”, yet are found to not be started.
  • System and Application Logs
    • The last few System or Application Event Logs that were of Error or Warning type

Breaking down the Report Script

To keep things as customizable as possible, I have tried to create the reporting script in a reasonably modular fashion. Therefore, each section of information that the script reports on relies on a PowerShell function or cmdlet that retrieves or builds the information we are after. Let’s break the script down and go through what makes things tick. You can download the script here to follow along.

To start with, in the region named “Variables and Arguments”, we define our customizable variables and parameters such as:

  • Mail Server settings (so that the script will email the report to us and knows which SMTP Server to use to send the report).
  • Thresholds and Script customizations (so that you can customize the report to give you as much or as little information you need in the final daily report).
  • Low Disk Space threshold. By default this is set to 20%.
  • Number of Warning or Error type event logs to report on ($EventNum). By default this is set to 3 to retrieve the last 3 event log entries for each Event Log section on each Server.
  • Computer/Server list – the variable called $list accepts a text file as an argument. This is mandatory for the script to run – you basically feed a list of computer names in to the script when it is run, and this variable is populated with that list of computer names.
  • Lastly, “$Report = @()” creates an empty array variable which we’ll use throughout the script to temporarily hold information for each section that uses this variable.

The Variables and Arguments region

We then have a region defined for the various Functions we’ll be using. Let’s go through these functions one at a time. The first function we have is called “Create-PieChart“. This function relies on the Microsoft Chart Controls for Microsoft .NET Framework 3.5 and allows us to generate a graphical chart on the fly in our script and output it to a .PNG graphic file. So do make sure that the system that is running this script has the Chart Controls installed (don’t worry, as it has a very small system footprint).

The function to create a piechart

As this is a custom function created just for this report, let’s run through it in detail. We begin by loading the assemblies needed to use the .NET Chart components. (System.Windows.Forms and System.Windows.Forms.DataVisualization).

1459-piechart.PNG

We now create our chart object and define the size of the chart graphic that we want. You can of course adjust the dimensions defined here depending on how large or small you would like your charts in the report to be.

Next we define a Chart Area, which is where our data will be plotted. This is done using the “New-Object” cmdlet. Once created, we add the Chart Area to the Chart object we created beforehand. We then add a data series to the chart, and move on to actually filling the chart with data by taking the arguments passed into the Create-PieChart function and creating data points with these arguments. (The foreach loop will go through each argument and create a data point, then add it to the Data Series).

Now we specify the chart as a pie chart type, and specify some cosmetic settings for our chart such as the label and drawing styles. Lastly we save the pie chart out to a .PNG file in the same directory that the script is being run from.

Our next Function is called “Get-HostUptime“.

The function to Calculate the Host uptime

This is a fairly simple PowerShell function which has a nice self-explanatory name. It takes a parameter called ComputerName and uses this in a Get-WMIObject call to fetch various Operating System information from the computer specified. The script then singles out the “LastBootTime” property and figures out the uptime value by subtracting “LastBootTime” from the current time and date (which is fetched by using a simple “Get-Date” call). A nicely formatted uptime value is returned at the end which will display nicely in our Report for each system.

For our HTML report to look half decent, we need to specify some HTML and CSS. We build the HTML header into an array called $HTMLHeader. This contains some standard HTML header code as well as some CSS for the various headings, text and more importantly, tables in our report. This will apply to all the plain tables that are generated by PowerShell in our final report and present us with something more interesting to look at in the final product.

The HTML Header ‘HereString’

Now we’ll look at the primary work horse of our reporting script – the main “foreach” loop. In this section, for every computer (server) listed in our $computers list, we loop through the various PowerShell script and cmdlets that retrieve the other information we will be reporting on, all the while appending this information to our $HTMLMiddle array which holds the bulk of our report. In this way, we can loop through every computer or server, gather the information we are looking for, and store it in this array. At the very end of the script we will add the HTML Header, Middle, and End sections together, giving us our final report. Let’s have a quick look at what the main loop covers then…

Starting off, we use the familiar disk space reporting script I talked about earlier to fetch information about any disks that are below the specified threshold percentage in disk space. For this we use the Get-WMIObject cmdlet to gather the relevant information and convert it into an HTML table by piping the output to the ConvertTo-HTML cmdlet at the end.

The Disk space Reporting script

The Operating System name for the current machine is gathered and stored into the $OS variable. To report on Memory figures for each computer or server, we fetch some more Operating System information and store this in the $SystemInfo variable, again using the Get-WMIObject cmdlet. From this point, we then break this up into other variables to store figures such as the Total, Free and Used RAM for each system. We clean up this information a little bit by using built-in [Math] functions to round off the decimal places on each figure.

The System Info Region

Next we’ll gather the top ten (default setting) system processes in terms of those which are using the most memory (Private Working Set memory). This one-liner employs the use of the “Get-Process” cmdlet to get a list of the top processes from the specified machine and select the ones we are interested in. It then sorts these on the Working Set Property in descending order and builds us another HTML table of the results to add to our now growing report. As you’ll notice, this information is stored in the $TopProcesses variable.

Listing the top ten system processes in terms of memory used

Moving on, the Services Report is generated by first declaring an array to use ($ServicesReport) and then fetching a list of services on the machines that are: a) set to “Automatic Startup” and b) currently in a “stopped” state. We do this with a simple “Where” clause in our cmdlet to check for any services that are Stopped and in Automatic Startup mode. After this, we loop through each service in our list of services that were gathered and build a row for our services table, adding information about each service to our row, such as; Name, Service Status and Start Mode. This will give us a good idea of what each service is and what its current state is when viewing the final report each day. Through each loop (each service object we loop through), our $ServicesReport array has the row added. Finally we convert the Services Report into an HTML table for later use in our final report.

The Services report

We then have a similar section called the “Event Logs Report” region. We use the same method we used for gathering the Services information, but this time we use the PowerShell cmdlet for fetching Event Logs called “Get-EventLog“.

The Events Log report

There are two arrays we build this time – one for System Events ($SystemEventsReport) and one for Application Events ($ApplicationEventsReport). We do this by simply changing the “LogName” parameter for the “Get-EventLogs” cmdlet. In each instance, we convert the final array/table into an HTML table where we later add it to our final HTML report.

Remember that Function we defined at the start called “Create-PieChart“? Well, we’ll now use this to generate our pie chart diagram of memory usage and output the file to the current working directory of our script. Now that we know what our Memory usage figures are (and have these stored in variables) we can specify these figures as parameters in our Create-PieChart function and actually generate the chart. Our chart’s filename is unique as it contains the name of the current computer/server being iterated through in our script ($computer).

Creating the Piechart

Just after creating the pie chart, we also add the name of the file that was outputted into an array called $ListOfAttachments. Right at the end, when sending our report out via e-mail, we’ll specify this array for the list of attachments to add to the e-mail (so that our report e-mail has all the images it needs of course)!

The last bit of our main loop ensures that all of the information gathered thus far is added to the $CurrentSystemHTML array. This is a little bit of a tedious section as we manually create some HTML headings and tables using common HTML tags. We also embed our chart image in the HTML using an <IMG> tag. The reason this all needs to be added in the loop, is because this information is all relevant to the current computer or server being looked at by the script. At the end of the loop, you’ll see that we append this HTML to the $HTMLMiddle array where it is stored for the end of our report. Each successive loop of this foreach main loop will add the next system’s information to $HTMLMiddle until we are finished.

The ‘tedious’ end to the main loop.

Finally, we create the $HTMLEnd array which just adds a few closing tags. We then create our final “$HTMLmessageby adding $HTMLHeader, $HTMLMiddle and $HTMLEnd. By piping $HTMLmessage to “Out-File” we can save the report out to an actual HTML file on the hard disk. We also use the “Send-MailMessage” cmdlet to send us our e-mail with the $HTMLmessage as the body of our e-mail. As long as you provided your SMTP server and address details, after running this script you’ll soon have your first system report arriving in your Inbox!

Adding the end tag and sending the email report

Closing off

It is often difficult to be proactive on the job when you constantly have to chase down issues and monitor various remote systems across the network. This is a clear cut case for using PowerShell to help you automate various checks and to repeat those processes that you often find yourself having to do manually. Hopefully this Systems-Reporting script will help bring useful information about your servers directly to your Inbox, as well as catch anything out of place before it becomes too much of an issue.

By modifying the report above you could easily build something that is customized more to your own environment – perhaps you could report on other services running on your servers, or modify the Create-PieChart function to build charts for other services or performance data for example. The sky is the limit when it comes to what PowerShell can do, so do try to think of other areas to report on and get scripting! If you would like to get an idea of what the final report looks like without trying the script yourself, here is a sample I generated from a list of six, Windows Server 2008 R2 machines on a domain. Please do feel free to add any ideas or customizations you may have thought of in the comments section.

The source of the PowerShell script, and a sample report, is in the download link below. You’re welcome to modify it for your own particular requirements.