Click here to monitor SSC
  • Av rating:
  • Total votes: 56
  • Total comments: 50
Sean Duffy

Building a Daily Systems Report Email With PowerShell

16 March 2012

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.

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.
# PowerShell Systems Report
# Example usage: .\SystemsReport.ps1 .\list.txt
# Remember that list.txt is the file containing a list of Server names to run this against

#region Variables and Arguments
$users = "youremail@yourcompany.com" # List of users to email your report to (separate by comma)
$fromemail = "youremail@yourcompany.com"
$server = "yourmailserver.yourcompany.com" #enter your own SMTP server DNS name / IP address here
$list = $args[0] #This accepts the argument you add to your scheduled task for the list of servers. i.e. list.txt
$computers = get-content $list #grab the names of the servers/computers to check from the list.txt file.
# Set free disk space threshold below in percent (default at 10%)
$thresholdspace = 20
[int]$EventNum = 3
[int]$ProccessNumToFetch = 10
$ListOfAttachments = @()
$Report = @()
$CurrentTime = Get-Date
#endregion

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).

Function Create-PieChart() {
       param([string]$FileName)
             
       [void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
       [void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.DataVisualization")
      
       #Create our chart object
       $Chart = New-object System.Windows.Forms.DataVisualization.Charting.Chart
       $Chart.Width = 300
       $Chart.Height = 290
       $Chart.Left = 10
       $Chart.Top = 10

       #Create a chartarea to draw on and add this to the chart
       $ChartArea = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea
       $Chart.ChartAreas.Add($ChartArea)
       [void]$Chart.Series.Add("Data")

       #Add a datapoint for each value specified in the arguments (args)
    foreach ($value in $args[0]) {
              Write-Host "Now processing chart value: " + $value
              $datapoint = new-object System.Windows.Forms.DataVisualization.Charting.DataPoint(0, $value)
           $datapoint.AxisLabel = "Value" + "(" + $value + " GB)"
           $Chart.Series["Data"].Points.Add($datapoint)
       }

       $Chart.Series["Data"].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Pie
       $Chart.Series["Data"]["PieLabelStyle"] = "Outside"
       $Chart.Series["Data"]["PieLineColor"] = "Black"
       $Chart.Series["Data"]["PieDrawingStyle"] = "Concave"
       ($Chart.Series["Data"].Points.FindMaxByValue())["Exploded"] = $true

       #Set the title of the Chart to the current date and time
       $Title = new-object System.Windows.Forms.DataVisualization.Charting.Title
       $Chart.Titles.Add($Title)
       $Chart.Titles[0].Text = "RAM Usage Chart (Used/Free)"

       #Save the chart to a file
       $Chart.SaveImage($FileName + ".png","png")
}

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).

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”.

Function Get-HostUptime {
       param ([string]$ComputerName)
       $Uptime = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $ComputerName
       $LastBootUpTime = $Uptime.ConvertToDateTime($Uptime.LastBootUpTime)
       $Time = (Get-Date) - $LastBootUpTime
       Return '{0:00} Days, {1:00} Hours, {2:00} Minutes, {3:00} Seconds' -f $Time.Days, $Time.Hours, $Time.Minutes, $Time.Seconds
}

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.

# Assemble the HTML Header and CSS for our Report
$HTMLHeader = @"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html><head><title>My Systems Report</title>
<style type="text/css">
<!--
body {
font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
}

    #report { width: 835px; }

    table{
       border-collapse: collapse;
       border: none;
       font: 10pt Verdana, Geneva, Arial, Helvetica, sans-serif;
       color: black;
       margin-bottom: 10px;
}

    table td{
       font-size: 12px;
       padding-left: 0px;
       padding-right: 20px;
       text-align: left;
}

    table th {
       font-size: 12px;
       font-weight: bold;
       padding-left: 0px;
       padding-right: 20px;
       text-align: left;
}

h2{ clear: both; font-size: 130%; }

h3{
       clear: both;
       font-size: 115%;
       margin-left: 20px;
       margin-top: 30px;
}

p{ margin-left: 20px; font-size: 12px; }

table.list{ float: left; }

    table.list td:nth-child(1){
       font-weight: bold;
       border-right: 1px grey solid;
       text-align: right;
}

table.list td:nth-child(2){ padding-left: 7px; }
table tr:nth-child(even) td:nth-child(even){ background: #CCCCCC; }
table tr:nth-child(odd) td:nth-child(odd){ background: #F2F2F2; }
table tr:nth-child(even) td:nth-child(odd){ background: #DDDDDD; }
table tr:nth-child(odd) td:nth-child(even){ background: #E5E5E5; }
div.column { width: 320px; float: left; }
div.first{ padding-right: 20px; border-right: 1px  grey solid; }
div.second{ margin-left: 30px; }
table{ margin-left: 20px; }
-->
</style>
</head>
<body>

"@

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.

$DiskInfo= Get-WMIObject -ComputerName $computer Win32_LogicalDisk | Where-Object{$_.DriveType -eq 3} | Where-Object{ ($_.freespace/$_.Size)*100 -lt $thresholdspace} `
       | Select-Object SystemName, DriveType, VolumeName, Name, @{n='Size (GB)';e={"{0:n2}" -f ($_.size/1gb)}}, @{n='FreeSpace (GB)';e={"{0:n2}" -f ($_.freespace/1gb)}}, @{n='PercentFree';e={"{0:n2}" -f ($_.freespace/$_.size*100)}} | ConvertTo-HTML -fragment

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.

#region System Info
       $OS = (Get-WmiObject Win32_OperatingSystem -computername $computer).caption
       $SystemInfo = Get-WmiObject -Class Win32_OperatingSystem -computername $computer | Select-Object Name, TotalVisibleMemorySize, FreePhysicalMemory
       $TotalRAM = $SystemInfo.TotalVisibleMemorySize/1MB
       $FreeRAM = $SystemInfo.FreePhysicalMemory/1MB
       $UsedRAM = $TotalRAM - $FreeRAM
       $RAMPercentFree = ($FreeRAM / $TotalRAM) * 100
       $TotalRAM = [Math]::Round($TotalRAM, 2)
       $FreeRAM = [Math]::Round($FreeRAM, 2)
       $UsedRAM = [Math]::Round($UsedRAM, 2)
       $RAMPercentFree = [Math]::Round($RAMPercentFree, 2)
       #endregion

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.

$TopProcesses = Get-Process -ComputerName $computer | Sort WS -Descending | Select ProcessName, Id, WS -First $ProccessNumToFetch | ConvertTo-Html -Fragment      

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.

#region Services Report
       $ServicesReport = @()
       $Services = Get-WmiObject -Class Win32_Service -ComputerName $computer | Where {($_.StartMode -eq "Auto") -and ($_.State -eq "Stopped")}

       foreach ($Service in $Services) {
              $row = New-Object -Type PSObject -Property @{
                     Name = $Service.Name
                     Status = $Service.State
                     StartMode = $Service.StartMode
              }
             
       $ServicesReport += $row
      
       }
      
       $ServicesReport = $ServicesReport | ConvertTo-Html -Fragment
       #endregion

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”.

#region Event Logs Report
       $SystemEventsReport = @()
       $SystemEvents = Get-EventLog -ComputerName $computer -LogName System -EntryType Error,Warning -Newest $EventNum
       foreach ($event in $SystemEvents) {
              $row = New-Object -Type PSObject -Property @{
                     TimeGenerated = $event.TimeGenerated
                     EntryType = $event.EntryType
                     Source = $event.Source
                     Message = $event.Message
              }
              $SystemEventsReport += $row
       }
                    
       $SystemEventsReport = $SystemEventsReport | ConvertTo-Html -Fragment
      
       $ApplicationEventsReport = @()
       $ApplicationEvents = Get-EventLog -ComputerName $computer -LogName Application -EntryType Error,Warning -Newest $EventNum
       foreach ($event in $ApplicationEvents) {
              $row = New-Object -Type PSObject -Property @{
                     TimeGenerated = $event.TimeGenerated
                     EntryType = $event.EntryType
                     Source = $event.Source
                     Message = $event.Message
              }
              $ApplicationEventsReport += $row
       }
      
       $ApplicationEventsReport = $ApplicationEventsReport | ConvertTo-Html -Fragment
       #endregion

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).

# Create the chart using our Chart Function
       Create-PieChart -FileName ((Get-Location).Path + "\chart-$computer") $FreeRAM, $UsedRAM
       $ListOfAttachments += "chart-$computer.png"
       #region Uptime
       # Fetch the Uptime of the current system using our Get-HostUptime Function.
       $SystemUptime = Get-HostUptime -ComputerName $computer
       #endregion

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.

# Create HTML Report for the current System being looped through
       $CurrentSystemHTML = @"
       <hr noshade size=3 width="100%">
       <div id="report">
       <p><h2>
$computer Report</p></h2>
       <h3>System Info</h3>
       <table class="list">
       <tr>
       <td>System Uptime</td>
       <td>
$SystemUptime</td>
       </tr>
       <tr>
       <td>OS</td>
       <td>
$OS</td>
       </tr>
       <tr>
       <td>Total RAM (GB)</td>
       <td>
$TotalRAM</td>
       </tr>
       <tr>
       <td>Free RAM (GB)</td>
       <td>
$FreeRAM</td>
       </tr>
       <tr>
       <td>Percent free RAM</td>
       <td>
$RAMPercentFree</td>
       </tr>
       </table>
      
       <IMG SRC="chart-
$computer.png" ALT="$computer Chart">
             
       <h3>Disk Info</h3>
       <p>Drive(s) listed below have less than
$thresholdspace % free space. Drives above this threshold will not be listed.</p>
       <table class="normal">
$DiskInfo</table>
       <br></br>
      
       <div class="first column">
       <h3>System Processes - Top
$ProccessNumToFetch Highest Memory Usage</h3>
       <p>The following
$ProccessNumToFetch processes are those consuming the highest amount of Working Set (WS) Memory (bytes) on $computer</p>
       <table class="normal">
$TopProcesses</table>
       </div>
       <div class="second column">
      
       <h3>System Services - Automatic Startup but not Running</h3>
       <p>The following services are those which are set to Automatic startup type, yet are currently not running on
$computer</p>
       <table class="normal">
      
$ServicesReport
       </table>
       </div>
      
       <h3>Events Report - The last
$EventNum System/Application Log Events that were Warnings or Errors</h3>
       <p>The following is a list of the last
$EventNum <b>System log</b> events that had an Event Type of either Warning or Error on $computer</p>
       <table class="normal">
$SystemEventsReport</table>

       <p>The following is a list of the last
$EventNum <b>Application log</b> events that had an Event Type of either Warning or Error on $computer</p>
       <table class="normal">
$ApplicationEventsReport</table>
"@

       # Add the current System HTML Report into the final HTML Report body
       $HTMLMiddle += $CurrentSystemHTML
      
       }

# Assemble the closing HTML for our report.
$HTMLEnd = @"
</div>
</body>
</html>
"@

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!

# Assemble the final report from all our HTML sections
$HTMLmessage = $HTMLHeader + $HTMLMiddle + $HTMLEnd
# Save the report out to a file in the current path
$HTMLmessage | Out-File ((Get-Location).Path + "\report.html")
# Email our report out
send-mailmessage -from $fromemail -to $users -subject "Systems Report" -Attachments $ListOfAttachments -BodyAsHTML -body $HTMLmessage -priority Normal -smtpServer $server

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 speech-bubble at the head of the article. You're welcome to modify it for your own particular requirements.

Sean Duffy

Author profile:

All round IT geek living in the greater London area. Working as an IT Infrastructure engineer specialising in virtualisation, hosting and support. Sean enjoys all forms of IT, and writes about the bits that intrigue and entertain him on his Shogan.Tech blog. When he’s not learning about the latest Server hardware, platform or software, you'll probably find him developing iPhone apps in objectiveC, gaming online, or on a dirt bike riding enduro-X.

You should follow Sean on Twitter here (@Shogan85)

Search for other articles by Sean Duffy

Rate this article:   Avg rating: from a total of 56 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: Building a Daily Systems Report Email With PowerShell
Posted by: kin (not signed in)
Posted on: Saturday, March 24, 2012 at 3:16 PM
Message: Excellent use of Powershell with nice looking graphs.

Only concern is that we have more than 400+ Prod servers in our environment, so generating and sending out the report will be huge.

Do you know how we can connect to Linux/Unix servers using Powershell to pull out the same info ?

Cheers

Subject: Server use / Linux Use
Posted by: Shogan (view profile)
Posted on: Sunday, March 25, 2012 at 2:16 PM
Message: Hi kin,

Thanks for the comment!

Yes, that is a rather large environment to run this against! I found that the script only took a few seconds to run against 10x Windows Server 2008 R2 machines so I wouldn't imagine it taking too much time to run against that number. I would however recommend testing it against a pre-production environment first if possible, or perhaps a smaller subset of servers in a list at first.

To keep the size down, you could potentially modify the script to capture the details of 20 servers at a time, send off the email, then do the next 20 for example. You could also play with presets to tone down the number of event log or process entries that are pulled out and put into the report. I think the script should be fairly customisable from that point of view, and hopefully allow you to modify it to suit your kind of environment.

With regard to your Linux/Unix servers, that is a whole other ball game when it comes to PowerShell. I don't know of any easy way, however I have recently been playing around with SharpSSH and wodSSH libraries for PowerShell which have allowed me to SSH into servers and execute remote commands against servers, then pull the info generated from these commands back into PowerShell. It may be worth having a look at these libraries to see if they may help.

I hope that helps!

Sean

Subject: 400+ prod servers
Posted by: Dkorzennik (view profile)
Posted on: Sunday, March 25, 2012 at 11:16 PM
Message: Hi. I have a similar scenario and do a similar collection of data using powershell. In South Africa a lot of our servers are across slow (by world standards) WAN links. So just authenticating takes a while. How I get around the time to capture the data on all the servers, and also not have the data collected in a serial fashion, but rather in parallel, is to spawn multiple PS threads, using start-job cmdlet. The trick is to create a scriptblock variable, with all the information, functions, modules and snappins that you need in the job and pass this script block to the start-job. Also, the server names, from a list, or in our case, a query against a SCOM server, must also be passed in as arguments. I then batch the 300+ servers into 25 batches. I then fire off the 1st batch of about 13 servers jobs, do a start-sleep for a few minutes, and then fire off the next batch, in a while loop. I do this batching because if I fire off 300+ start-job processes the server that this is running on is overwhelmed but all these processes and by the data being returned. I can get information like database info, data file info, log file info and whatever else I want in about 15 minutes for 300+ servers. If you need pseudo code for the above, drop me a msg.
David

Subject: 400+ prod servers
Posted by: Dkorzennik (view profile)
Posted on: Sunday, March 25, 2012 at 11:38 PM
Message: Hi. I have a similar scenario and do a similar collection of data using powershell. In South Africa a lot of our servers are across slow (by world standards) WAN links. So just authenticating takes a while. How I get around the time to capture the data on all the servers, and also not have the data collected in a serial fashion, but rather in parallel, is to spawn multiple PS threads, using start-job cmdlet. The trick is to create a scriptblock variable, with all the information, functions, modules and snappins that you need in the job and pass this script block to the start-job. Also, the server names, from a list, or in our case, a query against a SCOM server, must also be passed in as arguments. I then batch the 300+ servers into 25 batches. I then fire off the 1st batch of about 13 servers jobs, do a start-sleep for a few minutes, and then fire off the next batch, in a while loop. I do this batching because if I fire off 300+ start-job processes the server that this is running on is overwhelmed but all these processes and by the data being returned. I can get information like database info, data file info, log file info and whatever else I want in about 15 minutes for 300+ servers. If you need pseudo code for the above, drop me a msg.
David

Subject: Thanks
Posted by: Anonymous (not signed in)
Posted on: Monday, March 26, 2012 at 9:14 AM
Message: I had been planning a similar script but this saves me the effort. Thanks again.

Subject: 400+ prod servers
Posted by: Shogan (view profile)
Posted on: Monday, March 26, 2012 at 5:08 PM
Message: Hi Dkorzennik,

Thanks for the feedback - very interesting scenario you have there. In fact, I can even relate as I am originally from S. Africa myself and had to deal with 64k local municipality WAN links a number of years back! Thanks for the info - I haven't really used the Start-Job/Receive-Job cmdlets yet so I'll definitely be taking a look at them now that you mention it.

If I run into any trouble working with the cmdlets I'll definitely drop you a message over here to find out more, but imagine I should be fine after using get-help on the cmdlets :)

@Anonymous, thanks for the feedback - glad you have found this useful!

Sean

Subject: Monitoring Da Farm
Posted by: Anonymous (not signed in)
Posted on: Monday, April 02, 2012 at 7:27 AM
Message: Why reinvent the wheel? Just use SCOM.

Subject: Integration with sharepoint
Posted by: eddiechits (view profile)
Posted on: Thursday, April 19, 2012 at 8:08 AM
Message: Great stuff just what l need, tell me is it possible to publish the output onto sharepoint?

Subject: sharepoint publishing
Posted by: Shogan (view profile)
Posted on: Thursday, April 19, 2012 at 1:53 PM
Message: Hi eddiechits,

Yes, this should be possible. Seeing as though the report is output to an HTML file and some .PNGs for the graphs, you should be able to use something like this to send the files up to Sharepoint via PowerShell. http://poshcode.org/2122 Give it a try and let us know if that works... :)

Cheers,
Sean

Subject: Power Shell Script on Print Logs
Posted by: avijitd (view profile)
Posted on: Sunday, May 06, 2012 at 9:28 AM
Message: Hello Sean... First of all thanks for this Post.. Its really helpful for me to monitor the servers. Thanks a Ton.

Need small help from you. I want one PowerShell Script which will fetch all the print events from the print server and prepare one excel/HTML report on monthly basis with fields like Printer Shared Name, IP Address, User Name, Document Name, No of Pages Printed, Date & Time etc. and then send that report to mentioned email ID's.
Is possible we can we format the report headings.

Eagerly waiting for your reply…

Thanks in advance…

Subject: CSS
Posted by: itznfb (view profile)
Posted on: Monday, September 17, 2012 at 2:05 PM
Message: I'm having no luck getting the CSS configuration to apply to the html output. It's all there but it just doesn't take affect. Anyone else see this behaviour?

Subject: Advice on extending it?
Posted by: Paulexander (view profile)
Posted on: Friday, September 21, 2012 at 12:19 PM
Message: This is absolutely great, thanks for sharing this. Looking forward to getting this going today.

Do you have any advice on what someone needs to do to extend or edit this? I have a couple of other things I might want to add...

Subject: Connect to multiple SQL instances through Powershell
Posted by: shovan (view profile)
Posted on: Monday, September 24, 2012 at 6:33 AM
Message: Hi Sean,

Thanks a lot for sharing this really useful script.

Could you please let me know if similar kind of script can be created to perform SQL monitoring for multiple instances ? If yes, then it will be really great if you can share how multiple sql instances can be connected from a list of SQL instances.

Subject: replies!
Posted by: Shogan (view profile)
Posted on: Tuesday, September 25, 2012 at 5:51 PM
Message: @Paulexander - what are you looking at adding? If you can find a PowerShell cmdlet that works with the objects you are looking at, the best thing to do is to type "get-help cmdletname" in PowerShell and have a read there - all cmdlets have excellent built-in help :) Otherwise, drop me a message here or on twitter with what you are looking for and I'll try help out!

@shovan - I am not really a SQL guru so can't be of much help there... However another author here, Laerte Junior is quite clued up with both SQL and PowerShell and his articles on Simple-Talk might give you some good ideas... Hope that helps!

@itznfb - perhaps it is the web browser you are using? Have you tried the latest Chrome version? I used Chrome predominantly to test this script when creating it...

@avijitd - I know its an old reply, but see my reply to Paulexander above - best thing to do would be to look for any cmdlets in PowerShell relating to printing and look up their help - give that a go, create a simple script that achieves the first bit you are trying to do, and keep building to it like that - you'll be surprised at how easy PowerShell is to pick up once you get going! :)

Cheers,
Sean

Subject: reply
Posted by: shovan (view profile)
Posted on: Thursday, September 27, 2012 at 8:50 AM
Message: @Sean- Thanks a lot!! Probably, I can make use of Larte's existing script on Simpletalk - http://www.simple-talk.com/sql/sql-tools/the-posh-dba-grown-up-powershell-functions/

Will share it in here is I am able to create it :)

Subject: Something strange
Posted by: Newguy (view profile)
Posted on: Monday, October 22, 2012 at 11:40 PM
Message: Just wanted to start of with saying.. how awesome this script is.

I just seem to be having one little issue,
when the report generates I get about half a page of blank data, then one or two of the servers will be in the report with some of the data, then the last 3 entries have the data I need.

any reason this could happen?
I am running this script against 3 servers in a windows cluster enviroment ( not hyper-V ).

Subject: email error
Posted by: flavor4real (view profile)
Posted on: Wednesday, October 24, 2012 at 1:44 PM
Message: Hello,
This is a very good script. I'm getting a error and wanted to see if someone can point me in the right direction.

Send-MailMessage : Unable to connect to the remote server
At C:\Users\username\Documents\Script_Repository\SystemsReport.ps1:291 char:17
+ send-mailmessage <<<< -from $fromemail -to $users -subject "Systems Report" -Attachments $ListOfAttachments -BodyAsHTML -body $HTMLmessage -priority Normal -smtpServer $server
+ CategoryInfo : InvalidOperation: (System.Net.Mail.SmtpClient:SmtpClient) [Send-MailMessage], SmtpException
+ FullyQualifiedErrorId : SmtpException,Microsoft.PowerShell.Commands.SendMailMessage

Thanks

Subject: .NET 4.0 chart controls
Posted by: davey-kb (view profile)
Posted on: Thursday, November 08, 2012 at 12:13 PM
Message: Will the chart controls for .NET 4.0 be okay?

Thanks!!

Subject: .NET 4.0 chart controls
Posted by: davey-kb (view profile)
Posted on: Friday, November 09, 2012 at 6:53 AM
Message: Will the chart controls for .NET 4.0 be okay?

Thanks!!

Subject: .NET 4.0 chart controls
Posted by: davey-kb (view profile)
Posted on: Friday, November 09, 2012 at 6:54 AM
Message: Will the chart controls for .NET 4.0 be okay?

Thanks!!

Subject: wierd multiple posts.
Posted by: davey-kb (view profile)
Posted on: Friday, November 09, 2012 at 6:55 AM
Message: Sorry, not sure why. I'm going to clear my internet cache. sorry again. everytime I refresh my page it posts again.

Subject: Something strange posted by NewGuy
Posted by: stang32 (view profile)
Posted on: Wednesday, November 21, 2012 at 3:03 PM
Message: I had a similar issue where the first part was blank. After fiddling, I ran the script from CMD and it worked.

Subject: pie chart
Posted by: conivingmedal (view profile)
Posted on: Thursday, March 21, 2013 at 1:35 PM
Message: How would I just do pie charts for disks and make them red if need more disk or green if ok?

Subject: pie chart
Posted by: Shogan (view profile)
Posted on: Tuesday, April 02, 2013 at 10:40 AM
Message: @conivingmedal,

You would need to play with the Create-PieChart function. Instead of feeding it parameters for $FreeRAM and $UsedRAM you would need to feed it the disk capacity and disk free space as variables. For example $capacity and $diskfree if you added those to the report script. To play with the colours, you would need to look at what properties were available and see if you can find one that allows you to specify colours. You could then do a calculation and an IF statement to say that if the result is lower than the threshold, it should give the chart a red/orange colour scheme for example.

The replies below are old, but thought I would answer anyway!

@davey-kb, I have not tried the .NET 4.0 controls but in theory they should work fine and be backward compatible!

@stang, glad you got it working!

Subject: Run this as a scheduled task
Posted by: mberry1212 (view profile)
Posted on: Tuesday, May 28, 2013 at 4:44 PM
Message: Hi,

I've been unable to have this successfully run as a scheduled task.

When it runs, I get the email but there is no data. If I run the same script manually everything is there. I'm assuming this is related to a timeout and the script hasn't gotten the data back before sending the email. How can I set this script to run daily as a scheduled task?

Subject: troubleshooting
Posted by: Shogan (view profile)
Posted on: Tuesday, May 28, 2013 at 5:15 PM
Message: Hi mberry,

I would suggest trying out the troubleshooting in this forum thread: http://exchangeserverpro.com/forums/exchange-server-2010/3472-scheduled-powershell-problem-get-mailboxreport-ps1.html

In particular, try the suggestion of running the batch file that calls the script from cmd prompt to look for any possible errors that are appearing when the script tries to attach the report. I suspect it may be to do with paths...

Cheers,
Sean

Subject: Access denied
Posted by: TechKnow (view profile)
Posted on: Tuesday, July 09, 2013 at 4:43 PM
Message: I really have to hand it to you. I have been working on something much simpler, and have been looking around to see what else is out there. This is the most efficient script that I have seen. Additionally, the presentation is superb. Props.

I get a progression of access denied errors when I run the script. I am guessing that I need to put a -credentials attribute in there somewhere, but I am not sure where.

Again, this script is outstanding.

Subject: Access denied
Posted by: Shogan (view profile)
Posted on: Tuesday, July 09, 2013 at 4:50 PM
Message: Hi TechKnow,

Thanks for the comment and feedback. Good to see this is still proving useful!

I still have it set to run against some core servers I work with and it e-mails me a report each morning.

Yes, the access denied messages are probably to do with credentials for servers - play around with the credentials attribute and you should be able to get it working.

Cheers,
Sean

Subject: Got it
Posted by: TechKnow (view profile)
Posted on: Tuesday, July 09, 2013 at 11:12 PM
Message: I got it. I just have to run it from inside the domain. I can't auth from the net. N/P just have to get the email bit working.

Thanks again!

Subject: Anomaly
Posted by: TechKnow (view profile)
Posted on: Wednesday, July 10, 2013 at 2:32 PM
Message: I have gotten it working and it runs like a champ. It is truly an outstanding script.

There are only two small issues:

1.) It will only send to one email address (this is not really an issue because I am just going to set up a distro and send there"

2.) When I run the report (on let's say two servers) in my test environement, it runs fine, the second time I run it, the reports contains the original report and the new one (it shows reports for both servers twice), if I run it again, same thing happens (it shows both servers 3 times)

It is apparently saving data between results...

Any ideas?

Subject: Hi
Posted by: Shogan (view profile)
Posted on: Wednesday, July 10, 2013 at 2:40 PM
Message: Glad to see you have it running now.

1. Yes, distribution list, or the e-mail cmdlet may even accept a comma separated list of e-mails to send to if I recall correctly.

2. Are you running the script in a loop (while..do for example)? Otherwise that seems a bit odd. If you call the batch file to run the script once every so often as a scheduled task it shouldn't duplicate date. What you could do is at the end of the script logic after the full report is built by doing this, and then subsequently e-mailing the content of $HTMLmessage:

$HTMLmessage = $HTMLHeader + $HTMLMiddle + $HTMLEnd

...

send-mailmessage -from $fromemail -to $users -subject "Systems Report" -Attachments $ListOfAttachments -BodyAsHTML -body $HTMLmessage -priority Normal -smtpServer $server

You could set the $HTMLMiddle = $null

That way the report content is cleared before the next run. Make sure you set it to $null after the report is e-mailed out using send-mailmessage though.

That should do the trick I believe :)

Cheers,
Sean

Subject: Thanks!
Posted by: TechKnow (view profile)
Posted on: Wednesday, July 10, 2013 at 3:58 PM
Message: I will see if I need to even tweak it. My guess is, it will be fine. It appears that after a bit of time it clears whatever cache it has and operates as would be expected.

Whoever you are working for needs to give you a raise.

Subject: Optimized using Remoting
Posted by: zerocool18 (view profile)
Posted on: Tuesday, August 06, 2013 at 6:30 AM
Message: Thanks for Sharing. It's very useful. I added one more feature to "List Shared Folders". Also, I've applied remoting and it has become blazing fast!
I'll share the link soon.

Subject: list.txt location query
Posted by: Gary80 (view profile)
Posted on: Wednesday, August 14, 2013 at 5:58 AM
Message: where must the list.txt file be located?

Subject: list.txt location query
Posted by: Gary80 (view profile)
Posted on: Wednesday, August 14, 2013 at 6:03 AM
Message: where must the list.txt file be located?

Subject: replies
Posted by: Shogan (view profile)
Posted on: Wednesday, August 14, 2013 at 7:05 AM
Message: @zerocool18: You legend! Thanks for taking the time to look at extending the report.

@Gary80: It could in theory be located anywhere, but in this article's example, I store it in the same folder as the PowerShell script itself. Hence when you edit your batch file that calls the script, it refers to the text file with no other path, other than the filename itself (list.txt). So keep it in the same folder.

Sean

Subject: cannot read list.txt
Posted by: Sylar (view profile)
Posted on: Thursday, August 15, 2013 at 2:55 AM
Message: I get the following eror;

Get-Content : Cannot bind argument to parameter 'Path' because it is null.
At C:\scripts\SystemsReport.ps1:10 char:25
+ $computers = get-content <<<< $list #grab the names of the servers/computers to check from the list.txt file.
+ CategoryInfo : InvalidData: (:) [Get-Content], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.GetContentCommand

The list.txt is in the same folder as the script

Please help

Subject: Adding CPU value export to CSV
Posted by: Harvey (view profile)
Posted on: Wednesday, October 09, 2013 at 7:38 AM
Message: Hi,

Whatr would be the best way to add a CPU value into this?

Also would it be possible to export data values to csv?

Cheers

Subject: Adding CPU value export to CSV
Posted by: Harvey (view profile)
Posted on: Wednesday, October 09, 2013 at 10:48 AM
Message: Hi,

Whatr would be the best way to add a CPU value into this?

Also would it be possible to export data values to csv?

Cheers

Subject: Adding CPU value export to CSV
Posted by: Harvey (view profile)
Posted on: Thursday, October 10, 2013 at 2:46 AM
Message: Hi,

Whatr would be the best way to add a CPU value into this?

Also would it be possible to export data values to csv?

Cheers

Subject: Adding CPU value export to CSV
Posted by: Harvey (view profile)
Posted on: Thursday, October 10, 2013 at 4:03 AM
Message: Hi,

Whatr would be the best way to add a CPU value into this?

Also would it be possible to export data values to csv?

Cheers

Subject: Adding CPU value export to CSV
Posted by: Harvey (view profile)
Posted on: Thursday, October 10, 2013 at 5:17 AM
Message: Hi,

Whatr would be the best way to add a CPU value into this?

Also would it be possible to export data values to csv?

Cheers

Subject: Adding CPU value export to CSV
Posted by: Harvey (view profile)
Posted on: Thursday, October 10, 2013 at 8:04 AM
Message: Hi,

Whatr would be the best way to add a CPU value into this?

Also would it be possible to export data values to csv?

Cheers

Subject: reply
Posted by: Shogan (view profile)
Posted on: Thursday, October 10, 2013 at 8:49 AM
Message: Hi Harvey,

for some reason you comment keeps on getting spammed in. Careful you don't keep refreshing the page after having submitted one- I have found this sometimes duplicates posts on Simple-talk :)

To answer your question -> It would be easy to add CPU values in. Just simply duplicate a section of the PowerShell code that handles one bit - such as services, and use a different WMI query. You can do a search on google for the msdn documentation on WMI queries and find the WMI query reponsible for CPU info.

Regarding CSV, you can use the ConvertTo-Csv cmdlet in PowerShell. So where you for example use:

$ServicesReport = $ServicesReport | ConvertTo-Html -Fragment

to convert the services module report to HTML, you could also add a line just above that like this:

$ServicesReport | ConvertTo-Csv | Out-File myreport.csv

This should pipe everything in $ServicesReport to Csv and into an output file in the same directory called myreport.csv. That is off the top of my head, but it should do the trick.

Cheers,
Sean

Subject: Thanks
Posted by: Harvey (view profile)
Posted on: Thursday, October 10, 2013 at 9:03 AM
Message: Hi Sean,

I saw the duplicates too I think my workstation was having a bit of a fit!!

I sort of figured the CPU section out (plus a few other extra's :) ) by adding this -

$SystemCPU = gwmi win32_processor -computername $computer | Measure-Object -property LoadPercentage -Average | Select Average

However in the report I get this - @{Average=52.25} as the value which I am still trying to figure out.

Ill give the csv code a go as well. I'd also like to attach that to the email as well but that looks fairly straighforward!

Thanks for replying and for the main script as well.

Harvey



Subject: reply
Posted by: Shogan (view profile)
Posted on: Thursday, October 10, 2013 at 9:54 AM
Message: Hi Harvey,

Great - glad you got it going. Try look for an "Average" property on the $SystemCPU variable - so try something like $SystemCPU.Average instead of just $SystemCPU when you output it in your report - that should sort out the @{...} bit.

Cheers!

Subject: Average
Posted by: Harvey (view profile)
Posted on: Friday, October 11, 2013 at 2:09 AM
Message: $SystemCPU.Average that sorted that out thanks.
Not having any luck with exporintg to csv So I'm goign to spilt out the relevant sections and do that seperately i think.

Subject: Performance counters
Posted by: rcx001 (view profile)
Posted on: Wednesday, April 02, 2014 at 4:24 PM
Message: Hello, nice script! How would I add performance counters? I'm still new to PS and can gen up the counters but still new to exporting info to HTML. Can you give me an example? Thanks!

Subject: Enable CPU Usage Information in this script
Posted by: anandfranklin (view profile)
Posted on: Tuesday, April 08, 2014 at 5:27 AM
Message: Hello Sean,

Recently, I came across this PowerShell script (SystemsReport.ps1) and found to be very useful in getting the real-time information from multiple servers, except for the CPU information which need to be captured via the same script.

What information should be added under which line, please help me on this.

Thank You, Anand

Subject: replies
Posted by: Shogan (view profile)
Posted on: Tuesday, April 08, 2014 at 9:27 AM
Message: Hi @rcx001 and Anand :)

Seems both of you guys could use more WMI queries to get what you are looking for.

So, rcx, you probably want to look into retrieving perf counters using Get-WMIObject. Just as we do with other bits of info in this report, you can do something similar. Here is an article that has an example of getting some CPU perf stats: http://itknowledgeexchange.techtarget.com/powershell/wmi-and-performance-counters-i/

@Anand, you'll also want to use Get-WMIObject to fetch info about the target remote machines' CPUs. Here is an excellent example: http://blogs.technet.com/b/heyscriptingguy/archive/2011/09/26/use-powershell-and-wmi-to-get-processor-information.aspx

Cheers,
Sean

 

Top Rated

PowerShell One-Liners: Variables, Parameters, Properties, and Objects
 PowerShell isn't a conventional language, though it draws inspiration widely. Many people learn it, and... Read more...

Migrating to Microsoft BPOS - Part II
 In his last article, Johan gave us a crystal clear guide to preparing to migrate from an on-premises... Read more...

Emulating the Exchange 2003 RUS for Out-of-Band Mailbox Provisioning in Exchange 2007
 Exchange's Recipient Update Service was important in Exchange 2000 or 2003 in order to complete the... Read more...

The Postmasters
 The Exchange Team introduces themselves, and keeps you up-to-date Read more...

For this Exchange Server Archiver, “Transparency” Fits
 Sometimes, it is a great relief when a user of your software gives it a tough test and then reports... Read more...

Most Viewed

Upgrade Exchange 2003 to Exchange 2010
  In this article, the first of two in which Jaap describes how to move from Exchange Server 2003... Read more...

Upgrade Exchange 2003 to Exchange 2010 - Part II
 In Jaap's second article on upgrading straight from Exchange Server 2003 to 2010, he explains how to... Read more...

Goodbye Exchange ExMerge, Hello Export-Mailbox
 ExMerge was a great way of exporting a mailbox to an Exchange PST file, or for removing all occurences... Read more...

Exchange E-mail Addresses and the Outlook Address Cache
 Because Exchange auto-complete cache uses X.500 addresses for e-mail sent to addresses within the... Read more...

Using Exchange 2007 for Resource Booking
 The process of booking various resources to go with a meeting room just got a whole lot easier with... Read more...

Why Join

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.