Veeam Report ReDux!

A couple of months ago I posted about some Powershell I had written to report on the state of our Veeam backups.

I've been somewhat industrious here and am now on V3 of my script.

Requirements :

  • You need to run this from a vSphere install
  • You need Powershell 3 or 4 on vSphere and any Veeam servers
  • You need Powershell remoting enabled on all involved servers
  • As this is an unsigned script you need the execute policy set to unrestricted
  • Edit the script for your environment...SMTP-IP, veeamServers and viServer.

Things to do:

  • Get this running the vSphere stuff remotely...no idea why this wont work!

Here it is.

#
# Veeam Report
#
# Daily Veeam Report
#
# Version 3
#
# Allen Kong 09/01/205
#

# Psuedo Code
# 1. Get All vms from vSphere
#  1a. Connect to vSphere
# 2. Get all sessions in the last 24 hours from Veeam
# 3. Get all sessions in the last 5 days from Veeam
# 4. Process the data
# 5. Email the data
#

# 1. Get All vms from vSphere
Function GetAllVMs(){
 
    #
    # This function connects to vSphere and returns all the VMs registered in vSphere
    # Change this to you vSphere server!
    #
    $viServer = "Server1"


    # Add PowerShell VMware Snapin
    Add-PSSnapin "Vmware.VIMAutomation.Core"

    # Connect to vSphere
    $server = Connect-VIServer $viServer -User "DOMAIN\Account" -Password "Password"
 
    # Grab the VMs sorting them by VM Name
    $serverList = Get-VM | Sort-Object Name

    # Disconnect from vSphere
    Disconnect-VIServer $server -confirm:$false

    # Return data
    $serverList
}
Function GetAllBackups(){
    #
    # This function loops through all the servers
    # and then the sessions (veeam sessions) looking for a name match between server and session
    # if found then the session is added to $output and the loop broken
    # Effectively this checks all veeam sessions against the vSphere machines and returns matches.
    #
    # Probably don't need to do this as all sessions will (should) have a server they are based on
    #

    param($serverList, $sessions)

    $output = @()
    foreach($server in $serverList){
        foreach($session in $sessions){
            if($server.Name -eq $session.Name){
                $output += $session
                break
            }
        }
    }
    $output
}
Function GetAllReplicas(){
    #
    # This function loops through all the servers
    # and then the sessions (veeam sessions) looking for a name match between server and session
    # if found then the session is added to $output and the loop broken
    # Effectively this checks all veeam sessions against the vSphere machines and returns matches.
    #
    # This is needed as replica sessions are named after the source machine where as the target VM is postfixed with _Replica
    #

    param($serverList, $sessions)

    $output = @()
    foreach($server in $serverList){
        foreach($session in $sessions){
            $name = ($session.Name) + "_Replica"
            if($name -eq $server.Name){
                $output += $session
                break
            }
        }
    }
    $output
}

# 2. Get all sessions in the last 24 hours from Veeam
Function GetAllSession24hrs(){
    #
    # This function connects to the given server and request all the sessions based on the $type
    #
    # A new data type is created to store the information from the Veeam Session
    # This is needed as some data is referenced in objects outside the Veeam Session : $task.Progress.StartTime
    #

    param($serverName, $type)

    # Start new powershell session on the veeam server
    $session = New-PSSession -ComputerName $serverName
 
    # Load Veeam SnapIns
    icm -Session $session -ScriptBlock {Add-PSSnapin -Name VeeamPSSnapIn}
 
    switch($type){
        # Grab all the Veeam session information for the last 24hrs
        "Backup" {icm -Session $session -ScriptBlock {$vbrSessions = Get-VBRBackupSession | ? {$_.JobType -eq "Backup" -and $_.EndTime -ge (Get-Date).addhours(-24)} | Sort-Object JobName};break}
 
        # Grab all the Veeam session information for the last 24hrs
        "Replica" {icm -Session $session -ScriptBlock {$vbrSessions = Get-VBRBackupSession | ? {$_.JobType -eq "Replica" -and $_.EndTime -ge (Get-Date).addhours(-24)} | Sort-Object JobName};break}
    }
 
    # Somewhere to store the data
    $veeamArray = @();

    $veeamArray =
 
    icm -Session $session -ScriptBlock {
        param($type)
        # Run through each session
        foreach($vbrSession in $vbrSessions){

            # Run into the VMs
            foreach($task in $vbrSession.gettasksessions()){
             
                # Make vm object
                $vm = 1 | Select Name, Status, Date, JobName, SessionName, Retry, Type

                $vm.Name = $task.Name
                $vm.Status = $task.Status
                $vm.Date = $task.Progress.StartTime
                $vm.JobName = $task.JobName
                $vm.SessionName = $vbrSession.Name
                $vm.Retry = 0
                $vm.Type = $type

                if($vm.SessionName -match "Retry"){
                    $string = $vm.SessionName
                    $vm.Retry = $string.SubString($string.Length - 2, 1)
                }

                # Add vm to the array
                $vm
            }
        }
    } -ArgumentList $type
 
    # Close the session
    Remove-PSSession $session

    # Return the data
    $veeamArray
}

# 3. Get all sessions in the last 5 days (120 hours) from Veeam
Function GetAllSessions5Days(){
    #
    # NOT USED YET
    #

    param($serverName, $type)

    # Start new powershell session on the veeam server
    $session = New-PSSession -ComputerName $serverName
 
    # Load Veeam SnapIns
    icm -Session $session -ScriptBlock {Add-PSSnapin -Name VeeamPSSnapIn}
 
    switch($type){
        # Grab all the Veeam session information for the last 24hrs
        "Backup" {icm -Session $session -ScriptBlock {$vbrSessions = Get-VBRBackupSession | ? {$_.JobType -eq "Backup" -and $_.EndTime -ge (Get-Date).addhours(-120)} | Sort-Object Name};break}
 
        # Grab all the Veeam session information for the last 24hrs
        "Replica" {icm -Session $session -ScriptBlock {$vbrSessions = Get-VBRBackupSession | ? {$_.JobType -eq "Replica" -and $_.EndTime -ge (Get-Date).addhours(-120)} | Sort-Object Name};break}
    }
 
    # Somewhere to store the data
    $veeamArray = @();

    $veeamArray =
 
    icm -Session $session -ScriptBlock {
        param($type)
        # Run through each session
        foreach($vbrSession in $vbrSessions){

            # Run into the VMs
            foreach($task in $vbrSession.gettasksessions()){
             
                # Make vm object
                $vm = 1 | Select Name, Status, Date, JobName, SessionName, Retry, Type

                $vm.Name = $task.Name
                $vm.Status = $task.Status
                $vm.Date = $task.Progress.StartTime
                $vm.JobName = $task.JobName
                $vm.SessionName = $vbrSession.Name
                $vm.Retry = 0
                $vm.Type = $type

                if($vm.SessionName -match "Retry"){
                    $string = $vm.SessionName
                    $vm.Retry = $string.SubString($string.Length - 2, 1)
                }

                # Add vm to the array
                $vm
            }
        }
    } -ArgumentList $type
 
    # Close the session
    Remove-PSSession $session

    # Return the data
    $veeamArray
}

# 4. Process the data
Function MatchServerToSession(){
    #
    # This function finds all servers which have a corresponding session
    # Filter is inplace for replicas
    #
    param($serverlist, $veeamdata)
 
    $output = @()

    foreach($server in $serverlist){
        foreach($session in $veeamdata){
            if(($server.name -eq $session.name) -or (($server.name + "_Replica") -eq $session.name)){
                $output += $server
                break
            }
        }
    }

    $output
}
Function NotMatchServerToSession(){
    #
    # This function finds all servers which DO NOT have a corresponding session
    # Filter is inplace for replicas
    #

    param($serverlist, $veeamdata)
 
    $output = @()
    $flag = 0
    foreach($server in $serverlist){
        foreach($session in $veeamdata){
            if(($server.name -eq $session.name) -or (($session.name + "_replica") -eq $server.name)){
                $flag = 999
                break
            }
        }
        if($flag -eq 0){
            $output += $server
        }
        else{
            $flag = 0
        }
    }
    $output
}
Function GetFailures(){
    #
    # This function finds all veeam sessions which have failed
    # This is used in sending failure emails to ServiceDesk to raise calls
    #
    param($sessions)

    $output = @()

    foreach($session in $sessions){
        $state = $session.Status.Value
        if($state -eq "Failed"){
            $output += $session
        }
    }
    $output
}

# 5. Send Email
Function sendEmail{
param($from,$to,$subject,$smtphost,$htmlFileName)
$body = Get-Content $htmlFileName
$smtp= New-Object System.Net.Mail.SmtpClient $smtphost
$msg = New-Object System.Net.Mail.MailMessage $from, $to, $subject, $body
$msg.isBodyhtml = $true
$smtp.send($msg)
}
Function sendTxtEmail{
param($from,$to,$subject,$smtphost,$body)

$smtp= New-Object System.Net.Mail.SmtpClient $smtphost
$msg = New-Object System.Net.Mail.MailMessage $from, $to, $subject, $body
$msg.isBodyhtml = $true
$smtp.send($msg)
}
Function sendFailures(){
    param($data)
    foreach($session in $data){
        $sessionName = $session.Name
        sendTxtEmail veeam@contoso.com ithelpdesk@contoso.com "Veeam Failure" <SMTP_IP> "Veeam Job Failed $sessionName Please assign accordingly"
    }
}
# 5a. Write data
Function writeHeader(){
    param($filename, $serverTotal, $sessionTotal, $successTotal, $failedTotal, $warningTotal, $omittedTotal)

    $date = ( Get-Date ).ToString('yyyy/MM/dd')

Add-Content $fileName "<html>"
Add-Content $fileName "<head>"
Add-Content $fileName "<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>"
Add-Content $fileName '<title>Veeam Backup Report</title>'
Add-Content $fileName '<STYLE TYPE="text/css">'
Add-Content $fileName  "<!--"
Add-Content $fileName  "td {"
Add-Content $fileName  "font-family: Tahoma;"
Add-Content $fileName  "font-size: 11px;"
Add-Content $fileName  "border-top: 1px solid #999999;"
Add-Content $fileName  "border-right: 1px solid #999999;"
Add-Content $fileName  "border-bottom: 1px solid #999999;"
Add-Content $fileName  "border-left: 1px solid #999999;"
Add-Content $fileName  "padding-top: 0px;"
Add-Content $fileName  "padding-right: 0px;"
Add-Content $fileName  "padding-bottom: 0px;"
Add-Content $fileName  "padding-left: 0px;"
Add-Content $fileName  "}"
    Add-Content $fileName  ".input-color {"
    Add-Content $fileName  "position: relative;"
    Add-Content $fileName  "}"
    Add-Content $fileName  ".input-color input {"
    Add-Content $fileName  "padding-left: 20px;"
    Add-Content $fileName  "}"
    Add-Content $fileName  ".input-color .color-box {"
    Add-Content $fileName  "width: 10px;"
    Add-Content $fileName  "height: 10px;"
    Add-Content $fileName  "display: inline-block;"
    Add-Content $fileName  "background-color: #ccc;"
    Add-Content $fileName  "position: absolute;"
    Add-Content $fileName  "left: 5px;"
    Add-Content $fileName  "top: 5px;"
    Add-Content $fileName  "}"
Add-Content $fileName  "body {"
Add-Content $fileName  "margin-left: 5px;"
Add-Content $fileName  "margin-top: 5px;"
Add-Content $fileName  "margin-right: 0px;"
Add-Content $fileName  "margin-bottom: 10px;"
Add-Content $fileName  ""
Add-Content $fileName  "table {"
Add-Content $fileName  "border: thin solid #000000;"
Add-Content $fileName  "}"
Add-Content $fileName  "-->"
Add-Content $fileName  "</style>"
Add-Content $fileName "</head>"
Add-Content $fileName "<body>"
Add-Content $fileName  "<table width='60%'>"
Add-Content $fileName  "<tr bgcolor='#CCCCCC'>"
Add-Content $fileName  "<td colspan='6' height='25' align='center'>"
Add-Content $fileName  "<font face='tahoma' color='#003399' size='3'><strong>vSphere 24hr Backup Report V3 - $date</strong></font>"
Add-Content $fileName  "</td>"
    Add-Content $fileName  "<tr align='center'><td colspan='6'> </td></tr>"
    Add-Content $fileName  "<tr align='center'>"
    Add-Content $fileName  "<td>Total Servers: $serverTotal</td>"
    Add-Content $fileName  "<td>Total Sessions: $sessionTotal</td>"
    Add-Content $fileName  "<td>Total Success: $successTotal</td>"
    Add-Content $fileName  "<td>Total Failed: $failedTotal</td>"
    Add-Content $fileName  "<td>Total Warning: $warningTotal</td>"
    Add-Content $fileName  "<td>Total Omitted: $omittedTotal</td>"
    Add-Content $fileName  "</tr>"
    Add-Content $fileName  "<tr align='center'><td colspan='6'> </td></tr>"
    Add-Content $fileName  "<tr align='center'>"
    Add-Content $fileName  "<td bgcolor='#8CDD81'>Veeam Backup Successful</td>"
    Add-Content $fileName  "<td bgcolor='#FBEC5D'>Veeam Backup Warning</td>"
    Add-Content $fileName  "<td bgcolor='#EE0000'>Veeam Backup Failed</td>"
    Add-Content $fileName  "<td bgcolor='#79CDCD'>Veeam Backup Not in Place Planned</td>"
    Add-Content $fileName  "<td bgcolor='#FF9955'>Veeam Backup Not in Place Unplanned</td>"
    Add-Content $fileName  "<td bgcolor='#DDDDDD'>No Information</td>"
    Add-Content $fileName  "</tr>"
Add-Content $fileName  "</tr>"
Add-Content $fileName  "</table>"

}
Function writeFooter(){
    param($filename)

    Add-Content $fileName "</body>"
    Add-Content $fileName "</HTML>"
}
Function writeBackupDataTableHeader{
param($fileName)
Add-Content $fileName "<table width='60%'><tbody>"
    Add-Content $fileName "<tr bgcolor=#CCCCCC>"
    Add-Content $fileName "<td colspan='3' align='center'><font size='2'face='tahoma' color='#003399'><strong>Backup Sessions</strong></font></td>"
    Add-Content $fileName "</tr>"
Add-Content $fileName "<tr bgcolor=#CCCCCC>"
Add-Content $fileName "<td width='30%' align='center'><font size='2'face='tahoma' color='#003399'><strong>Server Name</strong></font></td>"
Add-Content $fileName "<td width='30%' align='center'><font size='2'face='tahoma' color='#003399'><strong>Status</strong></font></td>"
    Add-Content $fileName "<td width='40%' align='center'><font size='2'face='tahoma' color='#003399'><strong>Job Name</strong></font></td>"
    Add-Content $fileName "</tr>"
}
Function writeBackupDataTable{
param($fileName, $vms)

    foreach($vm in $vms){
     
        $colour = "DDDDDD"

   $ServerName = $vm.Name
        $ServerStatus = $vm.Status.Value
        $sessionName = $vm.SessionName
        $Retry = $vm.Retry

        # Make Failures Red etc...
        switch($ServerStatus){
            "Failed"{$colour = "ff0000";break}
            "Success"{$colour = "8CDD81";break}
            "Warning"{$colour = "FBEC5D";break}
        }

   Add-Content $fileName "<tr bgcolor=$colour>"
   Add-Content $fileName "<td align='center'>$ServerName</td>"
   Add-Content $fileName "<td align='center'>$ServerStatus</td>"
        Add-Content $fileName "<td align='center'>$SessionName</td>"
   Add-Content $fileName "</tr>"
    }
}
Function writeBackupDataTableFooter{
    param($filename)

    Add-Content $fileName "</tbody></table>"
}
Function writeReplicaDataTableHeader{
param($fileName)
Add-Content $fileName "<table width='60%'><tbody>"
    Add-Content $fileName "<tr bgcolor=#CCCCCC>"
    Add-Content $fileName "<td colspan='3' align='center'><font size='2'face='tahoma' color='#003399'><strong>Replica Sessions</strong></font></td>"
    Add-Content $fileName "</tr>"
Add-Content $fileName "<tr bgcolor=#CCCCCC>"
Add-Content $fileName "<td width='30%' align='center'><font size='2'face='tahoma' color='#003399'><strong>Server Name</strong></font></td>"
Add-Content $fileName "<td width='30%' align='center'><font size='2'face='tahoma' color='#003399'><strong>Status</strong></font></td>"
    Add-Content $fileName "<td width='40%' align='center'><font size='2'face='tahoma' color='#003399'><strong>Job Name</strong></font></td>"
    Add-Content $fileName "</tr>"
}
Function writeReplicaDataTable(){
    param($filename, $vms)
    foreach($vm in $vms){

        $colour = "DDDDDD"

   $ServerName = $vm.Name
        $ServerStatus = $vm.Status.Value
        $sessionName = $vm.SessionName
        $Retry = $vm.Retry

        # Make Failures Red etc...
        switch($ServerStatus){
            "Failed"{$colour = "ff0000";break}
            "Success"{$colour = "8CDD81";break}
            "Warning"{$colour = "FBEC5D";break}
        }

   Add-Content $fileName "<tr bgcolor=$colour>"
   Add-Content $fileName "<td align='center'>$ServerName</td>"
   Add-Content $fileName "<td align='center'>$ServerStatus</td>"
        Add-Content $fileName "<td align='center'>$SessionName</td>"
        #Add-Content $fileName "<td align='center'>$Retry</td>"
   Add-Content $fileName "</tr>"
    }  
}
Function writeReplicaDataTableFooter{
    param($filename)

    Add-Content $fileName "</tbody></table>"
}
Function writeDataTableFooter{
    param($filename)

    Add-Content $fileName "</tbody></table>"
}
Function writeMissingDataTableHeader{
param($fileName)
Add-Content $fileName "<table width='60%'><tbody>"
    Add-Content $fileName "<tr bgcolor=#CCCCCC>"
    Add-Content $fileName "<td colspan='2' align='center'><font size='2'face='tahoma' color='#003399'><strong>Remaining Servers</strong></font></td>"
    Add-Content $fileName "</tr>"
Add-Content $fileName "<tr bgcolor=#CCCCCC>"
Add-Content $fileName "<td width='50%' align='center'><font size='2'face='tahoma' color='#003399'><strong>Server Name</strong></font></td>"
Add-Content $fileName "<td width='50%' align='center'><font size='2'face='tahoma' color='#003399'><strong>Server Notes</strong></font></td>"
    Add-Content $fileName "</tr>"
}
Function writeMissingDataTable(){
    param($filename, $vms)

    foreach($vm in $vms){

        $ServerName = $vm.Name
        $ServerNotes = $vm.Notes
        $colour = "FF9955"

        switch -wildcard ($ServerNotes){
            ""{$colour = "#DDDDDD";break}
            "*Not Backed Up*"{$colour = "79CDCD";break}
        }

   Add-Content $fileName "<tr bgcolor=$colour>"
   Add-Content $fileName "<td align='center'>$ServerName</td>"
   Add-Content $fileName "<td align='center'>$ServerNotes</td>"
   Add-Content $fileName "</tr>"
    }
}
Function writeMissingDataTableFooter{
    param($filename)

    Add-Content $fileName "</tbody></table>"
}
Function writeHTML(){
    param($filename, $backups, $replicas, $missing, $servers)

    $totalSessions = $backups.Count + $replicas.Count
    $totalServers = $servers.Count
    $totalMissing = $missing.Count
    $totalSuccess = 0
    $totalWarning = 0
    $totalFailed = 0
 
    foreach($vm in $backups){
        $status = $vm.Status.Value
        switch($status){
            "Failed"{$totalFailed++;break}
            "Success"{$totalSuccess++;break}
            "Warning"{$totalWarning++;break}
        }
    }
    foreach($vm in $replicas){
        $status = $vm.Status.Value
        switch($status){
            "Failed"{$totalFailed++;break}
            "Success"{$totalSuccess++;break}
            "Warning"{$totalWarning++;break}
        }
    }

    writeHeader $filename $totalServers $totalSessions $totalSuccess $totalFailed $totalWarning $totalMissing
    writeBackupDataTableHeader $filename
    writeBackupDataTable $filename $backups
    writeBackupDataTableFooter $filename
    writeReplicaDataTableHeader $filename
    writeReplicaDataTable $filename $replicas
    writeReplicaDataTableFooter $filename
    writeMissingDataTableHeader $filename
    writeMissingDataTable $filename $missing
    writeMissingDataTableFooter $filename
    writeDataTableFooter $filename
    writeFooter $filename
}

# Variables for storing data
$date = ( Get-Date ).ToString('yyyyMMdd')
$filename = "C:\Scripts\veeam_daily_report_v3_$date.html"

# Array declaration
$allvirtualmachines = @()
$sessions5days = @()
$sessions24hr = @()
$sessionsMatched = @()
$sessionsNotMatched = @()
$backupfailures = @()
$replicafailures = @()
$sessionsBackups = @()
$sessionsReplicas = @()

# Get vSphere Servers
$allvirtualmachines = GetAllVMs

########################################################
#
# Get Veeam Sessions - add your Veeam servers here!
#
########################################################

$veeamServers = @("server1","server2","server3")

########################################################
#
########################################################

foreach($veeam in $veeamServers){

    # Run through each server and get the data
    $sessionsBackups += (GetAllSession24hrs $veeam "Backup") | Sort Date -Descending
    $sessionsReplicas += (GetAllSession24hrs $veeam "Replica") | Sort Date -Descending
}

$sessions24hr = $sessionsBackups + $sessionsReplicas

# Remove extra jobs
$sessionsBackup24hrProcessed = (GetAllBackups $allvirtualmachines $sessionsBackups) | sort Status -Descending
$sessionsReplica24hrProcessed = (GetAllBackups $allvirtualmachines $sessionsReplicas) | sort Status -Descending

# Find all servers with/without a Veeam Job
$serversMatched = MatchServerToSession $allvirtualmachines $sessions24hr
$serversNotMatched = NotMatchServerToSession $allvirtualmachines $sessions24hr

# Remove any old html files
if(Test-Path($filename)){
    Remove-Item $filename
}

# Get all failures
$backupfailures = GetFailures $sessionsBackup24hrProcessed
$replicafailures = GetFailures $sessionsReplica24hrProcessed

writeHTML $filename $sessionsBackup24hrProcessed $sessionsReplica24hrProcessed $serversNotMatched $allvirtualmachines

# Email IT Helpdesk with Failures
sendFailures $backupfailures
sendFailures $replicafailures

# Email Report!
sendEmail veeam@contoso.com allen.kong@contoso.com "Veeam Report" <SMTPIP> $filename

Comments

Popular posts from this blog

PXE booting, MDT and 802.1x

Intune installation requires a wire...or does it?

Powershell VPN connections - PEAP with MSCHAPv2