Skip to content

Instantly share code, notes, and snippets.

@theagreeablecow
Last active July 4, 2018 00:18
Show Gist options
  • Save theagreeablecow/3682550 to your computer and use it in GitHub Desktop.
Save theagreeablecow/3682550 to your computer and use it in GitHub Desktop.
SysAdmin Modular Reporting (SAMReports)
############################################################################################################
# Global Variables #
#------------------------
# Email Variables
$SmtpServer = "exchange.mydomain.com.au"
$EmailFrom = "[email protected]"
$EmailTo = "[email protected]"
$EmailSubject = "SysAdmin Modular Report for $module $($Date)"
# Report HTML Formatting
$DefaultBannerColour = "848284"
$BannerTxtColour = "FFFFFF"
$BannerTxtSize = "18pt"
$DefaultTitleColour = "D4DBDC"
$GoodTitleColour = "4EA846"
$WarningTitleColour = "FFB901"
$AlertTitleColour = "BD1917"
$TitleTxtColour = "FFFFFF"
$DefaultCommentColour = "F3F4F4"
$TableTxtColour = "000000"
$TableTxtSize = "10pt"
# Report Variables
$ReportTitle = $EmailSubject
$ReportSubTitle = "Created by <a href='sip:[email protected]' target='_blank'>The Agreeable Cow</a>, generated on $($ENV:Computername)."
#Environment Variables
$ScriptComputer = ($ENV:Computername)
$ScriptUser = ($ENV:Username)
############################################################################################################
# HTML Styles #
#------------------------
$DspHeader0 = "
BORDER-RIGHT: #bbbbbb 1px solid;
PADDING-RIGHT: 0px;
BORDER-TOP: #bbbbbb 1px solid;
DISPLAY: block;
PADDING-LEFT: 0px;
FONT-WEIGHT: bold;
FONT-SIZE: $($TableTxtSize);
MARGIN-BOTTOM: -1px;
MARGIN-LEFT: 0px;
BORDER-LEFT: #bbbbbb 1px solid;
COLOR: #$($BannerTxtColour);
MARGIN-RIGHT: 0px;
PADDING-TOP: 4px;
BORDER-BOTTOM: #bbbbbb 1px solid;
FONT-FAMILY: Tahoma;
POSITION: relative;
HEIGHT: 2.25em;
WIDTH: 95%;
TEXT-INDENT: 10px;
BACKGROUND-COLOR: #$($ReportBannerColour);
"
$DspHeader1 = "
BORDER-RIGHT: #bbbbbb 1px solid;
PADDING-RIGHT: 0px;
BORDER-TOP: #bbbbbb 1px solid;
DISPLAY: block;
PADDING-LEFT: 0px;
FONT-WEIGHT: bold;
FONT-SIZE: $($TableTxtSize);
MARGIN-BOTTOM: -1px;
MARGIN-LEFT: 0px;
BORDER-LEFT: #bbbbbb 1px solid;
COLOR: #$($TitleTxtColour);
MARGIN-RIGHT: 0px;
PADDING-TOP: 4px;
BORDER-BOTTOM: #bbbbbb 1px solid;
FONT-FAMILY: Tahoma;
POSITION: relative;
HEIGHT: 2.25em;
WIDTH: 95%;
TEXT-INDENT: 10px;
BACKGROUND-COLOR: #$($DefaultTitleColour);
"
$DspHeaderGood = "
BORDER-RIGHT: #bbbbbb 1px solid;
PADDING-RIGHT: 0px;
BORDER-TOP: #bbbbbb 1px solid;
DISPLAY: block;
PADDING-LEFT: 0px;
FONT-WEIGHT: bold;
FONT-SIZE: $($TableTxtSize);
MARGIN-BOTTOM: -1px;
MARGIN-LEFT: 0px;
BORDER-LEFT: #bbbbbb 1px solid;
COLOR: #$($TitleTxtColour);
MARGIN-RIGHT: 0px;
PADDING-TOP: 4px;
BORDER-BOTTOM: #bbbbbb 1px solid;
FONT-FAMILY: Tahoma;
POSITION: relative;
HEIGHT: 2.25em;
WIDTH: 95%;
TEXT-INDENT: 10px;
BACKGROUND-COLOR: #$($GoodTitleColour);
"
$DspHeaderWarning = "
BORDER-RIGHT: #bbbbbb 1px solid;
PADDING-RIGHT: 0px;
BORDER-TOP: #bbbbbb 1px solid;
DISPLAY: block;
PADDING-LEFT: 0px;
FONT-WEIGHT: bold;
FONT-SIZE: $($TableTxtSize);
MARGIN-BOTTOM: -1px;
MARGIN-LEFT: 0px;
BORDER-LEFT: #bbbbbb 1px solid;
COLOR: #$($TitleTxtColour);
MARGIN-RIGHT: 0px;
PADDING-TOP: 4px;
BORDER-BOTTOM: #bbbbbb 1px solid;
FONT-FAMILY: Tahoma;
POSITION: relative;
HEIGHT: 2.25em;
WIDTH: 95%;
TEXT-INDENT: 10px;
BACKGROUND-COLOR: #$($WarningTitleColour);
"
$DspHeaderAlert = "
BORDER-RIGHT: #bbbbbb 1px solid;
PADDING-RIGHT: 0px;
BORDER-TOP: #bbbbbb 1px solid;
DISPLAY: block;
PADDING-LEFT: 0px;
FONT-WEIGHT: bold;
FONT-SIZE: $($TableTxtSize);
MARGIN-BOTTOM: -1px;
MARGIN-LEFT: 0px;
BORDER-LEFT: #bbbbbb 1px solid;
COLOR: #$($TitleTxtColour);
MARGIN-RIGHT: 0px;
PADDING-TOP: 4px;
BORDER-BOTTOM: #bbbbbb 1px solid;
FONT-FAMILY: Tahoma;
POSITION: relative;
HEIGHT: 2.25em;
WIDTH: 95%;
TEXT-INDENT: 10px;
BACKGROUND-COLOR: #$($AlertTitleColour);
"
$dspcomments = "
BORDER-RIGHT: #bbbbbb 1px solid;
PADDING-RIGHT: 0px;
BORDER-TOP: #bbbbbb 1px solid;
DISPLAY: block;
PADDING-LEFT: 0px;
FONT-WEIGHT: bold;
FONT-SIZE: $($TableTxtSize);
MARGIN-BOTTOM: -1px;
MARGIN-LEFT: 0px;
BORDER-LEFT: #bbbbbb 1px solid;
COLOR: #$($TitleTxtColour);
MARGIN-RIGHT: 0px;
PADDING-TOP: 4px;
BORDER-BOTTOM: #bbbbbb 1px solid;
FONT-FAMILY: Tahoma;
POSITION: relative;
HEIGHT: 2.25em;
WIDTH: 95%;
TEXT-INDENT: 10px;
COLOR: #000000;
FONT-STYLE: ITALIC;
FONT-WEIGHT: normal;
FONT-SIZE: $($TableTxtSize);
BACKGROUND-COLOR: #$($DefaultCommentColour)
"
$filler = "
BORDER-RIGHT: medium none;
BORDER-TOP: medium none;
DISPLAY: block;
BACKGROUND: none transparent scroll repeat 0% 0%;
MARGIN-BOTTOM: -1px;
FONT: 100%/8px Tahoma;
MARGIN-LEFT: 43px;
BORDER-LEFT: medium none;
COLOR: #ffffff;
MARGIN-RIGHT: 0px;
PADDING-TOP: 4px;
BORDER-BOTTOM: medium none;
POSITION: relative
"
$dspcont ="
BORDER-RIGHT: #bbbbbb 1px solid;
BORDER-TOP: #bbbbbb 1px solid;
PADDING-LEFT: 0px;
FONT-SIZE: $($TableTxtSize);
MARGIN-BOTTOM: -1px;
PADDING-BOTTOM: 5px;
MARGIN-LEFT: 0px;
BORDER-LEFT: #bbbbbb 1px solid;
WIDTH: 95%;
COLOR: #000000;
MARGIN-RIGHT: 0px;
PADDING-TOP: 4px;
BORDER-BOTTOM: #bbbbbb 1px solid;
FONT-FAMILY: Tahoma;
POSITION: relative;
BACKGROUND-COLOR: #f9f9f9
"
############################################################################################################
# Style Sheet Functions #
#------------------------
# Functions Credit to Alan Renouf http://virtu-al.net vCheck
Function Get-CustomHeader0 ($Title){
$Report = @"
<div style="margin: 0px auto;">
<h1 style="$($DspHeader0)">$($Title)</h1>
<div style="$($filler)"></div>
"@
Return $Report
}
Function Get-CustomHeader ($Title, $Cmnt, $Alert){
if ($Alert -eq "Good") {
$Report = @"
<h2 style="$($DspHeaderGood)">$($Title)</h2>
"@
}
elseif ($Alert -eq "Warning") {
$Report = @"
<h2 style="$($DspHeaderWarning)">$($Title)</h2>
"@
}
elseif ($Alert -eq "Alert") {
$Report = @"
<h2 style="$($DspHeaderAlert)">$($Title)</h2>
"@
}
else {
$Report = @"
<h2 style="$($dspheader1)">$($Title)</h2>
"@
}
If ($Cmnt) {
$Report += @"
<div style="$($dspcomments)">$($Cmnt)</div>
"@
}
$Report += @"
<div style="$($dspcont)">
"@
Return $Report
}
Function Get-CustomHeaderClose ($Footer){
$Report = @"
</DIV>
<div style="$($filler)">$($Footer)</div>
"@
Return $Report
}
Function Get-CustomHeader0Close{
$Report = @"
</DIV>
"@
Return $Report
}
Function Get-CustomHTMLClose{
$Report = @"
</div>
</body>
</html>
"@
Return $Report
}
Function Get-ReportHeaderHTML ($Header){
$Report = @"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html><head><title>$($Header)</title>
<META http-equiv=Content-Type content='text/html; charset=windows-1252'>
<style type="text/css">
TABLE {
TABLE-LAYOUT: fixed;
font-family: Tahoma;
font-size:$($TableTxtSize);
WIDTH: 100%
}
*
{
margin:0
}
.pageholder {
margin: 0px auto;
}
td {
VERTICAL-ALIGN: TOP;
FONT-FAMILY: Tahoma
}
th {
VERTICAL-ALIGN: TOP;
COLOR: #$($TableTxtColour);
TEXT-ALIGN: left
}
</style>
</head>
<body margin-left: 4pt; margin-right: 4pt; margin-top: 6pt;>
<div style="font-family:Arial, Helvetica, sans-serif; font-size:$($BannerTxtSize); font-weight:bolder; background-color: ##BANNER_MARKER#;"><center>
<p class="accent">
<H1><FONT COLOR= $($BannerTxtColour)>$($ReportTitle)</Font></H1>
</p>
</center></div>
<div style="font-family:Arial, Helvetica, sans-serif; font-size:14px; font-weight:bold;"><center>$($ReportSubTitle)
</center></div>
"@
Return $Report
}
Function Get-HTMLTable {
param([array]$Data)
$HTMLTable = $Data | ConvertTo-Html -Fragment
$HTMLTable = $HTMLTable -Replace '<TABLE>', '<TABLE><style>tr:nth-child(even) { background-color: #e5e5e5; TABLE-LAYOUT: Fixed; FONT-SIZE: 100%; WIDTH: 100%}</style>'
$HTMLTable = $HTMLTable -Replace '<td>', '<td style= "FONT-FAMILY: Tahoma; FONT-SIZE: $($TableTxtSize);">'
$HTMLTable = $HTMLTable -Replace '<th>', '<th style= "COLOR: #$($DefaultBannerColour); FONT-FAMILY: Tahoma; FONT-SIZE: $($TableTxtSize);">'
$HTMLTable = $HTMLTable -replace '&lt;', "<"
$HTMLTable = $HTMLTable -replace '&gt;', ">"
Return $HTMLTable
}
Function Get-HTMLText ($OutText){
$Report = @"
<TABLE>
<tr>
$OutText
</tr>
</TABLE>
"@
Return $Report
}
Function Get-HTMLTable {
param([array]$OutData)
$HTMLTable = $OutData | ConvertTo-Html -Fragment
$HTMLTable = $HTMLTable -Replace '<TABLE>', '<TABLE><style>tr:nth-child(even) { background-color: #e5e5e5; TABLE-LAYOUT: Fixed; FONT-SIZE: 100%; WIDTH: 100%}</style>'
$HTMLTable = $HTMLTable -Replace '<td>', '<td style= "FONT-FAMILY: Tahoma; FONT-SIZE: $($TableTxtSize);">'
$HTMLTable = $HTMLTable -Replace '<th>', '<th style= "COLOR: #$($ReportBannerColour); FONT-FAMILY: Tahoma; FONT-SIZE: $($TableTxtSize);">'
$HTMLTable = $HTMLTable -replace '&lt;', "<"
$HTMLTable = $HTMLTable -replace '&gt;', ">"
Return $HTMLTable
}
############################################################################################################
# Manage Credentials #
#------------------------
function Get-MyCredential
{
param(
$CredPath,
[switch]$Help
)
$HelpText = @"
Get-MyCredential
Usage:
Get-MyCredential -CredPath <[CredPath]>
eg $Credentials = Get-MyCredential (join-path ($ScriptPath) Syncred.xml)
If a credential is stored in $CredPath, it will be used.
If no credential is found, Export-Credential will start and offer to
Store a credential at the location specified.
"@
if($Help -or (!($CredPath))){write-host $Helptext; Break}
if (!(Test-Path -Path $CredPath -PathType Leaf)) {
Export-Credential (Get-Credential) $CredPath
}
$cred = Import-Clixml $CredPath
$cred.Password = $cred.Password | ConvertTo-SecureString
$Credential = New-Object System.Management.Automation.PsCredential($cred.UserName, $cred.Password)
Return $Credential
}
function Export-Credential($cred, $path) {
$cred = $cred | Select-Object *
$cred.password = $cred.Password | ConvertFrom-SecureString
$cred | Export-Clixml $path
}
############################################################################################################
# Veeam Functions #
#------------------------
# Functin credit to ThomasMc http://vpowercli.wordpress.com/2012/01/23/vpowercli-v6-army-report/
function Get-vPCRepoInfo {
[CmdletBinding()]
param (
[Parameter(Position=0, ValueFromPipeline=$true)]
[PSObject[]]$Repository
)
Begin {
$outputAry = @()
[Reflection.Assembly]::LoadFile("C:\Program Files\Veeam\Backup and Replication\Veeam.Backup.Common.dll") | Out-Null
function Build-Object {param($name, $path, $free, $total)
$repoObj = New-Object -TypeName PSObject -Property @{
Target = $name
Storepath = $path
StorageFree = [Math]::Round([Decimal]$free/1GB,2)
StorageTotal = [Math]::Round([Decimal]$total/1GB,2)
FreePercentage = [Math]::Round(($free/$total)*100)
}
return $repoObj | Select Target, Storepath, StorageFree, StorageTotal, FreePercentage
}
}
Process {
foreach ($r in $Repository) {
if ($r.GetType().Name -eq [String]) {
$r = Get-VBRBackupRepository -Name $r
}
if ($r.Type -eq "WinLocal") {
$Server = $r.GetHost()
$FileCommander = [Veeam.Backup.Core.CRemoteWinFileCommander]::Create($Server.Info)
$storage = $FileCommander.GetDrives([ref]$null) | ?{$_.Name -eq $r.Path.Substring(0,3)}
$outputObj = Build-Object $r.Name $r.Path $storage.FreeSpace $storage.TotalSpace
}
elseif ($r.Type -eq "LinuxLocal") {
$Server = $r.GetHost()
$FileCommander = new-object Veeam.Backup.Core.CSshFileCommander $server.info
$storage = $FileCommander.FindDirInfo($r.Path)
$outputObj = Build-Object $r.Name $r.Path $storage.FreeSpace $storage.TotalSize
}
elseif ($r.Type -eq "CifsShare") {
$fso = New-Object -Com Scripting.FileSystemObject
$storage = $fso.GetDrive($r.Path)
$outputObj = Build-Object $r.Name $r.Path $storage.AvailableSpace $storage.TotalSize
}
$outputAry = $outputAry + $outputObj
}
}
End {
$outputAry
}
}
# Functin credit to ThomasMc http://vpowercli.wordpress.com/2012/01/23/vpowercli-v6-army-report/
function Get-vPCReplicaTarget {
[CmdletBinding()]
param(
[Parameter(Position=0)]
[String]$Name,
[Parameter(Position=1, ValueFromPipeline=$true)]
[PSObject[]]$InputObj
)
BEGIN {
$outputAry = @()
$dsAry = @()
if (($Name -ne $null) -and ($InputObj -eq $null)) {
$InputObj = Get-VBRJob -Name $Name
}
}
PROCESS {
foreach ($obj in $InputObj) {
if (($dsAry -contains $obj.ViReplicaTargetOptions.DatastoreName) -eq $false) {
$esxi = $obj.GetTargetHost()
$dtstr = $esxi | Find-VBRDatastore -Name $obj.ViReplicaTargetOptions.DatastoreName
$objoutput = New-Object -TypeName PSObject -Property @{
Target = $esxi.Name
Datastore = $obj.ViReplicaTargetOptions.DatastoreName
StorageFree = [Math]::Round([Decimal]$dtstr.FreeSpace/1GB,2)
StorageTotal = [Math]::Round([Decimal]$dtstr.Capacity/1GB,2)
FreePercentage = [Math]::Round(($dtstr.FreeSpace/$dtstr.Capacity)*100)
}
$dsAry = $dsAry + $obj.ViReplicaTargetOptions.DatastoreName
$outputAry = $outputAry + $objoutput
}
else {
return
}
}
}
END {
$outputAry | Select Target, Datastore, StorageFree, StorageTotal, FreePercentage
}
}
# Get the current product version of Veeam
function Get-VeeamVersion {
$veeamExe = Get-Item 'C:\Program Files\Veeam\Backup and Replication\Veeam.Backup.Manager.exe'
$VeeamVersion = $veeamExe.VersionInfo.ProductVersion
Return $VeeamVersion
}
############################################################################################################
# Visuals #
#------------------------
Function Get-Base64Image ($Path) {
$pic = Get-Content $Path -Encoding Byte
[Convert]::ToBase64String($pic)
}
# Function credit to Sean Duffy http://www.simple-talk.com/sysadmin/powershell/building-a-daily-systems-report-email-with-powershell/
# NB Required MS Chart Controls for .NET 3.5 installed (http://www.microsoft.com/en-us/download/details.aspx?id=14422)
Function Create-PieChart() {
param([string]$FileName)
[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.DataVisualization")
#Create chart object
$Chart = New-object System.Windows.Forms.DataVisualization.Charting.Chart
$Chart.Width = 150
$Chart.Height = 150
$Chart.Left = 1
$Chart.Top = 1
#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]) {
$datapoint = new-object System.Windows.Forms.DataVisualization.Charting.DataPoint(0, $value)
$datapoint.AxisLabel = $value
$Chart.Series["Data"].Points.Add($datapoint)
}
# Manually Select Colours
#$Colour1 = "#" + $GoodTitleColour
#$Colour2 = "#" + $WarningTitleColour
#$Colour3 = "#" + $AlertTitleColour
#if ($args[0]){
# $datapoint.Color = $Colour1
#}
#if ($args[1]){
# $datapoint.Color = $Colour2
#}
#if ($args[2]){
# $datapoint.Color = $Colour3
#}
$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 = "My Pie Chart"
#Save the chart to a file
$Chart.SaveImage($FileName,"png")
}
############################################################################################################
# Misc #
#---------------------
function Get-DirectoryStats() {
param ([string]$root = $(resolve-path .))
Get-ChildItem $root -recurse -force | where { -not $_.PSIsContainer } | measure-object -sum -property Length
}
############################################################################################################
# Info #
#------------------------
$Title = "Example Script"
$Comment = "This example script shows the top 10 process (by Working set Memory)"
$Author = "John Example"
$PluginDate = "1/1/2013"
$Version = "v1.0"
# 1.0 01/01/2013 John Example Original Build
############################################################################################################
# Main Script #
#------------------------
#Create an Array and run query
$ResultsData = @()
$Processes = get-process | sort-object ws -descending | select -first 10 | select-object id,Name,ws
# Add results into the array
foreach ($Process in $Processes){
$obj = New-Object PSobject
$Memory = ($Process.WS/1mb)
$obj | Add-Member -MemberType NoteProperty -name "Server" -value $($ENV:Computername)
$obj | Add-Member -MemberType NoteProperty -name "Process ID" -value $Process.ID
$obj | Add-Member -MemberType NoteProperty -name "Process Name" -value $Process.Name
$obj | Add-Member -MemberType NoteProperty -name "Memory (WS)" -value $Memory
$ResultsData += $obj
# Update Text and Alert count based on your criteria
if ($Memory -gt 300){
$AlertText += "Alert: High memory usage for " + $Process.name + " </br>"
$AlertCount += $AlertCount.count + 1
}
elseif ($Memory -gt 150){
$WarningText += "Warning: Moderate memory usage for " + $Process.name + " </br>"
$WarningCount += $WarningCount.count + 1
}
}
# Results Text
if ($AlertText -ne $null -or $WarningText -ne $null){
$ResultsText = $AlertText + $WarningText
}
else{
$ResultsText = "Memory usage normal"
}
# Results Data
$ResultsData = $ResultsData | sort -Property "Name"
# Results Alert
if ($AlertCount -ge 1){
$ResultsAlert = "Alert"
}
elseif ($WarningCount -ge 1){
$ResultsAlert = "Warning"
}
else{
$ResultsAlert = "Good"
}
############################################################################################################
# Output #
#------------------------
$OutText = $ResultsText # $OutText MUST be either $ResultsText or "" Valid $ResultsText is any text string
$OutData = $ResultsData # $OutData MUST be either $ResultsData or "" Valid $ResultsData is any data array
$OutAlert = $ResultsAlert # $OutAlert MUST be either $ResultsAlert or "" Valid $ResultsAlert are 'Good', 'Warning' or 'Alert'
$Attachment = "" # $Attachment MUST be either UNC path or ""
<#
.SYNOPSIS
The SysAdmin Modular Reporting framework provides flexible data collection and 'traffic light' style alerts for your environment
.DESCRIPTION
Using a modular plugin system, this report collates information from a series of sub-scripts, specific to the module chosen
Get-SAMReport.ps1 This script, used to call all other scripts and join report together
\_Assets
00_Global_Variables.ps1 Top level variables, applicable for all module sets <- Update this script accordingly
01_Global_StyleSheet.ps1 Top level HTML stylesheet, applicable for all module sets
02_Global_Functions.ps1 Top level functions, applicable for all module sets
Scheduled Task Example.xml Example task that can be imported into Task Scheduler
\<module name>
00 Module Variables.ps1 Module specific plugins, variables and overrides <- Update this script accordingly
01 Example Template.ps1 Get-Process demonstration showing example modular script format
\Output All generated output files for this module are saved here
\zzz_Ignore Items in here will not be processed or run
.USAGE
1. Modify the scripts in the Assets folder according to your environment
2. This script is currently designed to be run from a server with the relevant module's management tools and powershell plugins installed locally
Syntax
Get-SAMReport [[-module] <string>] [[-output] <Email/OnScreen>]
Example
Get-SAMReport Veeam Email
Scheduled Task Action settings
Start a program: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Arguments: -NonInteractive c:\Scripts\SAMReports\Get-SAMReport.ps1 Exchange
Start in: c:\Scripts\SAMReports\
3. Ensure script is run manually at least once for each module. You will be prompted for your "Run As" credentials for that module
- These will be saved for future (scheduled) use in a hashed xml file.
4. Any script in the appropriate format in the module's subfolder will be included in the report
- Scripts can be renumbered to define order. Rename the extension or simply move it to the zzz_ignore folder to exclude from report
.NOTES
Script Name: Get-SAMReport.ps1
Created By: The Agreeable Cow
Contact: [email protected]
Date: August 2012
.VERSION HISTORY
1.0 Aug 2012 The Agreeable Cow Original Build
2.0 Jul 2014 The Agreeable Cow Module expansions, customisation features and minor updates
.CREDIT
Report Stylesheet Alan Renouf http://virtu-al.net (vCheck)
Storage Functions ThomasMc http://vpowercli.wordpress.com/2012/01/23/vpowercli-v6-army-report/
License Check Arne Fokkema http://xtravirt.com/powershell-veeam-br-%E2%80%93-get-total-days-before-license%C2%A0expires
Charting Sean Duffy http://www.simple-talk.com/sysadmin/powershell/building-a-daily-systems-report-email-with-powershell/
#>
#----------------------------------------------------------------------------------------------------------------
# Variables
$ScriptPath = (Split-Path ((Get-Variable MyInvocation).Value).MyCommand.Path) + "\"
$ModuleArray = @(Get-ChildItem -exclude "_*" | where { $_.PSIsContainer} | ForEach-Object { $_.Name })
$OutputArray=@("OnScreen","Email")
$TranscriptLog = $True
$Date = get-date -f yyyy-MM-dd
#----------------------------------------------------------------------------------------------------------------
# Run SAM Report
Function get-SAMReport{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName = $true,Position=0)]
[String] $Module="",
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName = $true,Position=1)]
[String] $Output="email"
)
Begin{
#Logging
if ($TranscriptLog -eq $True){
$Logfile = $ScriptPath + "SAMReport.log"
Start-Transcript -path $Logfile -append
}
# Assets Folder
$AssetsFolder = $ScriptPath + "_Assets\"
$Assets = Get-ChildItem -Path $AssetsFolder -filter "*.ps1" | Sort Name
Foreach ($Asset in $Assets) {
. $Asset.Fullname
}
# Module Folder
$ModulesFolder = $ScriptPath + $module + "\"
$Modules = Get-ChildItem -Path $ModulesFolder -filter "*.ps1" | Sort Name
# Output Folder
$OutputFolder = $ModulesFolder + "Output\"
IF (!(TEST-PATH $OutputFolder)) {NEW-ITEM $OutputFolder -type Directory}
# Ignore Folder
$IgnoreFolder = $ModulesFolder + "zzz_Ignore\"
IF (!(TEST-PATH $IgnoreFolder)) {NEW-ITEM $IgnoreFolder -type Directory}
# Credentials
$Credentials = Get-MyCredential (join-path ($ModulesFolder) ModuleCredentials.xml)
}
Process{
$OutReport = Get-ReportHeaderHTML "$Module Modular Report"
$AllAttachments = @()
# Call each plugin
Foreach ($script in $Modules) {
. $script.Fullname
# Footer
$OutFooter = "<div style='font-family:Arial, Helvetica, sans-serif; font-size:9px; color:#bbbbbb'>" + $script.name + " " + $Author + " " + $PluginDate + " (" + $Version + ")</br></br>"
# Text and Data
if ($OutText -AND $OutData) {
$OutReport += Get-CustomHeader $Title $Comment $OutAlert
$OutReport += Get-HTMLText $OutText
$OutReport += "</br>"
$OutReport += Get-HTMLTable $OutData
$OutReport += Get-CustomHeaderClose $OutFooter
}
elseif ($OutText) {
$OutReport += Get-CustomHeader $Title $Comment $OutAlert
$OutReport += Get-HTMLText $OutText
$OutReport += Get-CustomHeaderClose $OutFooter
}
elseif ($OutData) {
$OutReport += Get-CustomHeader $Title $Comment $OutAlert
$OutReport += Get-HTMLTable $OutData
$OutReport += Get-CustomHeaderClose $OutFooter
}
$OutReport += Get-CustomHeader0Close
$OutReport += Get-CustomHTMLClose
# Attachments
if (!$attachment -eq ""){
$AllAttachments += $Attachment
}
# Count OutAlerts
if ($OutAlert -eq "Good"){
$GoodStatus += $GoodStatus.count + 1
}
elseif ($OutAlert -eq "Warning"){
$WarningStatus += $WarningStatus.count + 1
}
elseif ($OutAlert -eq "Alert"){
$AlertStatus += $AlertStatus.count + 1
}
# Reset Fields
$ResultsText = ""
$ResultsData = ""
$ResultsAlert = ""
$OutText = ""
$OutData = ""
$OutAlert = ""
}
}
End{
# Apply Global Banner Status Colours
if ($AlertStatus -ge 1){
$OutReport = $OutReport -replace ("#BANNER_MARKER#",$AlertTitleColour)
}
elseif ($WarningStatus -ge 1){
$OutReport = $OutReport -replace ("#BANNER_MARKER#",$WarningTitleColour)
}
else{
$OutReport = $OutReport -replace ("#BANNER_MARKER#",$GoodTitleColour)
}
# Update Colour Formatting for Text
$OutReport = $OutReport -replace ("!GREEN!","<span style='color:green'>")
$OutReport = $OutReport -replace ("!ORANGE!","<span style='color:orange'>")
$OutReport = $OutReport -replace ("!RED!","<span style='color:red'>")
if ($module -eq "Veeam") {
$OutReport = $OutReport -replace ("01/01/1900 00:00:00","")
$OutReport = $OutReport -replace ("Success:","<span style='color:green'>Success")
$OutReport = $OutReport -replace ("InProgress:","<span style='color:orange'>InProgress")
$OutReport = $OutReport -replace ("Pending:","<span style='color:orange'>Pending")
$OutReport = $OutReport -replace ("Warning:","<span style='color:orange'>Warning")
$OutReport = $OutReport -replace ("Error:","<span style='color:red'>Error:")
$OutReport = $OutReport -replace ("Unknown:","<span style='color:red'>Unknown:")
$OutReport = $OutReport -replace ("Failed","<span style='color:red'>Failed")
$OutReport = $OutReport -replace ("Stopped","<span style='color:red'>Stopped")
}
# Output Report
if ($output -eq "OnScreen"){
$Filename = $Env:TEMP + "\" + $Module + "_" + $Date + ".htm"
$OutReport | out-file -encoding ASCII -filepath $Filename
Invoke-Item $Filename
}
elseif ($output -eq "Email"){
if ($AllAttachments) {
Send-MailMessage -To $EmailTo -From $EmailFrom -Subject $EmailSubject -SmtpServer $SmtpServer -body $OutReport -BodyAsHtml -attachment $AllAttachments
}
else {
Send-MailMessage -To $EmailTo -From $EmailFrom -Subject $EmailSubject -SmtpServer $SmtpServer -body $OutReport -BodyAsHtml
}
}
# Close Log
if ($TranscriptLog -eq $True){
Stop-Transcript
$Format = Get-Content $Logfile
$Format > $Logfile
}
}
}
# Module Validation
$HelpText = @"
Missing or invalid arguments. Correct syntax is Get-SAMReport.ps1 <module> <output>
Valid modules are: $ModuleArray
Valid outputs are: $OutputArray
For example Get-SAMReport.ps1 Exchange OnScreen
"@
if ($args[0] -ne $NULL){
$ModuleCheck = $ModuleArray -contains $args[0]
if ($ModuleCheck -eq $false){
write-host $HelpText
exit
}
else{
$Module = $args[0]
}
}
else{
write-host $HelpText
exit
}
# Output Validation
if ($args[1] -ne $NULL){
$OutputCheck = $OutputArray -contains $args[1]
if ($OutputCheck -eq $false){
write-host $HelpText
exit
}
else{
$Output = $args[1]
}
}
else{
$Output = "Email"
}
get-SAMReport $Module $Output
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2012-11-22T09:58:09.334214</Date>
<Author>mydomain\administrator</Author>
</RegistrationInfo>
<Triggers>
<CalendarTrigger>
<StartBoundary>2014-08-01T06:00:00</StartBoundary>
<Enabled>true</Enabled>
<ScheduleByMonth>
<DaysOfMonth>
<Day>1</Day>
</DaysOfMonth>
<Months>
<January />
<February />
<March />
<April />
<May />
<June />
<July />
<August />
<September />
<October />
<November />
<December />
</Months>
</ScheduleByMonth>
</CalendarTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<UserId>mydomain\Administrator</UserId>
<LogonType>Password</LogonType>
<RunLevel>LeastPrivilege</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT2H</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe</Command>
<Arguments>C:\Scripts\SAMReports\Get-SAMReport.ps1 MODULE_NAME</Arguments>
<WorkingDirectory>C:\Scripts\SAMReports\</WorkingDirectory>
</Exec>
</Actions>
</Task>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment