Powershell HTML Reports
From time to time, you will be asked to create a web report with data that can only be easily obtained by PowerShell. There are hundreds of modules out there that can do this, which is very nice. This blog isn’t about those modules, but instead some tricks I have learned over the years. When it comes to custom HTML reports, these are very customized from data sources like Azure, Active Directory, even SLQlite services. If all possible I like to use the Universal dashboard. But the UD doesn’t translate to emails or IIS pages very well. Let’s get started with the here string.
Here-String
$HereString =@"
Information
"@
A here-string is used to create a block of text. In the example above you see the variable HereString is equal to @””@. This is very important. The @ symbol indicates the start and stops of a here-string. You can place variables inside the here-string as well. This means you can build variables outside of the here-string and plump them into the string. Here is an example of our base for the website.
$HTML = @"
<html>
<head>
<title>Employee List</title>
<style>
</style>
</head>
<body>
</body>
</html>
"@
From this template, we can add CSS coding, powershell tables, lists and much more. The next important item we need to talk about is the convertto-html commandlet.
Convertto-html
This command is the heart of creating tables out of PowerShell information. For example, you want to pull all enabled users from the employees OU and display their displaynames, samaccountnames, department, and title in order. That’s a simple get-aduser command.
$users = Get-ADUser -Filter {enabled -eq $true} `
-SearchBase "OU=hospice Users,DC=Hospice,DC=Local" `
-Properties displayname,samaccountname,title,department `
| Select-Object Displayname,Samaccountname,Title,Department `
| sort-object Displayname
Now we need to convert this array of data into a useable table. This is where convertto-html comes into place. We can tell the command to create a full website just for the tabled information or we can tell it to produce a single table using the as flag and the fragment flag.
$UsersToHTML = $users | ConvertTo-Html -as Table -Fragment
Now we have a user table that we can drop into our Here-string from above. The Here-string is the last thing you will create during this custom process.
$HTML = @"
<html>
<head>
<title>Employee List</title>
<style>
</style>
</head>
<body>
$UsersToHTML
</body>
</html>
"@
This is pretty much the basics of how you would create a site of all users and their user names. You export that site using the >> command. So it would look like this:
$HTML >> <Pathtohtml>.html
Going Deeper – Employee Directory With Images
Let’s go a little deeper by making an employee Directory with pictures. In this scenario, we will assume that the active directory has thumbnail photos inside of it. This Scenario is going to be based on an IIS server and everything is set up for a basic HTML page. I will as
Requirements:
- If a property is not present in the AD account, null or blank, do not display that property.
- All users must have a photo or a placeholder.
- The table must be in alphbetical order by the displayname.
- Required Properities:
- Photo
- Display Name
- Username
- Job Title
- Department
- Mobile Phone
- Office Phone
- Fax
- Manager
The first thing we need to do is grab the data that we need from the active directory. We do that with the get-aduser command.
$users = Get-ADUser -filter {enabled -eq $true} `
-Properties thumbnailPhoto,Displayname,SamAccountName,Title,Department,MobilePhone,OfficePhone,Fax,Manager
There will be two tables that we will be working with. The first table will be two columns and however many rows we will need. The first column will contain the 128×128 active directory thumbnail photo. The second column will contain the Users information. The second column will contain a table with two columns and 8 rows. The first column will be the name of the value and the second column will be the value itself.
I can do this in two ways. I can start the first table inside the here-string or I can create the table before the here-string. I’m going to create the table before the here-string. This way I have a clear image in my head of the table that I am inputting into the here-string. Notice the UserTable is a here-string as well. So we will refer to the Main HTML here-string as the HTML string from here on out.
$UserTable = @"
<table id="myTable">
<tr class="header">
<th style="width:20%;visibility: hidden;"></th>
<th style="width:30%;visibility: hidden;"></th>
</tr>
"@
Now we have a basic HTML table built. From here on out, we will be creating the rows for this table. Inside each row will be the two columns. The photo column and the data column. The data column will contain the table with the employee data on it. It’s time to start looping through that user’s object we created a while ago. We start off with a simple foreach loop. Inside the loop the logic will go as follows:
- Grab the photo from the thumbnail property and save it to the image repo using the username.
- Start the new row. Inside the first column of the new row, we check to see if that photo exists. If it does, then we we create an img tag of the file, if it doesn’t then we target the general image file.
- The second column we blank out the values for each item.
- Then we create the table.
- Then we create if statements that creates each row in table 2. if the users object has the property in question for that user, we set the value and create the <tr> accordingly.
- Finally we close off the second table and the row.
- Then after all of the rows on table 1 is created, we close off the table.
Phew. This is going to be crazy.
The first part is to download the photo in question. If the user’s profile has a thumbnail photo we will download it from AD using the set-content command. (PS5)
foreach ($User in $Users) {
$PhotoPath = "<path>/images/$($user.samaccountname).jpg"
if ($Null -ne $user.thumbnailPhoto) {
$user.thumbnailPhoto | Set-Content $PhotoPath -Encoding byte
}
}
The next step is to determine if the file exists. If it does, we want to create the Image URL towards the new user’s image. If not, we want it to go to the placeholder image. All of this is still within the Loop in question.
if (Test-Path $PhotoPath) {
$ImageURL = "Images/$($user.UserPrincipalName).jpg"
} else {
$ImageURL = "Images/Placeholder.png"
}
Now we need to make a placeholder for each property that we will be using. We do this so the next time the loop processes, we have fresh variables.
$DisplayName = ""
$Username = ""
$JobTitle = ""
$Department = ""
$MobilePhone = ""
$OfficePhone = ""
$Fax = ""
$Manager = ""
Now we need to test these variables and fill them up with HTML code. Each one of these will be a row on the table2. The code below is the same for each of these. All you need to do is replace the <value> with the value in question. The one on the left is what you will replace. The one on the right is an example.
if ($Null -ne $User.<Value>) {
$<value>= @"
<tr>
<td style='font-size:16px; text-align:justify; width:100px;'>
<strong><value>:</strong>
</td>
<td style='vertical-align: top; text-align:left;'>
$($User.<value>)
</td>
</tr>
"@
}
if ($Null -ne $User.OfficePhone) {
$OfficePhone= @"
<tr>
<td style='font-size:16px; text-align:justify; width:100px;'>
<strong>Office Phone:</strong>
</td>
<td style='vertical-align: top; text-align:left;'>
$($User.OfficePhone)
</td>
</tr>
"@
}
Once you create a value for each of these items, it’s time to put it together into the User’s table. AKA table2. We start off by using the $usertable from above combined with a here-string. Remember we are now entering the first table again and creating each row. Here is what the code will look like.
$UserTable = $UserTable + @"
<tr>
<td> <center><a href="$ImageURL" target="_blank"><img src='$ImageURL' style='width:125px; height:125px; border-radius:100px;'></a></center></td>
<td>
<table>
$DisplayName
$UserName
$JobTitle
$Department
$MobilePhone
$OfficePhone
$Fax
$Manager
</table>
</td>
</tr>
"@
We are building this string. This is what you see the $usertable = $usertable + Here-String. All of this is the single row for the table in question. We loop through all of the users using this method and close out the Loop. Once the loop is closed out what we need to do next is close out the table. We do this by adding a final </table> to the end of the table in question.
$UserTable = $UserTable + "</table>"
The final part is placing this table inside an HTML website. It’s as simple as slipping $UserTable between the two bodies.
$HTML = @"
<html>
<head>
<title>Employee List</title>
<style>
</style>
</head>
<body>
$UserTable
</body>
</html>
"@
Now we have an HTML site. We can further edit this and create a nice CSS code with some java-scripts, but I’m not going to get fancy here. The final thing we have to do is export this html page.
$HTML > <pathtosite>\index.html
This process is basically building inside out. We started by the image and then the data. We built accordingly. Now the script itself.
The Script
param (
$Filepath = (Read-Host "File Path for "),
$ImagePath = (Read-Host "Image Folder")
)
$users = Get-ADUser -filter {enabled -eq $true} `
-Properties thumbnailPhoto,Displayname,SamAccountName,Title,Department,MobilePhone,OfficePhone,Fax,Manager
$UserTable = @"
<table id="myTable">
<tr class="header">
<th style="width:20%;visibility: hidden;"></th>
<th style="width:30%;visibility: hidden;"></th>
</tr>
"@
foreach ($User in $Users) {
$PhotoPath = "$ImagePath/$($user.samaccountname).jpg"
if ($Null -ne $user.thumbnailPhoto) {
$user.thumbnailPhoto | Set-Content $PhotoPath -Encoding byte
}
if (Test-Path $PhotoPath) {
$ImageURL = "Images/$($user.UserPrincipalName).jpg"
} else {
$ImageURL = "Images/Placeholder.png"
}
$DisplayName = ""
$Username = ""
$JobTitle = ""
$Department = ""
$MobilePhone = ""
$OfficePhone = ""
$Fax = ""
$Manager = ""
if ($Null -ne $User.displayname) {
$DisplayName= @"
<tr>
<td style='font-size:16px; text-align:justify; width:100px;'>
<strong>Display Name:</strong>
</td>
<td style='vertical-align: top; text-align:left;'>
$($User.displayname)
</td>
</tr>
"@
}
if ($Null -ne $User.samaccountname) {
$Username= @"
<tr>
<td style='font-size:16px; text-align:justify; width:100px;'>
<strong>Username:</strong>
</td>
<td style='vertical-align: top; text-align:left;'>
$($User.samaccountname)
</td>
</tr>
"@
}
if ($Null -ne $User.title) {
$JobTitle = @"
<tr>
<td style='font-size:16px; text-align:justify; width:100px;'>
<strong>Job Title:</strong>
</td>
<td style='vertical-align: top; text-align:left;'>
$($User.title)
</td>
</tr>
"@
}
if ($Null -ne $User.Department) {
$Department= @"
<tr>
<td style='font-size:16px; text-align:justify; width:100px;'>
<strong>Department:</strong>
</td>
<td style='vertical-align: top; text-align:left;'>
$($User.Department)
</td>
</tr>
"@
}
if ($Null -ne $User.MobilePhone) {
$MobilePhone = @"
<tr>
<td style='font-size:16px; text-align:justify; width:100px;'>
<strong>Mobile Phone:</strong>
</td>
<td style='vertical-align: top; text-align:left;'>
$($User.MobilePhone)
</td>
</tr>
"@
}
if ($Null -ne $User.OfficePhone) {
$OfficePhone= @"
<tr>
<td style='font-size:16px; text-align:justify; width:100px;'>
<strong>Office Phone:</strong>
</td>
<td style='vertical-align: top; text-align:left;'>
$($User.OfficePhone)
</td>
</tr>
"@
}
if ($Null -ne $User.Fax) {
$Fax = @"
<tr>
<td style='font-size:16px; text-align:justify; width:100px;'>
<strong>Fax:</strong>
</td>
<td style='vertical-align: top; text-align:left;'>
$($User.Fax)
</td>
</tr>
"@
}
if ($Null -ne $User.Manager) {
$ManagerInfo = $User.Manager.split(',')[0] -replace "CN=",''
$Manager = @"
<tr>
<td style='font-size:16px; text-align:justify; width:100px;'>
<strong>Manager:</strong>
</td>
<td style='vertical-align: top; text-align:left;'>
$($ManagerInfo)
</td>
</tr>
"@
}
$UserTable = $UserTable + @"
<tr>
<td> <center><a href="$ImageURL" target="_blank"><img src='$ImageURL' style='width:125px; height:125px; border-radius:100px;'></a></center></td>
<td>
<table>
$DisplayName
$UserName
$JobTitle
$Department
$MobilePhone
$OfficePhone
$Fax
$Manager
</table>
</td>
</tr>
"@
}
$UserTable = $UserTable + "</table>"
$HTML = @"
<html>
<head>
<title>Employee List</title>
<style>
</style>
</head>
<body>
$UserTable
</body>
</html>
"@
$Html > $Filepath
That’s all folk, yall have a great week!