26 thoughts on “PowerShell/Veeam

  1. Ernie Bergan

    Your efforts in version 9.01 have been an incredible help to me in pulling specific information about backup and restore jobs. My first foray into a PowerShell script to derive a .CSV file focused on VMware backups and restores. I now need to expand the script to include Endpoint Backup information. Do you happen to know if there are logs begin written during Endpoint Backup operations that are exposed to the PowerShell environment, similar to the logs that are accessible in restore operations? I’m trying to get the backup size of each Endpoint Backup, and ideally whether the backup was full or incremental. I don’t see that information exposed as elements in Get-VBREPJob or VBREPSession.

    For VMware restore operations, I ended up writing a small function to look through the restore job log to pull information. IF similar information is logged for Endpoint jobs, I should be able to parse it.

    Admittedly, since Endpoint Backup is still free from Veeam, I understand why they have far fewer PowerPoint hooks in place for EP.

    Thank for sharing your great work.

    Reply
    1. smasterson Post author

      My guess is that these logs exist on the client and not the server. I’ve not seen anything beyond what you have mentioned. It will be interesting to see how they build upon the relationship between VB&R and EP.

      Reply
  2. Ernie Bergan

    Discovered that – for each EPSession, the repository is also creating information on a Restore Point. I was hoping to find an ID that would insure the Restore Point record I’m retrieving matches the Endpoint Protection session, but so far I have not seen how to make that connection. So I look for a restore point that is created during the EP Session.

    With apologies for my learning code, no doubt highly inefficient – here’s my current script. Anything good I learned from you. Lots of commented lines as I try different fields and approaches.

    param(
    [string]$vbrServer=”127.0.0.1″,
    [int]$vbrPort=9392,
    [string]$vbrUser,
    [string]$vbrPassword,
    [int]$LookBackHrs=48,
    [string]$outFile=””,
    [switch]$ConnectTest=$false
    )

    #$LookBackHrs = 2200 # Controls how many hours back to retrieve backup and restore jobs
    #$outFile = “E:\tmp\Veeam_APTARE_GenericBU_$(Get-Date -format MMddyyyy_hhmmss).csv” # Output file name
    #$outFile = “E:\tmp\Veeam_APTARE_GenericBU_v5.csv” # Output file name
    $Vendor = “‘Veeam'” #VendorName placed in CSV
    #$vbrServer = “10.2.3.159” #Veeam Server to be connected

    # Set variables to null

    $nullstrg=”‘null'”
    $outarray=@()
    $backuparray=@()
    $backupEParray=@()
    $restorearray=@()

    add-pssnapin “VeeamPSSnapIn” -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
    Disconnect-VBRServer
    #write-host (‘ConnectTest = ‘ + $ConnectTest)

    if ($ConnectTest) {
    if (Test-NetConnection -port $vbrPort -Computername $vbrServer -InformationLevel Quiet )
    {write-host ‘vbrServer Pings’}
    #Exit
    else
    {write-host (‘ERROR: Unable to connect to ‘+$vbrServer+’. Please verify connectivity to ‘+$vbrServer+’ from collector server using port ‘+ $vbrPort)
    Throw (‘Test NetConnection to ‘+$vbrServer+’ on port 9392 fails.’)
    Exit
    }
    }

    if ($ConnectTest) {
    Try {
    Connect-VBRServer -User $vbrUser -Password $vbrPassword -server $vbrServer -Port $vbrPort -ErrorAction Stop -WarningAction Stop -Timeout 1
    }
    Catch {
    Write-Host (‘ERROR: Authentication failed. Verify credentials.’)
    Throw ‘Error connecting to Veeam Server. Check credentials’
    Exit
    }
    Write-Host ‘SUCCESS: Successfully connected to Veeam Server’
    Exit
    }

    Try {
    Connect-VBRServer -User $vbrUser -Password $vbrPassword -server $vbrServer -Port $vbrPort -ErrorAction Stop -WarningAction Stop -Timeout 5
    } Catch {
    Throw ‘Error connecting to Veeam Server’
    Exit
    }

    function Convert-HEXtoDECRes
    {

    param(
    [Parameter(ValueFromPipeline=$true, Position=0)]
    [string]$HEXr)
    $subHEXr=””
    $hexrint=””
    $subHEXr = $HEXr.Substring(0, $HEXr.IndexOf(“-“) )
    ForEach ($value in $subHEXr)
    {
    $hexrint = [Convert]::ToInt64($value,16) + $hexrint
    }
    RETURN $hexrint
    }

    Function Get-RestoreSize {

    [CmdletBinding()]
    param(
    [Parameter(ValueFromPipeline=$true, Position=0)]
    [string]$Inputstring
    )
    [hashtable]$RestoreInfo = @{}
    If($inputstring) {
    If ($inputstring.Contains(“=”)) {
    $NbrOfFiles = $inputstring.Substring($inputstring.IndexOf(“=”)+1, $inputstring.IndexOf(“files”)-$inputstring.IndexOf(“=”) -2 )
    } else
    { $NbrOfFiles = 0}
    If ($inputstring.Contains(“(“)) {
    $RestoreString = $Inputstring.Substring($Inputstring.IndexOf(“(“)+1,($Inputstring.IndexOf(“)”)-($Inputstring.IndexOf(“(“)))-1)
    [integer]$RestoreSizeVal = $RestoreString.Substring(0,$RestoreString.IndexOf(” “))
    $RestoreSizeUnits = $RestoreString.Substring($RestoreString.IndexOf(” “)+1)
    $RestoreSizeKB = switch ($RestoreSizeUnits) {
    “MB” {[int]$RestoreSizeVal * 1024}
    “GB” {[int]$RestoreSizeVal * 1024 * 1024}
    “TB” {[int]$RestoreSizeVal * 1024*1024*1024}
    “PB” {[int]$RestoreSizeVal * 1024*1024*1024*1024}
    default {[int]$RestoreSizeVal}
    }
    } else
    { $RestoreSizeKB = 0
    }
    }
    $RestoreInfo.NbrofFiles = $NbrOfFiles
    $RestoreInfo.RestoreSizeKB = $RestoreSizeKB
    Return $RestoreInfo
    }
    $Backups = Get-VBRBackup -Name “Backup Job ERNIEB-PC”
    #$ernie = $Backups.sm # Info
    #$ernie

    $Jobs = Get-VBRJob -WarningAction SilentlyContinue

    foreach ($job in $Jobs) {
    $BackupSessionsList = @(Get-VBRBackupSession | ? {($_.CreationTime -ge (Get-Date).AddHours(-$LookBackHrs)) -and $_.JobName -eq $job.Name}) #-and $_.Status -ne “Success”
    $RestoreSessionsList = @(Get-VBRRestoreSession | ? {($_.CreationTime -ge (Get-Date).AddHours(-$LookBackHrs)) }) #-and $_.JobName -eq $job.Name}) #-and $_.Status -ne “Success”
    $BackupEPSessionsList = @(Get-VBREPSession | ? {($_.CreationTime -ge (Get-Date).AddHours(-$LookBackHrs)) }) # -and $_.JobName -eq $job.Name})

    #$BackupEPSessionsList
    #
    foreach ($tasks in $BackupSessionsList) {
    $task = Get-VBRTaskSession $tasks
    #[string]$JobIDstring=””
    #$JobID=””
    #$JobIDstring = $task.Id
    #$JobIDstring = $task.Id
    #$JobID = Convert-HEXtoDEC $JobIDstring

    $backuparray = $task | Select-Object @{Name=”VendorName”;Expression={$Vendor}},

    @{Name=”ClientName”; Expression = {“‘”+$_.Name+”‘”}},
    @{Name=”ClientIPAddress”; Expression = {$nullstrg}},
    #@{Name=”VendorJobType”; Expression = {“‘”+$job.JobType+”‘”}},
    @{Name=”VendorJobType”; Expression = {“‘BACKUP'”}},
    #@{Name=”JobStatus”; Expression = {$job.JobStatus}},
    #@{Name=”Long Job Name”;Expression={$tasks.Name}},
    #@{Name=”Job Level”;Expression={$tasks.Name.Substring(($tasks.Name.IndexOf(“(“))+1,($tasks.Name.IndexOf(“)”)-($tasks.Name.IndexOf(“(“)))-1)}},
    @{Name=”StartDateString”; Expression = {“‘”+$_.Progress.StartTime.ToString(“yyyy-MM-dd HH:mm:ss”)+”‘”}},
    @{Name=”FinishDateString”; Expression = {“‘”+$_.Progress.StopTime.ToString(“yyyy-MM-dd HH:mm:ss”)+”‘”}},
    @{Name=”BackupKilobytes”; Expression = {[math]::Round(($_.Progress.ReadSize/1024),1)}},
    #@{Name=”TransferedSize in KB”; Expression = {[math]::Round(($_.Progress.TransferedSize/1024),1)}},
    @{Name=”NbrOfFiles”; Expression = {$_.Progress.ProcessedObjects}},
    @{Name=”MediaType”; Expression = {“‘D'”}},
    #Status,
    @{Name=”VendorStatus”; Expression = {switch ($_.Status) {
    “Success” {0}
    “Failed” {2}
    default {1}
    }
    }},
    @{Name=”VendorJobID”; Expression = {“‘”+$_.Id+”‘”}},
    #@{Name=”VendorJobID”; Expression = {(“‘”+(Convert-HEXtoDECRes $_.Id)+”‘”) }},
    #@{Name=”VendorJobID3″; Expression = { “‘”+$JobIDstring+”‘” }},
    @{Name=”VendorPolicyName”; Expression={“‘”+$_.JobName+”‘”}},
    @{Name=”JobLevel”; Expression={“‘”+$_.Info.WorkDetails.TaskAlgorithm+”‘”}},
    #@{Name=”JobLevelSession”;Expression={ $_.JobSess.SessionInfo.SessionAlgorithm}},
    @{Name=”TargetName”;Expression={“‘”+$_.JobSess.JobSourceType+”‘”}},
    @{Name=”Schedule”; Expression = {“‘”+$_.Schedule+”‘”}}#,

    # @{Name=”Details”; Expression = {
    # If ($tasks.GetDetails() -eq “”){“‘”+($tasks.GetDetails()).Replace(“`n”,” “).Replace(“”,” – “) + ($tasks | Get-VBRTaskSession | %{If ($tasks.GetDetails()){$tasks.Name + “: ” + $tasks.GetDetails()}})+”‘”}
    # Else {(“‘”+$tasks.GetDetails()).Replace(“`n”,” “).Replace(“”,” – “)+”‘”}}} #| Format-Table #| ConvertTo-csv | % {$_ -replace ‘”‘,””} #-auto
    $outarray = $outarray + $backuparray
    }

    foreach ($EPtasks in $BackupEPSessionsList) {
    [string]$backupepstring=””
    #$EPtasks.sm
    #$backupepstring = $Job.Logger.GetLog() #.UpdatedRecords #| Select-Object title
    #$backupepstring
    #$EPtask = Get-VBREPTaskSession $EPtasks
    #write-hosts (‘$EPTask =’ + $EPtask)
    #[string]$JobIDstring=””
    #$JobID=””
    #$JobIDstring = $task.Id
    #$JobIDstring = $task.Id
    #$JobID = Convert-HEXtoDEC $JobIDstring

    $EPJob=””
    $EPRestorePoint = Get-VBRRestorePoint | ? {($_.CreationTime -ge ($EPtasks.CreationTime) -and $_.CreationTime -le ($EPtasks.EndTime)) }
    $EPJob = Get-VBREPJob -Id $EPRestorePoint.FindSourceJob().Id
    #write-host (‘Object Count = ‘ + $EPJob.ObjectsCount)
    #$EPRestorePoint
    $backupEParray = $EPtasks | Select-Object @{Name=”VendorName”;Expression={$Vendor}},

    @{Name=”ClientName”; Expression = {“‘”+$EPRestorePoint.fqdn+”‘”}},
    @{Name=”ClientIPAddress”; Expression = {$nullstrg}},
    #@{Name=”VendorJobType”; Expression = {“‘”+$job.JobType+”‘”}},
    @{Name=”VendorJobType”; Expression = {“‘BACKUP'”}},
    #@{Name=”JobStatus”; Expression = {$job.JobStatus}},
    #@{Name=”Long Job Name”;Expression={$tasks.Name}},
    #@{Name=”Job Level”;Expression={$tasks.Name.Substring(($tasks.Name.IndexOf(“(“))+1,($tasks.Name.IndexOf(“)”)-($tasks.Name.IndexOf(“(“)))-1)}},
    @{Name=”StartDateString”; Expression = {“‘”+$_.CreationTime.ToString(“yyyy-MM-dd HH:mm:ss”)+”‘”}},
    @{Name=”FinishDateString”; Expression = {“‘”+$_.EndTime.ToString(“yyyy-MM-dd HH:mm:ss”)+”‘”}},
    @{Name=”BackupKilobytes”; Expression = {[math]::Round(($EPRestorePoint.GetStorage().Stats.DataSize/1024),1)}},
    #@{Name=”TransferedSize in KB”; Expression = {[math]::Round(($_.Progress.TransferedSize/1024),1)}},
    @{Name=”NbrOfFiles”; Expression = {$EPJob.ObjectsCount}},
    @{Name=”MediaType”; Expression = {“‘D'”}},
    #Status,
    @{Name=”VendorStatus”; Expression = {switch ($_.Result) {
    “Success” {0}
    “Failed” {2}
    default {1}
    }
    }},
    @{Name=”VendorJobID”; Expression = {“‘”+$_.Id+”‘”}},
    #@{Name=”VendorJobID”; Expression = {(“‘”+(Convert-HEXtoDECRes $_.Id)+”‘”) }},
    #@{Name=”VendorJobID3″; Expression = { “‘”+$JobIDstring+”‘” }},
    @{Name=”VendorPolicyName”; Expression={“‘”+$EPRestorePoint.Name+”‘”}},
    @{Name=”JobLevel”; Expression={“‘”+$EPRestorePoint.Type+”‘”}},
    #@{Name=”JobLevelSession”;Expression={ $_.JobSess.SessionInfo.SessionAlgorithm}},
    @{Name=”TargetName”;Expression={“‘”+$EPRestorePoint.FindBackup().DirPath+”‘”}},
    @{Name=”Schedule”; Expression = {“‘”+$EPRestorePoint.FindBackup().TypeToString+”‘”}}#,

    # @{Name=”Details”; Expression = {
    # If ($tasks.GetDetails() -eq “”){“‘”+($tasks.GetDetails()).Replace(“`n”,” “).Replace(“”,” – “) + ($tasks | Get-VBRTaskSession | %{If ($tasks.GetDetails()){$tasks.Name + “: ” + $tasks.GetDetails()}})+”‘”}
    # Else {(“‘”+$tasks.GetDetails()).Replace(“`n”,” “).Replace(“”,” – “)+”‘”}}} #| Format-Table #| ConvertTo-csv | % {$_ -replace ‘”‘,””} #-auto
    $outarray = $outarray + $backupEParray
    }

    foreach ($restores in $RestoreSessionsList) {
    [string]$restorestring=””
    #[string]$JobIDstring=””
    #$RestoreJobIDs=””
    #$JobIDInteger=””

    $RestoreInfoDetails=””
    $restorestring = $restores.Logger.GetLog().UpdatedRecords | where-object {$_.title.Contains(“files to restore”)} | Select-Object title
    #$JobIDstring = $restores.ID
    #$RestoreJobID = Convert-HEXtoDECRes $JobIDstring
    $RestoreInfoDetails = Get-RestoreSize $restorestring
    #$RestoreInfoDetails.RestoreSizeKB
    #$RestoreInfoDetails.NbrofFiles
    $restorearray = $restores | Sort CreationTime | Select @{Name=”VendorName”;Expression={$Vendor}},
    @{Name=”ClientName”; Expression = {“‘”+$restores.Info.VmDisplayName+”‘”}},
    @{Name=”ClientIPAddress”; Expression = {$nullstrg}},
    @{Name=”VendorJobType”; Expression = {“‘RESTORE'”}},
    @{Name=”VendorPolicyName”; Expression={“‘”+$restores.Info.JobName+”‘”}},
    @{Name=”StartDateString”; Expression = {“‘”+$_.CreationTime.ToString(“yyyy-MM-dd HH:mm:ss”)+”‘”}},
    @{Name=”FinishDateString”; Expression = {“‘”+$_.EndTime.ToString(“yyyy-MM-dd HH:mm:ss”)+”‘”}},
    @{Name=”BackupKilobytes”; Expression = {$RestoreInfoDetails.RestoreSizeKB}},
    @{Name=”NbrOfFiles”; Expression = {$RestoreInfoDetails.NbrofFiles}},
    @{Name=”MediaType”; Expression = {“‘D'”}},
    #@{Name=”Duration (Mins)”; Expression = {[Math]::Round((New-TimeSpan $_.CreationTime $_.EndTime).TotalMinutes,2)}},
    @{Name=”VendorStatus”; Expression = {switch ($_.Info.Result) {
    “Success” {0}
    “Failed” {2}
    default {1}
    } }},
    @{Name=”VendorJobID”; Expression = {“‘”+$_.Id+”‘”}},
    #@{Name=”VendorJobID3″; Expression = {(“‘”+$RestoreJobID+”‘”)}},
    #@{Name=”VendorJobID”; Expression = {(“‘”+(Convert-HEXtoDECRes $_.Id)+”‘”)}},
    @{Name=”JobLevel”; Expression = {“‘”+$_.JobTypeString+”‘”}},
    @{Name=”TargetName”; Expression = {“‘”+$restores.Info.VmDisplayName+”‘”}},
    @{Name=”Schedule”; Expression = {“‘”+$_.Info.Initiator.Name+”‘”}},
    @{Name=”Details”; Expression = {“‘”+$_.Info.Reason+”‘”}}
    #@{Name=”Result”; Expression = {$_.Info.Result}} #| Format-Table -auto
    $outarray = $outarray + $restorearray
    }
    }
    # $outarray | ConvertTo-csv -NoTypeInformation | select -Skip 1 |% {$_ -replace ‘”‘,”} #| Out-File $outFile -Encoding utf8
    # $outarray | ConvertTo-csv -NoTypeInformation | select -Skip 1 |% {$_ -replace ‘”‘,”} | {switch ($outFile) {“” {} default {Out-File $outFile -Encoding utf8}}}

    If ($outFile ) {
    $outarray | ConvertTo-csv -NoTypeInformation | select -Skip 1 |% {$_ -replace ‘”‘,”} | Out-File $outFile -Encoding utf8
    }
    else
    {
    $outFile
    $outarray | ConvertTo-csv -NoTypeInformation | select -Skip 1 |% {$_ -replace ‘”‘,”} #| Out-File $outFile -Encoding utf8
    }

    Reply
  3. John Quinn

    Is there a way to encrypt the password in a file and then call that file from the powershell script, instead of stuffing the password in the file in clear text?

    Reply
    1. smasterson Post author

      I’m guessing you are talking about the email credentials? Certainly can be done, I typically run the script under an account that has privileges to the SMTP server and therefore neither the username or pwd get hard coded.

      You should be able to find plenty of examples of what you are looking for on the web.

      Reply
      1. john quinn

        Actually I was referring to veeam server account and password (and veeam server IP) that is required in the script to access the backups and create the report.

        Reply
        1. smasterson Post author

          Hmmm, then you have me confused as the script does not hold the VBR u/p – it could, and could save this info to a file, but the assumption is that you would be running the script as a user with permissions, else some code would need to be added.

          Reply
  4. pada

    Hello,
    First of all, Thanks a lot for your excelent job !
    Would it be possible to add a new feature : could it be possible get an “alert email” eventif report is not generated, or if veeam backup services are not availiable, or any reason preventing report to be received ? I think to manage it not at “Veeam powershell pluging” level, but lower at “windows powershell level”

    Reply
  5. Zaki Khan

    Hi Shawn

    Thanks for the wonderful scripts provided. It certainly makes our job much easier and also keep the management happy.

    We have been successfully using the below script for quite a while, untill we upgraded our Veeam infrastructure to 9.5.4
    https://blog.smasterson.com/2017/12/22/veeam-v9-my-veeam-report-9-5-3/

    With 9.5.4, the script has stopped reporting agent backups. Most of our backups are agent-based and hence script is of much use to us anymore.

    Also, the Cloud repository shows Total and free space as 0, and free % as 100.

    I tried veeam forum but could not get what I wanted.
    https://forums.veeam.com/powershell-f26/my-veeam-report-v9-5-1-t47058.html

    Could you please help me with the same.

    Reply
  6. Darren

    As mentioned by Bram above

    Your script is amazing and I have been using it for years.Version 10 would be well appreciated when you get the time.

    Kind Regards

    Reply
  7. Samuel

    Hi all,

    The solution is for the version comparison that it does in an “if”. The data collection that the script does works fine.

    You just have to change the following in the script to make it work:

    Line 1385:
    – Before:
    If ($ VeeamVersion -lt 9.5) {

    – After:
    If ([version] $ VeeamVersion -lt 9.5) {

    I hope that helps,
    Regards.

    Reply
      1. Jay

        PS C:\Users\20652167> D:\Veeam_script\Monthly_Backup_reports-v3.ps1
        At D:\Veeam_script\Monthly_Backup_reports-v3.ps1:1382 char:15
        + If ([version] $ VeeamVersion -lt 9.5) {
        + ~
        Unexpected token ‘$’ in expression or statement.
        At D:\Veeam_script\Monthly_Backup_reports-v3.ps1:1382 char:15
        + If ([version] $ VeeamVersion -lt 9.5) {
        + ~
        Missing closing ‘)’ after expression in ‘If’ statement.
        At D:\Veeam_script\Monthly_Backup_reports-v3.ps1:1382 char:37
        + If ([version] $ VeeamVersion -lt 9.5) {
        + ~
        Unexpected token ‘)’ in expression or statement.
        + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
        + FullyQualifiedErrorId : UnexpectedToken

        Reply
        1. Jay

          Here is the script I updated

          #region Report
          # Get Veeam Version
          $VeeamVersion = Get-VeeamVersion

          # If ($VeeamVersion -lt 9.5) {
          If ([version] $ VeeamVersion -lt 9.5) {
          Write-Host “Script requires VBR v9.5” -ForegroundColor Red
          Write-Host “Version detected – $VeeamVersion” -ForegroundColor Red
          exit
          }

          Reply
          1. Jay

            Fixed the issue with the line, when I coped the code and pasted it an extra space was added which I removed.

            If ([version] $ VeeamVersion -lt 9.5) { <- remove the space after $ VeeamVersion

            If ([version] $VeeamVersion -lt 9.5) { <- updated

            Now it says…

            WARNING: This cmdlet is obsolete and no longer supported. To get computer backup job use "Get-VBRComputerBackupJob"
            instead.

          2. Jay

            After taking out the extra space the script ran successfully in Veeam 10, ignore the warning about the following, it is erroneous.

            WARNING: This cmdlet is obsolete and no longer supported. To get computer backup job use “Get-VBRComputerBackupJob”
            instead.

  8. Udo

    Works well with V10, only the license support date read out of the registry with the Get-VeeamSupportDate function apparently does not return any reasonable value (wmi connection failed).
    Looks like the format of the license reg key must have changed in V10.

    Has anyone managed to adapt the code here?

    Reply
  9. Debra Morcio

    upgraded to v11 – no output.
    I modified the ‘version’ in script updated to v11 no other changes.

    any ideas

    Reply
    1. Chris Evans

      Just remove this block (lines 1385-1389 if you’ve not otherwise edited the script):

      If ($VeeamVersion -lt 9.5) {
      Write-Host “Script requires VBR v9.5” -ForegroundColor Red
      Write-Host “Version detected – $VeeamVersion” -ForegroundColor Red
      exit
      }

      Unrelated to your question, but I’d also remove lines 564-570 as well since v11 no longer uses PSSnapins:

      # Load Veeam Snapin
      If (!(Get-PSSnapin -Name VeeamPSSnapIn -ErrorAction SilentlyContinue)) {
      If (!(Add-PSSnapin -PassThru VeeamPSSnapIn)) {
      Write-Error “Unable to load Veeam snapin” -ForegroundColor Red
      Exit
      }
      }

      Reply
  10. zaki

    Hi

    The script does not show the jobs, session information related to agent backups managed by Veeam backup server.
    I guess script needs to include the command like “Get-VBRComputerBackupJob” and “Get-VBRComputerBackupJobSession” to get the details.
    Can someone help me to fix the same.

    Reply
  11. Pingback: Veeam Backup Report Powershell - SecuredGuide

Leave a Reply

Your email address will not be published. Required fields are marked *