Veeam backup microsoft 365 script audit


Userlevel 4
Badge +1

Hello,

 

This is a script to audit VBM365. 

Feel free to say what you would like if it’s missing :) I know it’s not “complete”

 

# =======================================================
# NAME: VBM365audit.ps1
# AUTHOR: Commenge Damien, Axians Cloud Builder
# DATE: 11/07/2022
#
# VERSION 1.0
# COMMENTS: This script is created to Audit Veeam backup for microsoft 365
# <N/A> is used for not available
# =======================================================

$date = (get-date -Format "dd_MM_yyyy_HH_mm")

#Create directory
New-Item -ItemType Directory -Path "C:\temp\VBM365Audit\$date" -Force | Out-Null

#ReportPath
$ReportPath="C:\temp\VBM365Audit\$date"
#Report file HTML path
$htmlReportPath = "$ReportPath\VeeamBackupMicrosoft365.html"

$HTMLCSS = @'
<style>
body{color:black;font-family:Vinci Sans Light;font-size:0.79em;line-height:1.25;margin:5;}
a{color:black;}
H1{color:white;font-family:Verdana;font-weight:bold;font-size:20pt;margin-bottom:50px;margin-top:40px;text-align:center;background-color:#005EB8;}
H2{color:#A20067;font-family:Verdana;font-size:16pt;margin-left:14px;text-align:left;}
H3{color:#005EB8;font-family:Verdana;font-size:13pt;margin-left:16px;}
H4{color:black;font-family:Verdana;font-size:11pt;margin-left:16px;}
table {border-collapse: collapse;margin-left:10px;border-radius:7px 7px 0px 0px;}
th, td {padding: 8px;text-align: left;border-bottom: 1px solid #ddd;}
th {background-color: #296b0c;color: white;}
td:first-child{font-weight:bold;}
tr:nth-child(even){background-color: #f2f2f2}
table.table2 td:first-child{background-color: #A20067;color: white}
</style>
'@

#Connect to VBO Server
Write-host "$(get-date -Format HH:mm) - Connecting to VBM 365 server"
try {
Connect-VBOServer -ErrorAction Stop
Write-host "$(get-date -Format HH:mm) - Connected to VBM 365 server"

}
catch [System.Management.Automation.RuntimeException]{
Write-host "$(get-date -Format HH:mm) - Connexion is already done"
}
catch {
Write-host "$(get-date -Format HH:mm) - $($_.Exception.message) " -ForegroundColor Red
break
}

<#
.Synopsis
Get configuration Summary from Veeam Microsoft 365 server
.DESCRIPTION
Get server name, OS, OS build and VBM365 version
.EXAMPLE
Get-DCVBMSummary
#>
function Get-DCVBMSummary
{
Write-host "$(get-date -Format HH:mm) - VBM365 Summary"

$VBM365ServerName = $env:COMPUTERNAME
$VBM365ServerOS = (Get-WmiObject win32_operatingsystem).caption
$OSBuild = Get-ItemPropertyValue -path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion" -name 'UBR'
$VBM365Version = (Get-Module Veeam.Archiver.PowerShell).NestedModules.Version.ToString()

[PScustomObject]@{
Name = $VBM365ServerName
OS = $VBM365ServerOS
OSBuild = $OSBuild
VBM365Version = $VBM365Version
}
}

<#
.Synopsis
Get configuration about organizations
.DESCRIPTION
Get organization name, account used, type (on premise, hybride, O365), service (exchange, sharepoint), region, authentication (basic, modern with legacy protocol, modern), auxiliar backup account/application number
.EXAMPLE
Get-DCVBMOrganization
#>
function Get-DCVBMOrganization
{
Write-host "$(get-date -Format HH:mm) - VBM365 Organization"

$OrgName = (Get-VBOOrganization).OfficeName
$OrgAccount = (Get-VBOOrganization).username
$OrgType = (Get-VBOOrganization).type
$OrgService = (Get-VBOOrganization).BackupParts
$OrgRegion = (Get-VBOOrganization).region
if ((Get-VBOOrganization).Office365ExchangeConnectionSettings -ne $null)
{
$OrgAuth = (Get-VBOOrganization).Office365ExchangeConnectionSettings.AuthenticationType
}
else
{
$OrgAuth = (Get-VBOOrganization).Office365SharePointConnectionSettings.AuthenticationType
}
if ($OrgAuth -eq "Basic")
{
$AuxAccount = (Get-VBOOrganization).backupaccounts.count

}
else
{
$AuxAccount = (Get-VBOOrganization).backupapplications.count

}

[PScustomObject]@{
Name = $OrgName
Account = $OrgAccount
Type = $OrgType
Service = $OrgService
Region = $OrgRegion
Authentication = $OrgAuth
AuxAccount = $AuxAccount
}
}

<#
.Synopsis
Get configuration about job configuration
.DESCRIPTION
Get job name, type, included object, excluded object, repository, proxy, schedule, active or disabled state
.EXAMPLE
Get-DCVBMJob
#>
function Get-DCVBMJob
{
Write-host "$(get-date -Format HH:mm) - VBM365 Jobs"

foreach ($obj in Get-VBOJob)
{
$JobName = $obj.name
$JobType = $obj.JobBackupType
$JobIncludedObj = $obj.SelectedItems -join ","
$JobExcludedObj = $obj.ExcludedItems -join ","
$JobRepository = $obj.Repository
#Get proxy name from associated proxy ID repository
$JobProxy = (Get-VBOProxy -id (Get-VBORepository -name $obj.Repository).proxyid).Hostname
$JobSchedule = "<N/A>"
if ($obj.schedulepolicy.EnableSchedule -and $obj.schedulepolicy.Type -eq "daily")
{
$JobSchedule = $obj.schedulepolicy.dailytime.tostring()
}
$JobEnabled = $obj.IsEnabled


[PScustomObject]@{
Name = $JobName
Type = $JobType
InclObject = $JobIncludedObj
ExclObject = $OrgService
Repository = $JobRepository
Proxy = $JobProxy
Schedule = $JobSchedule
Enabled = $JobEnabled
}
}
}

<#
.Synopsis
Get configuration about proxy configuration
.DESCRIPTION
Get proxy name, port, thread number, throttling, internet proxy used or not, internet proxy port and account
.EXAMPLE
Get-DCVBMProxy
#>
function Get-DCVBMProxy
{

Write-host "$(get-date -Format HH:mm) - VBM365 Proxy"

foreach ($obj in Get-VBOProxy)
{
$ProxyName = $obj.hostname
$ProxyPort = $obj.port
$ProxyThread = $obj.ThreadsNumber
$ProxyThrottling = [string]$obj.ThrottlingValue + " " + $obj.ThrottlingUnit
$ProxyIntHost = "<N/A>"
$ProxyInternetPort = "<N/A>"
$ProxyInternetAccount = "<N/A>"
if ($obj.InternetProxy.UseInternetProxy)
{
$ProxyIntHost = $obj.InternetProxy.UseInternetProxy.Host
$ProxyInternetPort = $obj.InternetProxy.UseInternetProxy.Port
$ProxyInternetAccount = $obj.InternetProxy.UseInternetProxy.User
}

[PScustomObject]@{
Name = $ProxyName
Port = $ProxyPort
Thread = $ProxyThread
Throttling = $ProxyThrottling
IntProxyHost = $ProxyIntHost
IntProxyPort = $ProxyInternetPort
IntProxyAccount = $ProxyInternetAccount
}
}
}

<#
.Synopsis
Get configuration about repository configuration
.DESCRIPTION
Get repository name, proxy associated, path, host, retention type and value
.EXAMPLE
Get-DCVBMRepository
#>
function Get-DCVBMRepository
{

Write-host "$(get-date -Format HH:mm) - VBM365 Repository"

foreach ($obj in Get-VBORepository)
{
$RepositoryName = $obj.name
$RepositoryProxy = (Get-VBOProxy -id (Get-VBORepository -name $obj.name).proxyid).Hostname
$RepositoryPath = $obj.Path
$RepositoryHost = ""
$RepositoryRetention = [string]$obj.retentionperiod + " " + $obj.RetentionType

[PScustomObject]@{
Name = $RepositoryName
Proxy = $RepositoryProxy
Path = $RepositoryPath
Host = $RepositoryHost
Retention = $RepositoryRetention
}
}
}

<#
.Synopsis
Get configuration about license
.DESCRIPTION
Get license type, expiration date, customer, contact, usage
.EXAMPLE
Get-DCVBMLicense
#>
function Get-DCVBMLicense
{

Write-host "$(get-date -Format HH:mm) - VBM365 License"

$LicenseType = (Get-VBOLicense).type
$LicenseExpiration = (Get-VBOLicense).expirationdate.ToShortDateString()
$LicenseTo = (Get-VBOLicense).LicensedTo
$LicenseContact = (Get-VBOLicense).ContactPerson
$LicenseUser = [string](Get-VBOLicense).usedNumber + "/" + (Get-VBOLicense).TotalNumber

[PScustomObject]@{
Type = $LicenseType
Expiration = $LicenseExpiration
To = $LicenseTo
Contact = $LicenseContact
Number = $LicenseUser
}
}

<#
.Synopsis
Get configuration about restore operator configuration
.DESCRIPTION
Get role name, organization, operator, associated object, excluded object
.EXAMPLE
Get-DCVBMRestoreOperator
#>
function Get-DCVBMRestoreOperator
{
Write-host "$(get-date -Format HH:mm) - VBM365 Restore Operator"

foreach ($obj in Get-VBORbacRole)
{
$RoleName = $obj.name
$OrganizationName = (Get-VBOOrganization -Id ($obj.OrganizationId)).Name
$OperatorName = $obj.operators.DisplayName -join ","
$IncludedObject = "Organization"
if ($obj.RoleType -ne "EntireOrganization")
{
$IncludedObject = $obj.SelectedItems.DisplayName -join ","
}
$ExcludedObject = "<N/A>"
if ($obj.ExcludedItems -ne $null)
{
$ExcludedObject = $obj.ExcludedItems.DisplayName -join ","
}
[PScustomObject]@{
Role = $RoleName
Organization = $OrganizationName
Operator = $OperatorName
IncludedObject = $IncludedObject
ExcludedObject = $ExcludedObject
}
}
}

<#
.Synopsis
Get configuration about RestAPI configuration
.DESCRIPTION
Get state, token life time, port, certificate thumbprint friendly name and expiration date
.EXAMPLE
Get-DCVBMRestAPI
#>
function Get-DCVBMRestAPI
{
Write-host "$(get-date -Format HH:mm) - VBM365 REST API"

$Enabled = (Get-VBORestAPISettings).IsServiceEnabled
$TokenTime = (Get-VBORestAPISettings).AuthTokenLifeTime
$Port = (Get-VBORestAPISettings).HTTPSPort
$CertThumbprint = (Get-VBORestAPISettings).CertificateThumbprint
$CertFriendlyName = (Get-VBORestAPISettings).CertificateFriendlyName
$CertExpiration = (Get-VBORestAPISettings).CertificateExpirationDate.ToShortDateString()

[PScustomObject]@{
Enabled = $Enabled
TokenTime = $TokenTime
Port = $Port
CertThumbprint = $CertThumbprint
CertFriendlyName = $CertFriendlyName
CertExpiration = $CertExpiration
}
}


<#
.Synopsis
Get configuration about Restore portal configuration
.DESCRIPTION
Get state, application ID, certificate thumbprint friendly name and expiration date
.EXAMPLE
Get-DCVBMRestorePortal
#>
function Get-DCVBMRestorePortal
{

Write-host "$(get-date -Format HH:mm) - VBM365 Restore portal"

$Enabled = (Get-VBORestorePortalSettings).IsServiceEnabled
$ApplicationID = (Get-VBORestorePortalSettings).ApplicationId.Guid
$CertThumbprint = (Get-VBORestorePortalSettings).CertificateThumbprint
$CertFriendlyName = (Get-VBORestorePortalSettings).CertificateFriendlyName
$CertExpiration = (Get-VBORestorePortalSettings).CertificateExpirationDate.ToShortDateString()

[PScustomObject]@{
Enabled = $Enabled
ApplicationID = $ApplicationID
CertThumbprint = $CertThumbprint
CertFriendlyName = $CertFriendlyName
CertExpiration = $CertExpiration
}
}

<#
.Synopsis
Get configuration about operator Authentication portal configuration
.DESCRIPTION
Get state, certificate thumbprint friendly name and expiration date
.EXAMPLE
Get-DCVBMOperatorAuthentication
#>
function Get-DCVBMOperatorAuthentication
{

Write-host "$(get-date -Format HH:mm) - VBM365 Authentication"

$Enabled = (Get-VBOOperatorAuthenticationSettings).AuthenticationEnabled
$CertThumbprint = (Get-VBORestAPISettings).CertificateThumbprint
$CertFriendlyName = (Get-VBOOperatorAuthenticationSettings).CertificateFriendlyName
$CertExpiration = (Get-VBOOperatorAuthenticationSettings).CertificateExpirationDate

[PScustomObject]@{
Enabled = $Enabled
CertThumbprint = $CertThumbprint
CertFriendlyName = $CertFriendlyName
CertExpiration = $CertExpiration
}
}

<#
.Synopsis
Get configuration about internet proxy
.DESCRIPTION
Get state, host, port and account
.EXAMPLE
Get-DCVBMInternetProxy
#>
function Get-DCVBMInternetProxy
{

Write-host "$(get-date -Format HH:mm) - VBM365 Internet Proxy"

$IntProxyEnabled = (Get-VBOInternetProxySettings).UseInternetProxy
$IntProxyHost = "<N/A>"
$IntProxyPort = "<N/A>"
$IntProxyUser = "<N/A>"
if ((Get-VBOInternetProxySettings).UseInternetProxy)
{
$IntProxyHost = (Get-VBOInternetProxySettings).Host
$IntProxyPort = (Get-VBOInternetProxySettings).Port
$IntProxyUser = (Get-VBOInternetProxySettings).User
}

[PScustomObject]@{
Enabled = $IntProxyEnabled
Host = $IntProxyHost
Port = $IntProxyPort
Account = $IntProxyUser
}
}

<#
.Synopsis
Get configuration about SMTP
.DESCRIPTION
Get state, server, port, ssl, account
.EXAMPLE
Get-DCVBMSMTP
#>
function Get-DCVBMSMTP
{

Write-host "$(get-date -Format HH:mm) - VBM365 SMTP configuration"

$SMTPEnabled = (Get-VBOEmailSettings).EnableNotification
$SMTPServer = "<N/A>"
$SMTPPort = "<N/A>"
$SMTPSSL = "<N/A>"
$SMTPAccount = "<N/A>"
if ((Get-VBOEmailSettings).EnableNotification)
{
$SMTPServer = (Get-VBOEmailSettings).SMTPServer
$SMTPPort = (Get-VBOEmailSettings).Port
$SMTPSSL = (Get-VBOEmailSettings).UseSSL
if ((Get-VBOEmailSettings).UseAuthentication)
{
$SMTPAccount = (Get-VBOEmailSettings).Username
}
}

[PScustomObject]@{
Enabled = $SMTPEnabled
Server = $SMTPServer
Port = $SMTPPort
SSL = $SMTPSSL
Account = $SMTPAccount
}
}


<#
.Synopsis
Get configuration about Notifications
.DESCRIPTION
Get state, sender, receiver, notification on success, warning and failure, send only last retry notification
.EXAMPLE
Get-DCVBMNotification
#>
function Get-DCVBMNotification
{

Write-host "$(get-date -Format HH:mm) - VBM365 Notifications"

$NotificationEnabled = (Get-VBOEmailSettings).EnableNotification
$NotificationSender = "<N/A>"
$NotificationReceiver = "<N/A>"
$NotificationSuccess = "<N/A>"
$NotificationWarning = "<N/A>"
$NotificationFailure = "<N/A>"
$LastRetryNotificationOnly = "<N/A>"
if ((Get-VBOEmailSettings).EnableNotification)
{
$NotificationSender = (Get-VBOEmailSettings).From -join ","
$NotificationReceiver = (Get-VBOEmailSettings).To -join ","
$NotificationSuccess = (Get-VBOEmailSettings).NotifyOnSuccess
$NotificationWarning = (Get-VBOEmailSettings).NotifyOnWarning
$NotificationFailure = (Get-VBOEmailSettings).NotifyOnFailure
$LastRetryNotificationOnly = (Get-VBOEmailSettings).SupressUntilLastRetry
}

[PScustomObject]@{
Enabled = $NotificationEnabled
Sender = $NotificationSender
Receiver = $NotificationReceiver
OnSuccess = $NotificationSuccess
OnWarning = $NotificationWarning
OnFailure = $NotificationFailure
OnlyLastRetry = $LastRetryNotificationOnly
}
}


<#
.Synopsis
Create array for HTML report
.DESCRIPTION
Create array with title and precontent
.EXAMPLE
CreateArray -title "my Title" -var $MyData -PreContent $MyPrecontent
#>
Function CreateArray ($Title,$Var,$PreContent)
{
if ($Title)
{
"<h3>$Title</h3>"
}
if ($PreContent)
{
$Var | ConvertTo-Html -Fragment -PreContent $PreContent
}
else
{
$Var | ConvertTo-Html -Fragment
}
}


<#
.Synopsis
Generate HTML report
.DESCRIPTION
Use all variable to build html report with CSS style
.EXAMPLE
Get-HTMLReport -Path "c:\temp\report.html"
#>

function Get-HTMLReport
{
[CmdletBinding()]

Param
(
#HTML file path
[Parameter(Mandatory=$true)]
[string] $Path,

#HTML file name
[string] $FileName = "VBM365Audit.html"
)
Write-Host "Building HTML"

#region HTML
@"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Axians vSphere Report</title>
$HTMLCSS
</head>
<body>
<br><br><br><br>

<h1>VEEAM Backup for Microsoft 365 Report</h1>

$(CreateArray -title "Summary" -var $DCVBMSummary)
$(CreateArray -title "License" -var $DCVBMLicense)
$(CreateArray -title "SMTP" -var $DCVBMSMTP)
$(CreateArray -title "Notifications" -var $DCVBMNotification)
$(CreateArray -title "Internet Proxy" -var $DCVBMInternetProxy)

$(CreateArray -title "REST API" -var $DCVBMRestAPI)
$(CreateArray -title "Restore portal" -var $DCVBMRestorePortal)
$(CreateArray -title "Operator authentication" -var $DCVBMOperatorAuthentication )

$(CreateArray -title "Repositories" -var $DCVBMRepository)
$(CreateArray -title "Proxies" -var $DCVBMProxy)

$(CreateArray -title "Organizations" -var $DCVBMOrganization)
$(CreateArray -title "Jobs" -var $DCVBMJob)

$(CreateArray -title "Restore operators" -var $DCVBMRestoreOperator )

</body>
"@ | Out-File -Encoding utf8 $htmlReportPath

Invoke-Item $htmlReportPath


}
#endregion


#Write here all function that need to be displayed

$DCVBMSummary = Get-DCVBMSummary
$DCVBMOrganization = Get-DCVBMOrganization
$DCVBMJob = Get-DCVBMJob
$DCVBMProxy = Get-DCVBMProxy
$DCVBMRepository = Get-DCVBMRepository
$DCVBMLicense = Get-DCVBMLicense
$DCVBMRestoreOperator = Get-DCVBMRestoreOperator
$DCVBMRestAPI = Get-DCVBMRestAPI
$DCVBMRestorePortal = Get-DCVBMRestorePortal
$DCVBMOperatorAuthentication = Get-DCVBMOperatorAuthentication
$DCVBMInternetProxy = Get-DCVBMInternetProxy
$DCVBMSMTP = Get-DCVBMSMTP
$DCVBMNotification = Get-DCVBMNotification



Get-HTMLReport -Path "$ReportPath\$domain"


Disconnect-VBOServer

 


8 comments

Userlevel 7
Badge +8

This is great thanks for sharing 👍

Userlevel 4
Badge +1

Sample here 

 

I add object repository + backup copy job settings

I’m waiting an answer on the forum for host repository name to be found 
I see it’s impossible to edit my previous post, new code is here :

 

# =======================================================
# NAME: VBM365audit.ps1
# AUTHOR: Commenge Damien, Axians Cloud Builder
# DATE: 11/07/2022
#
# VERSION 1.0
# COMMENTS: This script is created to Audit Veeam backup for microsoft 365
# <N/A> is used for not available
# =======================================================

$date = (get-date -Format "dd_MM_yyyy_HH_mm")

#Create directory
New-Item -ItemType Directory -Path "C:\temp\VBM365Audit\$date" -Force | Out-Null

#ReportPath
$ReportPath="C:\temp\VBM365Audit\$date"
#Report file HTML path
$htmlReportPath = "$ReportPath\VeeamBackupMicrosoft365.html"

$HTMLCSS = @'
<style>
body{color:black;font-family:Vinci Sans Light;font-size:0.79em;line-height:1.25;margin:5;}
a{color:black;}
H1{color:white;font-family:Verdana;font-weight:bold;font-size:20pt;margin-bottom:50px;margin-top:40px;text-align:center;background-color:#005EB8;}
H2{color:#A20067;font-family:Verdana;font-size:16pt;margin-left:14px;text-align:left;}
H3{color:#005EB8;font-family:Verdana;font-size:13pt;margin-left:16px;}
H4{color:black;font-family:Verdana;font-size:11pt;margin-left:16px;}
table {border-collapse: collapse;margin-left:10px;border-radius:7px 7px 0px 0px;}
th, td {padding: 8px;text-align: left;border-bottom: 1px solid #ddd;}
th {background-color: #296b0c;color: white;}
td:first-child{font-weight:bold;}
tr:nth-child(even){background-color: #f2f2f2}
table.table2 td:first-child{background-color: #A20067;color: white}
</style>
'@

#Connect to VBO Server
Write-host "$(get-date -Format HH:mm) - Connecting to VBM 365 server"
try {
Connect-VBOServer -ErrorAction Stop
Write-host "$(get-date -Format HH:mm) - Connected to VBM 365 server"

}
catch [System.Management.Automation.RuntimeException]{
Write-host "$(get-date -Format HH:mm) - Connexion is already done"
}
catch {
Write-host "$(get-date -Format HH:mm) - $($_.Exception.message) " -ForegroundColor Red
break
}

<#
.Synopsis
Get configuration Summary from Veeam Microsoft 365 server
.DESCRIPTION
Get server name, OS, OS build and VBM365 version
.EXAMPLE
Get-DCVBMSummary
#>
function Get-DCVBMSummary
{
Write-host "$(get-date -Format HH:mm) - VBM365 Summary"

$VBM365ServerName = $env:COMPUTERNAME
$VBM365ServerOS = (Get-WmiObject win32_operatingsystem).caption
$OSBuild = Get-ItemPropertyValue -path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion" -name 'UBR'
$VBM365Version = (Get-Module Veeam.Archiver.PowerShell).NestedModules.Version.ToString()

[PScustomObject]@{
Name = $VBM365ServerName
OS = $VBM365ServerOS
OSBuild = $OSBuild
VBM365Version = $VBM365Version
}
}

<#
.Synopsis
Get configuration about organizations
.DESCRIPTION
Get organization name, account used, type (on premise, hybride, O365), service (exchange, sharepoint), region, authentication (basic, modern with legacy protocol, modern), auxiliar backup account/application number
.EXAMPLE
Get-DCVBMOrganization
#>
function Get-DCVBMOrganization
{
Write-host "$(get-date -Format HH:mm) - VBM365 Organization"

$OrgName = (Get-VBOOrganization).OfficeName
$OrgAccount = (Get-VBOOrganization).username
$OrgType = (Get-VBOOrganization).type
$OrgService = (Get-VBOOrganization).BackupParts
$OrgRegion = (Get-VBOOrganization).region
if ((Get-VBOOrganization).Office365ExchangeConnectionSettings -ne $null)
{
$OrgAuth = (Get-VBOOrganization).Office365ExchangeConnectionSettings.AuthenticationType
}
else
{
$OrgAuth = (Get-VBOOrganization).Office365SharePointConnectionSettings.AuthenticationType
}
if ($OrgAuth -eq "Basic")
{
$AuxAccount = (Get-VBOOrganization).backupaccounts.count

}
else
{
$AuxAccount = (Get-VBOOrganization).backupapplications.count

}

[PScustomObject]@{
Name = $OrgName
Account = $OrgAccount
Type = $OrgType
Service = $OrgService
Region = $OrgRegion
Authentication = $OrgAuth
AuxAccount = $AuxAccount
}
}

<#
.Synopsis
Get configuration about backup job configuration
.DESCRIPTION
Get job name, type, included object, excluded object, repository, proxy, schedule, active or disabled state
.EXAMPLE
Get-DCVBMBackupJob
#>
function Get-DCVBMBackupJob
{
Write-host "$(get-date -Format HH:mm) - VBM365 Backup Jobs"

foreach ($obj in Get-VBOJob)
{
$JobName = $obj.name
$JobType = $obj.JobBackupType
$JobIncludedObj = $obj.SelectedItems -join ","
$JobExcludedObj = $obj.ExcludedItems -join ","
$JobRepository = $obj.Repository
#Get proxy name from associated proxy ID repository
$JobProxy = (Get-VBOProxy -id (Get-VBORepository -name $obj.Repository).proxyid).Hostname
$JobSchedule = "<N/A>"
if ($obj.schedulepolicy.EnableSchedule -and $obj.schedulepolicy.Type -eq "daily")
{
$JobSchedule = $obj.schedulepolicy.dailytime.tostring()
}
$JobEnabled = $obj.IsEnabled


[PScustomObject]@{
Name = $JobName
Type = $JobType
InclObject = $JobIncludedObj
ExclObject = $OrgService
Repository = $JobRepository
Proxy = $JobProxy
Schedule = $JobSchedule
Enabled = $JobEnabled
}
}
}

<#
.Synopsis
Get configuration about backup copy job configuration
.DESCRIPTION
Get job name, repository, backupjob linked, schedule, active or disabled state
.EXAMPLE
Get-DCVBMBackupCopyJob
#>
function Get-DCVBMBackupCopyJob
{
Write-host "$(get-date -Format HH:mm) - VBM365 Backup copy Jobs"

foreach ($obj in Get-VBOCopyJob)
{
$JobName = $obj.name
$JobRepository = $obj.Repository
$JobBackupLinked = $obj.BackupJob
if ($obj.schedulepolicy.type -eq "daily")
{
$JobSchedule = [string]$obj.SchedulePolicy.DailyTime + " " + $obj.SchedulePolicy.DailyType
}
if ($obj.schedulepolicy.type -eq "Periodically")
{
$JobSchedule = $obj.SchedulePolicy.PeriodicallyEvery
}
else
{
$JobSchedule = $obj.SchedulePolicy.Type
}
$JobEnabled = $obj.IsEnabled

[PScustomObject]@{
Name = $JobName
Repository = $JobRepository
BackupLinked = $JobBackupLinked
Schedule = $JobSchedule
Enabled = $JobEnabled
}
}
}


<#
.Synopsis
Get configuration about proxy configuration
.DESCRIPTION
Get proxy name, port, thread number, throttling, internet proxy used or not, internet proxy port and account
.EXAMPLE
Get-DCVBMProxy
#>
function Get-DCVBMProxy
{

Write-host "$(get-date -Format HH:mm) - VBM365 Proxy"

foreach ($obj in Get-VBOProxy)
{
$ProxyName = $obj.hostname
$ProxyPort = $obj.port
$ProxyThread = $obj.ThreadsNumber
$ProxyThrottling = [string]$obj.ThrottlingValue + " " + $obj.ThrottlingUnit
$ProxyIntHost = "<N/A>"
$ProxyInternetPort = "<N/A>"
$ProxyInternetAccount = "<N/A>"
if ($obj.InternetProxy.UseInternetProxy)
{
$ProxyIntHost = $obj.InternetProxy.UseInternetProxy.Host
$ProxyInternetPort = $obj.InternetProxy.UseInternetProxy.Port
$ProxyInternetAccount = $obj.InternetProxy.UseInternetProxy.User
}

[PScustomObject]@{
Name = $ProxyName
Port = $ProxyPort
Thread = $ProxyThread
Throttling = $ProxyThrottling
IntProxyHost = $ProxyIntHost
IntProxyPort = $ProxyInternetPort
IntProxyAccount = $ProxyInternetAccount
}
}
}

<#
.Synopsis
Get configuration about repository configuration
.DESCRIPTION
Get repository name, proxy associated, path, host, retention type and value, repository object name and encryption
.EXAMPLE
Get-DCVBMRepository
#>
function Get-DCVBMRepository
{

Write-host "$(get-date -Format HH:mm) - VBM365 Repository"

foreach ($obj in Get-VBORepository)
{
$RepositoryName = $obj.name
$RepositoryProxy = (Get-VBOProxy -id (Get-VBORepository -name $obj.name).proxyid).Hostname
$RepositoryPath = $obj.Path
#En attente forum https://forums.veeam.com/veeam-backup-for-microsoft-365-f47/powershell-host-repository-t81718.html
$RepositoryHost = ""
$RepositoryRetention = [string]$obj.retentionperiod + " " + $obj.RetentionType
$RepositoryObjectName = "<N/A>"
if ($obj.ObjectStorageRepository -ne $null)
{
$RepositoryObjectName = $obj.ObjectStorageRepository.Name
}
$encryption = $obj.EnableObjectStorageEncryption
[PScustomObject]@{
Name = $RepositoryName
Proxy = $RepositoryProxy
Path = $RepositoryPath
Host = $RepositoryHost
ObjectRepository = $RepositoryObjectName
Retention = $RepositoryRetention
Encryption = $encryption
}
}
}

<#
.Synopsis
Get configuration about object repository configuration
.DESCRIPTION
Get repository name, folder, type, size limit and if it's long term achive
.EXAMPLE
Get-DCVBMRepository
#>
function Get-DCVBMObjectRepository
{

Write-host "$(get-date -Format HH:mm) - VBM365 Object Repository"

foreach ($obj in Get-VBOObjectStorageRepository)
{
$RepositoryName = $obj.name
$RepositoryFolder = $obj.Folder
$RepositoryType = $obj.Type
$RepositorySizeLimit = "<N/A>"
if ($obj.EnableSizeLimit)
{
$RepositorySizeLimit = [String]$obj.UsedSpace + "/" + $obj.SizeLimit
}
$RepositoryArchive = $obj.IsLongTerm
[PScustomObject]@{
Name = $RepositoryName
Folder = $RepositoryFolder
Type = $RepositoryType
SizeLimit = $RepositorySizeLimit
LongTerm = $RepositoryArchive
}
}
}

<#
.Synopsis
Get configuration about license
.DESCRIPTION
Get license type, expiration date, customer, contact, usage
.EXAMPLE
Get-DCVBMLicense
#>
function Get-DCVBMLicense
{

Write-host "$(get-date -Format HH:mm) - VBM365 License"

$LicenseType = (Get-VBOLicense).type
$LicenseExpiration = (Get-VBOLicense).expirationdate.ToShortDateString()
$LicenseTo = (Get-VBOLicense).LicensedTo
$LicenseContact = (Get-VBOLicense).ContactPerson
$LicenseUser = [string](Get-VBOLicense).usedNumber + "/" + (Get-VBOLicense).TotalNumber

[PScustomObject]@{
Type = $LicenseType
Expiration = $LicenseExpiration
To = $LicenseTo
Contact = $LicenseContact
Number = $LicenseUser
}
}

<#
.Synopsis
Get configuration about restore operator configuration
.DESCRIPTION
Get role name, organization, operator, associated object, excluded object
.EXAMPLE
Get-DCVBMRestoreOperator
#>
function Get-DCVBMRestoreOperator
{
Write-host "$(get-date -Format HH:mm) - VBM365 Restore Operator"

foreach ($obj in Get-VBORbacRole)
{
$RoleName = $obj.name
$OrganizationName = (Get-VBOOrganization -Id ($obj.OrganizationId)).Name
$OperatorName = $obj.operators.DisplayName -join ","
$IncludedObject = "Organization"
if ($obj.RoleType -ne "EntireOrganization")
{
$IncludedObject = $obj.SelectedItems.DisplayName -join ","
}
$ExcludedObject = "<N/A>"
if ($obj.ExcludedItems -ne $null)
{
$ExcludedObject = $obj.ExcludedItems.DisplayName -join ","
}
[PScustomObject]@{
Role = $RoleName
Organization = $OrganizationName
Operator = $OperatorName
IncludedObject = $IncludedObject
ExcludedObject = $ExcludedObject
}
}
}

<#
.Synopsis
Get configuration about RestAPI configuration
.DESCRIPTION
Get state, token life time, port, certificate thumbprint friendly name and expiration date
.EXAMPLE
Get-DCVBMRestAPI
#>
function Get-DCVBMRestAPI
{
Write-host "$(get-date -Format HH:mm) - VBM365 REST API"

$Enabled = (Get-VBORestAPISettings).IsServiceEnabled
$TokenTime = (Get-VBORestAPISettings).AuthTokenLifeTime
$Port = (Get-VBORestAPISettings).HTTPSPort
$CertThumbprint = (Get-VBORestAPISettings).CertificateThumbprint
$CertFriendlyName = (Get-VBORestAPISettings).CertificateFriendlyName
$CertExpiration = (Get-VBORestAPISettings).CertificateExpirationDate.ToShortDateString()

[PScustomObject]@{
Enabled = $Enabled
TokenTime = $TokenTime
Port = $Port
CertThumbprint = $CertThumbprint
CertFriendlyName = $CertFriendlyName
CertExpiration = $CertExpiration
}
}


<#
.Synopsis
Get configuration about Restore portal configuration
.DESCRIPTION
Get state, application ID, certificate thumbprint friendly name and expiration date
.EXAMPLE
Get-DCVBMRestorePortal
#>
function Get-DCVBMRestorePortal
{

Write-host "$(get-date -Format HH:mm) - VBM365 Restore portal"

$Enabled = (Get-VBORestorePortalSettings).IsServiceEnabled
$ApplicationID = (Get-VBORestorePortalSettings).ApplicationId.Guid
$CertThumbprint = (Get-VBORestorePortalSettings).CertificateThumbprint
$CertFriendlyName = (Get-VBORestorePortalSettings).CertificateFriendlyName
$CertExpiration = (Get-VBORestorePortalSettings).CertificateExpirationDate.ToShortDateString()

[PScustomObject]@{
Enabled = $Enabled
ApplicationID = $ApplicationID
CertThumbprint = $CertThumbprint
CertFriendlyName = $CertFriendlyName
CertExpiration = $CertExpiration
}
}

<#
.Synopsis
Get configuration about operator Authentication portal configuration
.DESCRIPTION
Get state, certificate thumbprint friendly name and expiration date
.EXAMPLE
Get-DCVBMOperatorAuthentication
#>
function Get-DCVBMOperatorAuthentication
{

Write-host "$(get-date -Format HH:mm) - VBM365 Authentication"

$Enabled = (Get-VBOOperatorAuthenticationSettings).AuthenticationEnabled
$CertThumbprint = (Get-VBORestAPISettings).CertificateThumbprint
$CertFriendlyName = (Get-VBOOperatorAuthenticationSettings).CertificateFriendlyName
$CertExpiration = (Get-VBOOperatorAuthenticationSettings).CertificateExpirationDate

[PScustomObject]@{
Enabled = $Enabled
CertThumbprint = $CertThumbprint
CertFriendlyName = $CertFriendlyName
CertExpiration = $CertExpiration
}
}

<#
.Synopsis
Get configuration about internet proxy
.DESCRIPTION
Get state, host, port and account
.EXAMPLE
Get-DCVBMInternetProxy
#>
function Get-DCVBMInternetProxy
{

Write-host "$(get-date -Format HH:mm) - VBM365 Internet Proxy"

$IntProxyEnabled = (Get-VBOInternetProxySettings).UseInternetProxy
$IntProxyHost = "<N/A>"
$IntProxyPort = "<N/A>"
$IntProxyUser = "<N/A>"
if ((Get-VBOInternetProxySettings).UseInternetProxy)
{
$IntProxyHost = (Get-VBOInternetProxySettings).Host
$IntProxyPort = (Get-VBOInternetProxySettings).Port
$IntProxyUser = (Get-VBOInternetProxySettings).User
}

[PScustomObject]@{
Enabled = $IntProxyEnabled
Host = $IntProxyHost
Port = $IntProxyPort
Account = $IntProxyUser
}
}

<#
.Synopsis
Get configuration about SMTP
.DESCRIPTION
Get state, server, port, ssl, account
.EXAMPLE
Get-DCVBMSMTP
#>
function Get-DCVBMSMTP
{

Write-host "$(get-date -Format HH:mm) - VBM365 SMTP configuration"

$SMTPEnabled = (Get-VBOEmailSettings).EnableNotification
$SMTPServer = "<N/A>"
$SMTPPort = "<N/A>"
$SMTPSSL = "<N/A>"
$SMTPAccount = "<N/A>"
if ((Get-VBOEmailSettings).EnableNotification)
{
$SMTPServer = (Get-VBOEmailSettings).SMTPServer
$SMTPPort = (Get-VBOEmailSettings).Port
$SMTPSSL = (Get-VBOEmailSettings).UseSSL
if ((Get-VBOEmailSettings).UseAuthentication)
{
$SMTPAccount = (Get-VBOEmailSettings).Username
}
}

[PScustomObject]@{
Enabled = $SMTPEnabled
Server = $SMTPServer
Port = $SMTPPort
SSL = $SMTPSSL
Account = $SMTPAccount
}
}


<#
.Synopsis
Get configuration about Notifications
.DESCRIPTION
Get state, sender, receiver, notification on success, warning and failure, send only last retry notification
.EXAMPLE
Get-DCVBMNotification
#>
function Get-DCVBMNotification
{

Write-host "$(get-date -Format HH:mm) - VBM365 Notifications"

$NotificationEnabled = (Get-VBOEmailSettings).EnableNotification
$NotificationSender = "<N/A>"
$NotificationReceiver = "<N/A>"
$NotificationSuccess = "<N/A>"
$NotificationWarning = "<N/A>"
$NotificationFailure = "<N/A>"
$LastRetryNotificationOnly = "<N/A>"
if ((Get-VBOEmailSettings).EnableNotification)
{
$NotificationSender = (Get-VBOEmailSettings).From -join ","
$NotificationReceiver = (Get-VBOEmailSettings).To -join ","
$NotificationSuccess = (Get-VBOEmailSettings).NotifyOnSuccess
$NotificationWarning = (Get-VBOEmailSettings).NotifyOnWarning
$NotificationFailure = (Get-VBOEmailSettings).NotifyOnFailure
$LastRetryNotificationOnly = (Get-VBOEmailSettings).SupressUntilLastRetry
}

[PScustomObject]@{
Enabled = $NotificationEnabled
Sender = $NotificationSender
Receiver = $NotificationReceiver
OnSuccess = $NotificationSuccess
OnWarning = $NotificationWarning
OnFailure = $NotificationFailure
OnlyLastRetry = $LastRetryNotificationOnly
}
}


<#
.Synopsis
Create array for HTML report
.DESCRIPTION
Create array with title and precontent
.EXAMPLE
CreateArray -title "my Title" -var $MyData -PreContent $MyPrecontent
#>
Function CreateArray ($Title,$Var,$PreContent)
{
if ($Title)
{
"<h3>$Title</h3>"
}
if ($PreContent)
{
$Var | ConvertTo-Html -Fragment -PreContent $PreContent
}
else
{
$Var | ConvertTo-Html -Fragment
}
}


<#
.Synopsis
Generate HTML report
.DESCRIPTION
Use all variable to build html report with CSS style
.EXAMPLE
Get-HTMLReport -Path "c:\temp\report.html"
#>

function Get-HTMLReport
{
[CmdletBinding()]

Param
(
#HTML file path
[Parameter(Mandatory=$true)]
[string] $Path,

#HTML file name
[string] $FileName = "VBM365Audit.html"
)
Write-Host "Building HTML"

#region HTML
@"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Axians vSphere Report</title>
$HTMLCSS
</head>
<body>
<br><br><br><br>

<h1>VEEAM Backup for Microsoft 365 Report</h1>

$(CreateArray -title "Summary" -var $DCVBMSummary)
$(CreateArray -title "License" -var $DCVBMLicense)
$(CreateArray -title "SMTP" -var $DCVBMSMTP)
$(CreateArray -title "Notifications" -var $DCVBMNotification)
$(CreateArray -title "Internet Proxy" -var $DCVBMInternetProxy)

$(CreateArray -title "REST API" -var $DCVBMRestAPI)
$(CreateArray -title "Restore portal" -var $DCVBMRestorePortal)
$(CreateArray -title "Authentication" -var $DCVBMAuthentication )

$(CreateArray -title "Repositories" -var $DCVBMRepository)
$(CreateArray -title "Object Repositories" -var $DCVBMObjectRepository)

$(CreateArray -title "Proxies" -var $DCVBMProxy)

$(CreateArray -title "Organizations" -var $DCVBMOrganization)
$(CreateArray -title "Backup jobs" -var $DCVBMBackupJob)
$(CreateArray -title "Backup copy jobs" -var $DCVBMBackupCopyJob)



$(CreateArray -title "Restore operators" -var $DCVBMRestoreOperator )

</body>
"@ | Out-File -Encoding utf8 $htmlReportPath

Invoke-Item $htmlReportPath


}
#endregion






#Write here all function that need to be displayed in all reports types

$DCVBMSummary = Get-DCVBMSummary
$DCVBMOrganization = Get-DCVBMOrganization
$DCVBMBackupJob = Get-DCVBMBackupJob
$DCVBMProxy = Get-DCVBMProxy
$DCVBMRepository = Get-DCVBMRepository
$DCVBMLicense = Get-DCVBMLicense
$DCVBMRestoreOperator = Get-DCVBMRestoreOperator
$DCVBMRestAPI = Get-DCVBMRestAPI
$DCVBMRestorePortal = Get-DCVBMRestorePortal
$DCVBMOperatorAuthentication = Get-DCVBMOperatorAuthentication
$DCVBMInternetProxy = Get-DCVBMInternetProxy
$DCVBMSMTP = Get-DCVBMSMTP
$DCVBMNotification = Get-DCVBMNotification
$DCVBMObjectRepository = Get-DCVBMObjectRepository
$DCVBMBackupCopyJob = Get-DCVBMBackupCopyJob



Get-HTMLReport -Path "$ReportPath\$domain"


Disconnect-VBOServer

 

Userlevel 4
Badge +1

New version with some fixes :

-VBM version

-Host for repository is the proxy (I ignored it)

-Change green color

# =======================================================
# NAME: VBM365audit.ps1
# AUTHOR: Commenge Damien, Axians Cloud Builder
# DATE: 11/07/2022
#
# VERSION 1.01
# COMMENTS: This script is created to Audit Veeam backup for microsoft 365
# <N/A> is used for not available
# =======================================================

$date = (get-date -Format "dd_MM_yyyy_HH_mm")

#Create directory
New-Item -ItemType Directory -Path "C:\temp\VBM365Audit\$date" -Force | Out-Null

#ReportPath
$ReportPath="C:\temp\VBM365Audit\$date"
#Report file HTML path
$htmlReportPath = "$ReportPath\VeeamBackupMicrosoft365.html"

$HTMLTitle = "VBM365 report"
$HTMLCSS = @'
<style>
body{color:black;font-family:Vinci Sans Light;font-size:0.79em;line-height:1.25;margin:5;}
a{color:black;}
H1{color:white;font-family:Verdana;font-weight:bold;font-size:20pt;margin-bottom:50px;margin-top:40px;text-align:center;background-color:#005EB8;}
H2{color:#A20067;font-family:Verdana;font-size:16pt;margin-left:14px;text-align:left;}
H3{color:#005EB8;font-family:Verdana;font-size:13pt;margin-left:16px;}
H4{color:black;font-family:Verdana;font-size:11pt;margin-left:16px;}
table {border-collapse: collapse;margin-left:10px;border-radius:7px 7px 0px 0px;}
th, td {padding: 8px;text-align: left;border-bottom: 1px solid #ddd;}
th {background-color: #006400;color: white;}
td:first-child{font-weight:bold;}
tr:nth-child(even){background-color: #f2f2f2}
table.table2 td:first-child{background-color: #A20067;color: white}
</style>
'@

#Connect to VBO Server
Write-host "$(get-date -Format HH:mm) - Connecting to VBM 365 server"
try {
Connect-VBOServer -ErrorAction Stop
Write-host "$(get-date -Format HH:mm) - Connected to VBM 365 server"

}
catch [System.Management.Automation.RuntimeException]{
Write-host "$(get-date -Format HH:mm) - Connexion is already done"
}
catch {
Write-host "$(get-date -Format HH:mm) - $($_.Exception.message) " -ForegroundColor Red
break
}

<#
.Synopsis
Get configuration Summary from Veeam Microsoft 365 server
.DESCRIPTION
Get server name, OS, OS build and VBM365 version
.EXAMPLE
Get-DCVBMSummary
#>
function Get-DCVBMSummary
{
Write-host "$(get-date -Format HH:mm) - VBM365 Summary"

$VBM365ServerName = $env:COMPUTERNAME
$VBM365ServerOS = (Get-WmiObject win32_operatingsystem).caption
$OSBuild = Get-ItemPropertyValue -path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion" -name 'UBR'
#No way to find version with powershell but associated version with VBR is available here : https://www.veeam.com/kb4106
$VBRbuild = (Get-Module Veeam.Archiver.PowerShell).nestedmodules.version.tostring()
switch ($VBRbuild)
{
"10.0.2.1061" {$VBM365Build = "5.0.0.1061"}
"10.0.2.1063" {$VBM365Build = "5.0.0.1063"}
"10.0.2.1070" {$VBM365Build = "5.0.0.1070"}
"10.0.3.179" {$VBM365Build = "5.0.1.179"}
"10.0.3.207" {$VBM365Build = "5.0.1.207"}
"10.0.3.225" {$VBM365Build = "5.0.1.225"}
"10.0.3.252" {$VBM365Build = "5.0.1.252"}
"10.0.4.22" {$VBM365Build = "5.0.2.22"}
"10.0.4.42" {$VBM365Build = "5.0.2.42"}
"10.0.5.1033" {$VBM365Build = "5.0.3.1033"}
"10.0.5.1035" {$VBM365Build = "5.0.3.1035"}
"10.0.5.1051" {$VBM365Build = "5.0.3.1051"}
"10.0.5.1060" {$VBM365Build = "5.0.3.1060"}
"10.0.5.1063" {$VBM365Build = "5.0.3.1063"}
"11.1.0.367" {$VBM365Build = "6.0.0.367"}
"11.1.0.379" {$VBM365Build = "6.0.0.379"}
"11.1.0.385" {$VBM365Build = "6.0.0.385"}
Default {$VBM365Build = "Unknown Value, script update is necessary"}
}

[PScustomObject]@{
Name = $VBM365ServerName
OS = $VBM365ServerOS
OSBuild = $OSBuild
VBM365Version = $VBM365Build
}
}

<#
.Synopsis
Get configuration about organizations
.DESCRIPTION
Get organization name, account used, type (on premise, hybride, O365), service (exchange, sharepoint), region, authentication (basic, modern with legacy protocol, modern), auxiliar backup account/application number
.EXAMPLE
Get-DCVBMOrganization
#>
function Get-DCVBMOrganization
{
Write-host "$(get-date -Format HH:mm) - VBM365 Organization"

$OrgName = (Get-VBOOrganization).OfficeName
$OrgAccount = (Get-VBOOrganization).username
$OrgType = (Get-VBOOrganization).type
$OrgService = (Get-VBOOrganization).BackupParts
$OrgRegion = (Get-VBOOrganization).region
if ((Get-VBOOrganization).Office365ExchangeConnectionSettings -ne $null)
{
$OrgAuth = (Get-VBOOrganization).Office365ExchangeConnectionSettings.AuthenticationType
}
else
{
$OrgAuth = (Get-VBOOrganization).Office365SharePointConnectionSettings.AuthenticationType
}
if ($OrgAuth -eq "Basic")
{
$AuxAccount = (Get-VBOOrganization).backupaccounts.count

}
else
{
$AuxAccount = (Get-VBOOrganization).backupapplications.count

}

[PScustomObject]@{
Name = $OrgName
Account = $OrgAccount
Type = $OrgType
Service = $OrgService
Region = $OrgRegion
Authentication = $OrgAuth
AuxAccount = $AuxAccount
}
}

<#
.Synopsis
Get configuration about backup job configuration
.DESCRIPTION
Get job name, type, included object, excluded object, repository, proxy, schedule, active or disabled state
.EXAMPLE
Get-DCVBMBackupJob
#>
function Get-DCVBMBackupJob
{
Write-host "$(get-date -Format HH:mm) - VBM365 Backup Jobs"

foreach ($obj in Get-VBOJob)
{
$JobName = $obj.name
$JobType = $obj.JobBackupType
$JobIncludedObj = $obj.SelectedItems -join ","
$JobExcludedObj = $obj.ExcludedItems -join ","
$JobRepository = $obj.Repository
#Get proxy name from associated proxy ID repository
$JobProxy = (Get-VBOProxy -id (Get-VBORepository -name $obj.Repository).proxyid).Hostname
$JobSchedule = "<N/A>"
if ($obj.schedulepolicy.EnableSchedule -and $obj.schedulepolicy.Type -eq "daily")
{
$JobSchedule = $obj.schedulepolicy.dailytime.tostring()
}
$JobEnabled = $obj.IsEnabled


[PScustomObject]@{
Name = $JobName
Type = $JobType
InclObject = $JobIncludedObj
ExclObject = $JobExcludedObj
Repository = $JobRepository
Proxy = $JobProxy
Schedule = $JobSchedule
Enabled = $JobEnabled
}
}
}

<#
.Synopsis
Get configuration about backup copy job configuration
.DESCRIPTION
Get job name, repository, backupjob linked, schedule, active or disabled state
.EXAMPLE
Get-DCVBMBackupCopyJob
#>
function Get-DCVBMBackupCopyJob
{
Write-host "$(get-date -Format HH:mm) - VBM365 Backup copy Jobs"

foreach ($obj in Get-VBOCopyJob)
{
$JobName = $obj.name
$JobRepository = $obj.Repository
$JobBackupLinked = $obj.BackupJob
if ($obj.schedulepolicy.type -eq "daily")
{
$JobSchedule = [string]$obj.SchedulePolicy.DailyTime + " " + $obj.SchedulePolicy.DailyType
}
if ($obj.schedulepolicy.type -eq "Periodically")
{
$JobSchedule = $obj.SchedulePolicy.PeriodicallyEvery
}
else
{
$JobSchedule = $obj.SchedulePolicy.Type
}
$JobEnabled = $obj.IsEnabled

[PScustomObject]@{
Name = $JobName
Repository = $JobRepository
BackupLinked = $JobBackupLinked
Schedule = $JobSchedule
Enabled = $JobEnabled
}
}
}


<#
.Synopsis
Get configuration about proxy configuration
.DESCRIPTION
Get proxy name, port, thread number, throttling, internet proxy used or not, internet proxy port and account
.EXAMPLE
Get-DCVBMProxy
#>
function Get-DCVBMProxy
{

Write-host "$(get-date -Format HH:mm) - VBM365 Proxy"

foreach ($obj in Get-VBOProxy)
{
$ProxyName = $obj.hostname
$ProxyPort = $obj.port
$ProxyThread = $obj.ThreadsNumber
$ProxyThrottling = [string]$obj.ThrottlingValue + " " + $obj.ThrottlingUnit
$ProxyIntHost = "<N/A>"
$ProxyInternetPort = "<N/A>"
$ProxyInternetAccount = "<N/A>"
if ($obj.InternetProxy.UseInternetProxy)
{
$ProxyIntHost = $obj.InternetProxy.UseInternetProxy.Host
$ProxyInternetPort = $obj.InternetProxy.UseInternetProxy.Port
$ProxyInternetAccount = $obj.InternetProxy.UseInternetProxy.User
}

[PScustomObject]@{
Name = $ProxyName
Port = $ProxyPort
Thread = $ProxyThread
Throttling = $ProxyThrottling
IntProxyHost = $ProxyIntHost
IntProxyPort = $ProxyInternetPort
IntProxyAccount = $ProxyInternetAccount
}
}
}

<#
.Synopsis
Get configuration about repository configuration
.DESCRIPTION
Get repository name, proxy associated, path, retention type and value, repository object name and encryption
.EXAMPLE
Get-DCVBMRepository
#>
function Get-DCVBMRepository
{

Write-host "$(get-date -Format HH:mm) - VBM365 Repository"

foreach ($obj in Get-VBORepository)
{
$RepositoryName = $obj.name
$RepositoryProxy = (Get-VBOProxy -id (Get-VBORepository -name $obj.name).proxyid).Hostname
$RepositoryPath = $obj.Path
#En attente forum https://forums.veeam.com/veeam-backup-for-microsoft-365-f47/powershell-host-repository-t81718.html
$RepositoryRetention = [string]$obj.retentionperiod + " " + $obj.RetentionType
$RepositoryObjectName = "<N/A>"
if ($obj.ObjectStorageRepository -ne $null)
{
$RepositoryObjectName = $obj.ObjectStorageRepository.Name
}
$encryption = $obj.EnableObjectStorageEncryption
[PScustomObject]@{
Name = $RepositoryName
Proxy = $RepositoryProxy
Path = $RepositoryPath
ObjectRepository = $RepositoryObjectName
Retention = $RepositoryRetention
Encryption = $encryption
}
}
}

<#
.Synopsis
Get configuration about object repository configuration
.DESCRIPTION
Get repository name, folder, type, size limit and if it's long term achive
.EXAMPLE
Get-DCVBMRepository
#>
function Get-DCVBMObjectRepository
{

Write-host "$(get-date -Format HH:mm) - VBM365 Object Repository"

foreach ($obj in Get-VBOObjectStorageRepository)
{
$RepositoryName = $obj.name
$RepositoryFolder = $obj.Folder
$RepositoryType = $obj.Type
$RepositorySizeLimit = "<N/A>"
if ($obj.EnableSizeLimit)
{
$RepositorySizeLimit = [String]$obj.UsedSpace + "/" + $obj.SizeLimit
}
$RepositoryArchive = $obj.IsLongTerm
[PScustomObject]@{
Name = $RepositoryName
Folder = $RepositoryFolder
Type = $RepositoryType
SizeLimit = $RepositorySizeLimit
LongTerm = $RepositoryArchive
}
}
}

<#
.Synopsis
Get configuration about license
.DESCRIPTION
Get license type, expiration date, customer, contact, usage
.EXAMPLE
Get-DCVBMLicense
#>
function Get-DCVBMLicense
{

Write-host "$(get-date -Format HH:mm) - VBM365 License"

$LicenseType = (Get-VBOLicense).type
$LicenseExpiration = (Get-VBOLicense).expirationdate.ToShortDateString()
$LicenseTo = (Get-VBOLicense).LicensedTo
$LicenseContact = (Get-VBOLicense).ContactPerson
$LicenseUser = [string](Get-VBOLicense).usedNumber + "/" + (Get-VBOLicense).TotalNumber

[PScustomObject]@{
Type = $LicenseType
Expiration = $LicenseExpiration
To = $LicenseTo
Contact = $LicenseContact
Number = $LicenseUser
}
}

<#
.Synopsis
Get configuration about restore operator configuration
.DESCRIPTION
Get role name, organization, operator, associated object, excluded object
.EXAMPLE
Get-DCVBMRestoreOperator
#>
function Get-DCVBMRestoreOperator
{
Write-host "$(get-date -Format HH:mm) - VBM365 Restore Operator"

foreach ($obj in Get-VBORbacRole)
{
$RoleName = $obj.name
$OrganizationName = (Get-VBOOrganization -Id ($obj.OrganizationId)).Name
$OperatorName = $obj.operators.DisplayName -join ","
$IncludedObject = "Organization"
if ($obj.RoleType -ne "EntireOrganization")
{
$IncludedObject = $obj.SelectedItems.DisplayName -join ","
}
$ExcludedObject = "<N/A>"
if ($obj.ExcludedItems -ne $null)
{
$ExcludedObject = $obj.ExcludedItems.DisplayName -join ","
}
[PScustomObject]@{
Role = $RoleName
Organization = $OrganizationName
Operator = $OperatorName
IncludedObject = $IncludedObject
ExcludedObject = $ExcludedObject
}
}
}

<#
.Synopsis
Get configuration about RestAPI configuration
.DESCRIPTION
Get state, token life time, port, certificate thumbprint friendly name and expiration date
.EXAMPLE
Get-DCVBMRestAPI
#>
function Get-DCVBMRestAPI
{
Write-host "$(get-date -Format HH:mm) - VBM365 REST API"

$Enabled = (Get-VBORestAPISettings).IsServiceEnabled
$TokenTime = (Get-VBORestAPISettings).AuthTokenLifeTime
$Port = (Get-VBORestAPISettings).HTTPSPort
$CertThumbprint = (Get-VBORestAPISettings).CertificateThumbprint
$CertFriendlyName = (Get-VBORestAPISettings).CertificateFriendlyName
$CertExpiration = (Get-VBORestAPISettings).CertificateExpirationDate.ToShortDateString()

[PScustomObject]@{
Enabled = $Enabled
TokenTime = $TokenTime
Port = $Port
CertThumbprint = $CertThumbprint
CertFriendlyName = $CertFriendlyName
CertExpiration = $CertExpiration
}
}


<#
.Synopsis
Get configuration about Restore portal configuration
.DESCRIPTION
Get state, application ID, certificate thumbprint friendly name and expiration date
.EXAMPLE
Get-DCVBMRestorePortal
#>
function Get-DCVBMRestorePortal
{

Write-host "$(get-date -Format HH:mm) - VBM365 Restore portal"

$Enabled = (Get-VBORestorePortalSettings).IsServiceEnabled
$ApplicationID = (Get-VBORestorePortalSettings).ApplicationId.Guid
$CertThumbprint = (Get-VBORestorePortalSettings).CertificateThumbprint
$CertFriendlyName = (Get-VBORestorePortalSettings).CertificateFriendlyName
$CertExpiration = (Get-VBORestorePortalSettings).CertificateExpirationDate.ToShortDateString()

[PScustomObject]@{
Enabled = $Enabled
ApplicationID = $ApplicationID
CertThumbprint = $CertThumbprint
CertFriendlyName = $CertFriendlyName
CertExpiration = $CertExpiration
}
}

<#
.Synopsis
Get configuration about operator Authentication portal configuration
.DESCRIPTION
Get state, certificate thumbprint friendly name and expiration date
.EXAMPLE
Get-DCVBMOperatorAuthentication
#>
function Get-DCVBMOperatorAuthentication
{

Write-host "$(get-date -Format HH:mm) - VBM365 Authentication"

$Enabled = (Get-VBOOperatorAuthenticationSettings).AuthenticationEnabled
$CertThumbprint = (Get-VBORestAPISettings).CertificateThumbprint
$CertFriendlyName = (Get-VBOOperatorAuthenticationSettings).CertificateFriendlyName
$CertExpiration = (Get-VBOOperatorAuthenticationSettings).CertificateExpirationDate

[PScustomObject]@{
Enabled = $Enabled
CertThumbprint = $CertThumbprint
CertFriendlyName = $CertFriendlyName
CertExpiration = $CertExpiration
}
}

<#
.Synopsis
Get configuration about internet proxy
.DESCRIPTION
Get state, host, port and account
.EXAMPLE
Get-DCVBMInternetProxy
#>
function Get-DCVBMInternetProxy
{

Write-host "$(get-date -Format HH:mm) - VBM365 Internet Proxy"

$IntProxyEnabled = (Get-VBOInternetProxySettings).UseInternetProxy
$IntProxyHost = "<N/A>"
$IntProxyPort = "<N/A>"
$IntProxyUser = "<N/A>"
if ((Get-VBOInternetProxySettings).UseInternetProxy)
{
$IntProxyHost = (Get-VBOInternetProxySettings).Host
$IntProxyPort = (Get-VBOInternetProxySettings).Port
$IntProxyUser = (Get-VBOInternetProxySettings).User
}

[PScustomObject]@{
Enabled = $IntProxyEnabled
Host = $IntProxyHost
Port = $IntProxyPort
Account = $IntProxyUser
}
}

<#
.Synopsis
Get configuration about SMTP
.DESCRIPTION
Get state, server, port, ssl, account
.EXAMPLE
Get-DCVBMSMTP
#>
function Get-DCVBMSMTP
{

Write-host "$(get-date -Format HH:mm) - VBM365 SMTP configuration"

$SMTPEnabled = (Get-VBOEmailSettings).EnableNotification
$SMTPServer = "<N/A>"
$SMTPPort = "<N/A>"
$SMTPSSL = "<N/A>"
$SMTPAccount = "<N/A>"
if ((Get-VBOEmailSettings).EnableNotification)
{
$SMTPServer = (Get-VBOEmailSettings).SMTPServer
$SMTPPort = (Get-VBOEmailSettings).Port
$SMTPSSL = (Get-VBOEmailSettings).UseSSL
if ((Get-VBOEmailSettings).UseAuthentication)
{
$SMTPAccount = (Get-VBOEmailSettings).Username
}
}

[PScustomObject]@{
Enabled = $SMTPEnabled
Server = $SMTPServer
Port = $SMTPPort
SSL = $SMTPSSL
Account = $SMTPAccount
}
}


<#
.Synopsis
Get configuration about Notifications
.DESCRIPTION
Get state, sender, receiver, notification on success, warning and failure, send only last retry notification
.EXAMPLE
Get-DCVBMNotification
#>
function Get-DCVBMNotification
{

Write-host "$(get-date -Format HH:mm) - VBM365 Notifications"

$NotificationEnabled = (Get-VBOEmailSettings).EnableNotification
$NotificationSender = "<N/A>"
$NotificationReceiver = "<N/A>"
$NotificationSuccess = "<N/A>"
$NotificationWarning = "<N/A>"
$NotificationFailure = "<N/A>"
$LastRetryNotificationOnly = "<N/A>"
if ((Get-VBOEmailSettings).EnableNotification)
{
$NotificationSender = (Get-VBOEmailSettings).From -join ","
$NotificationReceiver = (Get-VBOEmailSettings).To -join ","
$NotificationSuccess = (Get-VBOEmailSettings).NotifyOnSuccess
$NotificationWarning = (Get-VBOEmailSettings).NotifyOnWarning
$NotificationFailure = (Get-VBOEmailSettings).NotifyOnFailure
$LastRetryNotificationOnly = (Get-VBOEmailSettings).SupressUntilLastRetry
}

[PScustomObject]@{
Enabled = $NotificationEnabled
Sender = $NotificationSender
Receiver = $NotificationReceiver
OnSuccess = $NotificationSuccess
OnWarning = $NotificationWarning
OnFailure = $NotificationFailure
OnlyLastRetry = $LastRetryNotificationOnly
}
}


<#
.Synopsis
Create array for HTML report
.DESCRIPTION
Create array with title and precontent
.EXAMPLE
CreateArray -title "my Title" -var $MyData -PreContent $MyPrecontent
#>
Function CreateArray ($Title,$Var,$PreContent)
{
if ($Title)
{
"<h3>$Title</h3>"
}
if ($PreContent)
{
$Var | ConvertTo-Html -Fragment -PreContent $PreContent
}
else
{
$Var | ConvertTo-Html -Fragment
}
}


<#
.Synopsis
Generate HTML report
.DESCRIPTION
Use all variable to build html report with CSS style
.EXAMPLE
Get-HTMLReport -Path "c:\temp\report.html"
#>

function Get-HTMLReport
{
[CmdletBinding()]

Param
(
#HTML file path
[Parameter(Mandatory=$true)]
[string] $Path,

#HTML file name
[string] $FileName = "VBM365Audit.html"
)
Write-Host "Building HTML"

#region HTML
@"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>$HTMLTitle</title>
$HTMLCSS
</head>
<body>
<br><br><br><br>

<h1>VEEAM Backup for Microsoft 365 Report</h1>

$(CreateArray -title "Summary" -var $DCVBMSummary)
$(CreateArray -title "License" -var $DCVBMLicense)
$(CreateArray -title "SMTP" -var $DCVBMSMTP)
$(CreateArray -title "Notifications" -var $DCVBMNotification)
$(CreateArray -title "Internet Proxy" -var $DCVBMInternetProxy)

$(CreateArray -title "REST API" -var $DCVBMRestAPI)
$(CreateArray -title "Restore portal" -var $DCVBMRestorePortal)
$(CreateArray -title "Get-DCVBMOperatorAuthentication" -var $DCVBMOperatorAuthentication )

$(CreateArray -title "Repositories" -var $DCVBMRepository)
$(CreateArray -title "Object Repositories" -var $DCVBMObjectRepository)

$(CreateArray -title "Proxies" -var $DCVBMProxy)

$(CreateArray -title "Organizations" -var $DCVBMOrganization)
$(CreateArray -title "Backup jobs" -var $DCVBMBackupJob)
$(CreateArray -title "Backup copy jobs" -var $DCVBMBackupCopyJob)



$(CreateArray -title "Restore operators" -var $DCVBMRestoreOperator )

</body>
"@ | Out-File -Encoding utf8 $htmlReportPath

Invoke-Item $htmlReportPath


}
#endregion






#Write here all function that need to be displayed in all reports types

$DCVBMSummary = Get-DCVBMSummary
$DCVBMOrganization = Get-DCVBMOrganization
$DCVBMBackupJob = Get-DCVBMBackupJob
$DCVBMProxy = Get-DCVBMProxy
$DCVBMRepository = Get-DCVBMRepository
$DCVBMLicense = Get-DCVBMLicense
$DCVBMRestoreOperator = Get-DCVBMRestoreOperator
$DCVBMRestAPI = Get-DCVBMRestAPI
$DCVBMRestorePortal = Get-DCVBMRestorePortal
$DCVBMOperatorAuthentication = Get-DCVBMOperatorAuthentication
$DCVBMInternetProxy = Get-DCVBMInternetProxy
$DCVBMSMTP = Get-DCVBMSMTP
$DCVBMNotification = Get-DCVBMNotification
$DCVBMObjectRepository = Get-DCVBMObjectRepository
$DCVBMBackupCopyJob = Get-DCVBMBackupCopyJob



Get-HTMLReport -Path "$ReportPath\$domain"


Disconnect-VBOServer

Don’t hesitate about feedback :)

Hello Damien, 

really a nice script. I have only two suggestions for improvement:

1st) I would create the directory for the report path after defining the $ReportPath variable and use the $ReportPath variable to create the folder. Like this:

$date = (get-date -Format "dd_MM_yyyy_HH_mm")

#ReportPath
$ReportPath="C:\PATHNAME\VBM365Audit\$date"

#Create directory
New-Item -ItemType Directory -Path $ReportPath -Force | Out-Null

#Report file HTML path
$htmlReportPath = "$ReportPath\VeeamBackupMicrosoft365.html"


2nd) With my community edition, I received an error message when determining the expiration date of the license. So I would check the license type first. Like this:

if ($LicenseType -ne "Community") {
$LicenseExpiration = (Get-VBOLicense).expirationdate.ToShortDateString()
} else {
$LicenseExpiration = ""
}

Thank you and have a nice day

Jan

Userlevel 7
Badge +8

Hello Damien, 

really a nice script. I have only two suggestions for improvement:

1st) I would create the directory for the report path after defining the $ReportPath variable and use the $ReportPath variable to create the folder. Like this:

$date = (get-date -Format "dd_MM_yyyy_HH_mm")

#ReportPath
$ReportPath="C:\PATHNAME\VBM365Audit\$date"

#Create directory
New-Item -ItemType Directory -Path $ReportPath -Force | Out-Null

#Report file HTML path
$htmlReportPath = "$ReportPath\VeeamBackupMicrosoft365.html"


2nd) With my community edition, I received an error message when determining the expiration date of the license. So I would check the license type first. Like this:

if ($LicenseType -ne "Community") {
$LicenseExpiration = (Get-VBOLicense).expirationdate.ToShortDateString()
} else {
$LicenseExpiration = ""
}

Thank you and have a nice day

Jan

I like the report directory suggestions. Will keep things organized so you could run this from one location on multiple servers in other locations. 😁

Userlevel 4
Badge +1

Hello :)

I totally agree for the first.

For the second, the script is for production purpose and community edition is not.

 

Update :

 

# =======================================================
# NAME: VBM365audit.ps1
# AUTHOR: Commenge Damien, Axians Cloud Builder
# DATE: 11/07/2022
#
# VERSION 1.03
# COMMENTS: This script is created to Audit Veeam backup for microsoft 365
# <N/A> is used for not available
# 16/07/2022 : Update lot of code for better performance
# 18/07/2022 : Change date format and replace VBM to VB365
# 26/07/2022 : Optimize path creation
# =======================================================
#Requires -modules Veeam.Archiver.PowerShell

#Date to create folder
$Date = Get-Date -Format "yyyy-MM-dd HH_mm"
#ReportPath to create folder
$ReportPath="C:\temp\VBM365Audit\$Date"
#Create folder
New-Item -ItemType Directory -Path $ReportPath -Force | Out-Null
#Report file HTML path
$HTMLReportPath = "$ReportPath\VeeamBackupMicrosoft365.html"

#Web page title
$HTMLTitle = "VBM365 report"
#Web page CSS style
$HTMLCSS = @'
<style>
body{color:black;font-family:Vinci Sans Light;font-size:0.79em;line-height:1.25;margin:5;}
a{color:black;}
H1{color:white;font-family:Verdana;font-weight:bold;font-size:20pt;margin-bottom:50px;margin-top:40px;text-align:center;background-color:#005EB8;}
H2{color:#A20067;font-family:Verdana;font-size:16pt;margin-left:14px;text-align:left;}
H3{color:#005EB8;font-family:Verdana;font-size:13pt;margin-left:16px;}
H4{color:black;font-family:Verdana;font-size:11pt;margin-left:16px;}
table {border-collapse: collapse;margin-left:10px;border-radius:7px 7px 0px 0px;}
th, td {padding: 8px;text-align: left;border-bottom: 1px solid #ddd;}
th {background-color: #006400;color: white;}
td:first-child{font-weight:bold;}
tr:nth-child(even){background-color: #f2f2f2}
table.table2 td:first-child{background-color: #A20067;color: white}
</style>
'@

#Connect to VBO Server
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - Connecting to VBM 365 server"
try {
Connect-VBOServer -ErrorAction Stop
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - Connected to VBM 365 server"
}
catch [System.Management.Automation.RuntimeException]{
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - Connexion is already done"
}
catch {
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - $($_.Exception.message) " -ForegroundColor Red
return
}

<#
.SYNOPSIS
Get configuration Summary from Veeam Microsoft 365 server
.DESCRIPTION
Get server name, OS, OS build and VBM365 version
.EXAMPLE
Get-DCVB365Summary
#>
function Get-DCVB365Summary
{
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 Summary"

$ServerName = $env:COMPUTERNAME
$ServerOS = (Get-CimInstance Win32_OperatingSystem).Caption
$OSBuild = Get-ItemPropertyValue -path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion" -name 'UBR'
#No way to find version with powershell but associated version with VBR is available here : https://www.veeam.com/kb4106
$VBRbuild = (Get-Module Veeam.Archiver.PowerShell).nestedmodules.version.tostring()

$VB365Build = Switch ($VBRbuild)
{
"10.0.2.1061" {"5.0.0.1061"}
"10.0.2.1063" {"5.0.0.1063"}
"10.0.2.1070" {"5.0.0.1070"}
"10.0.3.179" {"5.0.1.179"}
"10.0.3.207" {"5.0.1.207"}
"10.0.3.225" {"5.0.1.225"}
"10.0.3.252" {"5.0.1.252"}
"10.0.4.22" {"5.0.2.22"}
"10.0.4.42" {"5.0.2.42"}
"10.0.5.1033" {"5.0.3.1033"}
"10.0.5.1035" {"5.0.3.1035"}
"10.0.5.1051" {"5.0.3.1051"}
"10.0.5.1060" {"5.0.3.1060"}
"10.0.5.1063" {"5.0.3.1063"}
"11.1.0.367" {"6.0.0.367"}
"11.1.0.379" {"6.0.0.379"}
"11.1.0.385" {"6.0.0.385"}
Default {"Unknown Value, script update is necessary"}
}

[PScustomObject]@{
Name = $ServerName
OS = $ServerOS
OSBuild = $OSBuild
VB365Version = $VB365Build
}
}

<#
.SYNOPSIS
Get configuration about organizations
.DESCRIPTION
Get organization name, account used, type (on premise, hybride, O365), service (exchange, sharepoint), region, authentication (basic, modern with legacy protocol, modern), auxiliar backup account/application number
.EXAMPLE
Get-DCVB365Organization
#>
function Get-DCVB365Organization
{
Write-host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 Organization"

$Organization = Get-VBOOrganization

if ($Organization.Office365ExchangeConnectionSettings)
{
$OrgAuth = (Get-VBOOrganization).Office365ExchangeConnectionSettings.AuthenticationType
}
else
{
$OrgAuth = (Get-VBOOrganization).Office365SharePointConnectionSettings.AuthenticationType
}
if ($OrgAuth -eq "Basic")
{
$AuxAccount = (Get-VBOOrganization).backupaccounts.count
}
else
{
$AuxAccount = (Get-VBOOrganization).backupapplications.count
}

[PScustomObject]@{
Name = $Organization.OfficeName
Account = $Organization.username
Type = $Organization.type
Service = $Organization.BackupParts
Region = $Organization.region
Authentication = $OrgAuth
AuxAccount = $AuxAccount
}
}

<#
.SYNOPSIS
Get configuration about backup job configuration
.DESCRIPTION
Get job name, type, included object, excluded object, repository, proxy, schedule, active or disabled state
.EXAMPLE
Get-DCVB365BackupJob
#>
function Get-DCVB365BackupJob
{
Write-host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 Backup Jobs"

foreach ($Job in Get-VBOJob)
{
#Get proxy name from associated proxy ID repository
$JobSchedule = "<N/A>"
if ($Job.schedulepolicy.EnableSchedule -and $Job.SchedulePolicy.Type -eq "daily")
{
$JobSchedule = [string]$Job.SchedulePolicy.DailyTime + " " + $Job.SchedulePolicy.DailyType
}
if ($Job.schedulepolicy.EnableSchedule -and $Job.SchedulePolicy.Type -eq "Periodically")
{
$JobSchedule = $Job.SchedulePolicy.PeriodicallyEvery
}

[PScustomObject]@{
Name = $Job.Name
Type = $Job.JobBackupType
InclObject = $Job.SelectedItems -join ", "
ExclObject = $Job.ExcludedItems -join ", "
Repository = $Job.Repository
Proxy = (Get-VBOProxy -id (Get-VBORepository -Name $Job.Repository).ProxyID).Hostname
Schedule = $JobSchedule
Enabled = $Job.IsEnabled
}
}
}

<#
.SYNOPSIS
Get configuration about backup copy job configuration
.DESCRIPTION
Get job name, repository, backupjob linked, schedule, active or disabled state
.EXAMPLE
Get-DCVB365BackupCopyJob
#>
function Get-DCVB365BackupCopyJob
{
Write-host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 Backup copy Jobs"

foreach ($CopyJob in Get-VBOCopyJob)
{
if ($CopyJob.SchedulePolicy.Type -eq "daily")
{
$JobSchedule = [string]$CopyJob.SchedulePolicy.DailyTime + " " + $CopyJob.SchedulePolicy.DailyType
}
if ($CopyJob.SchedulePolicy.Type -eq "Periodically")
{
$JobSchedule = $CopyJob.SchedulePolicy.PeriodicallyEvery
}
else
{
$JobSchedule = $CopyJob.SchedulePolicy.Type
}
[PScustomObject]@{
Name = $CopyJob.name
Repository = $CopyJob.Repository
BackupLinked = $CopyJob.BackupJob
Schedule = $JobSchedule
Enabled = $CopyJob.IsEnabled
}
}
}


<#
.SYNOPSIS
Get configuration about proxy configuration
.DESCRIPTION
Get proxy name, port, thread number, throttling, internet proxy used or not, internet proxy port and account
.EXAMPLE
Get-DCVB365Proxy
#>
function Get-DCVB365Proxy
{

Write-host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 Proxy"

foreach ($Proxy in Get-VBOProxy)
{
$Proxy = [PScustomObject]@{
Name = $Proxy.hostname
Port = $Proxy.port
Thread = $Proxy.ThreadsNumber
Throttling = [string]$Proxy.ThrottlingValue + " " + $Proxy.ThrottlingUnit
IntProxyHost = "<N/A>"
IntProxyPort = "<N/A>"
IntProxyAccount = "<N/A>"
}
if ($Proxy.InternetProxy.UseInternetProxy)
{
$Proxy.IntProxyHost = $Proxy.InternetProxy.UseInternetProxy.Host
$Proxy.IntProxyPort = $Proxy.InternetProxy.UseInternetProxy.Port
$Proxy.IntProxyAccount = $Proxy.InternetProxy.UseInternetProxy.User
}
}
$Proxy
}

<#
.SYNOPSIS
Get configuration about repository configuration
.DESCRIPTION
Get repository name, proxy associated, path, retention type and value, repository object name and encryption
.EXAMPLE
Get-DCVB365Repository
#>
function Get-DCVB365Repository
{

Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 Repository"

foreach ($Repository in Get-VBORepository)
{
$Proxy = (Get-VBOProxy -id (Get-VBORepository -name $Repository.Name).ProxyID).Hostname
$Retention = [string]$Repository.RetentionPeriod + " " + $Repository.RetentionType
$ObjectName = "<N/A>"
if ($Repository.ObjectStorageRepository)
{
$ObjectName = $Repository.ObjectStorageRepository.Name
}
[PScustomObject]@{
Name = $Repository.Name
Proxy = $Proxy
Path = $Repository.Path
ObjectRepository = $ObjectName
Retention = $Retention
Encryption = $Repository.EnableObjectStorageEncryption
}
}
}

<#
.SYNOPSIS
Get configuration about object repository configuration
.DESCRIPTION
Get repository name, folder, type, size limit and if it's long term achive
.EXAMPLE
Get-DCVB365ObjectRepository
#>
function Get-DCVB365ObjectRepository
{
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 Object Repository"

foreach ($ObjectStorage in Get-VBOObjectStorageRepository)
{
$SizeLimit = "<N/A>"
if ($ObjectStorage.EnableSizeLimit)
{
$SizeLimit = [String]$ObjectStorage.UsedSpace + "/" + $ObjectStorage.SizeLimit
}
[PScustomObject]@{
Name = $ObjectStorage.name
Folder = $ObjectStorage.Folder
Type = $ObjectStorage.Type
SizeLimit = $SizeLimit
LongTerm = $ObjectStorage.IsLongTerm
}
}
}

<#
.SYNOPSIS
Get configuration about license
.DESCRIPTION
Get license type, expiration date, customer, contact, usage
.EXAMPLE
Get-DCVB365License
#>
function Get-DCVB365License
{

Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 License"
$License = (Get-VBOLicense)
$Usage = [string]$License.usedNumber + "/" + (Get-VBOLicense).TotalNumber

[PScustomObject]@{
Type = $License.Type
Expiration = $License.ExpirationDate.ToShortDateString()
To = $License.LicensedTo
Contact = $License.ContactPerson
Number = $Usage
}
}

<#
.SYNOPSIS
Get configuration about restore operator configuration
.DESCRIPTION
Get role name, organization, operator, associated object, excluded object
.EXAMPLE
Get-DCVB365RestoreOperator
#>
function Get-DCVB365RestoreOperator
{
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 Restore Operator"

foreach ($Role in Get-VBORbacRole)
{
$IncludedObject = "Organization"
$ExcludedObject = "<N/A>"
if ($Role.RoleType -ne "EntireOrganization")
{
$IncludedObject = $Role.SelectedItems.DisplayName -join ","
}
if ($Role.ExcludedItems)
{
$ExcludedObject = $Role.ExcludedItems.DisplayName -join ","
}
[PScustomObject]@{
Role = $Role.Name
Organization = (Get-VBOOrganization -Id ($Role.OrganizationId)).Name
Operator = $Role.Operators.DisplayName -join ", "
IncludedObject = $IncludedObject
ExcludedObject = $ExcludedObject
}
}
}

<#
.SYNOPSIS
Get configuration about RestAPI configuration
.DESCRIPTION
Get state, token life time, port, certificate thumbprint friendly name and expiration date
.EXAMPLE
Get-DCVB365RestAPI
#>
function Get-DCVB365RestAPI
{
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 REST API"

$RestAPI = Get-VBORestAPISettings

[PScustomObject]@{
Enabled = $RestAPI.IsServiceEnabled
CertThumbprint = $RestAPI.CertificateThumbprint
CertFriendlyName = $RestAPI.CertificateFriendlyName
CertExpiration = $RestAPI.CertificateExpirationDate.ToShortDateString()
TokenTime = $RestAPI.AuthTokenLifeTime
Port = $RestAPI.HTTPSPort
}
}


<#
.SYNOPSIS
Get configuration about Restore portal configuration
.DESCRIPTION
Get state, application ID, certificate thumbprint friendly name and expiration date
.EXAMPLE
Get-DCVB365RestorePortal
#>
function Get-DCVB365RestorePortal
{

Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 Restore portal"

$RestorePortal = Get-VBORestorePortalSettings

[PScustomObject]@{
Enabled = $RestorePortal.IsServiceEnabled
CertThumbprint = $RestorePortal.CertificateThumbprint
CertFriendlyName = $RestorePortal.CertificateFriendlyName
CertExpiration = $RestorePortal.CertificateExpirationDate.ToShortDateString()
ApplicationID = $RestorePortal.ApplicationId.Guid
}
}

<#
.SYNOPSIS
Get configuration about operator Authentication portal configuration
.DESCRIPTION
Get state, certificate thumbprint friendly name and expiration date
.EXAMPLE
Get-DCVB365OperatorAuthentication
#>
function Get-DCVB365OperatorAuthentication
{
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 Authentication"

$OperatorAuthentication = Get-VBOOperatorAuthenticationSettings

[PScustomObject]@{
Enabled = $OperatorAuthentication.AuthenticationEnabled
CertThumbprint = "NotAvailable.WaitingNextPatch"
CertFriendlyName = $OperatorAuthentication.CertificateFriendlyName
CertExpiration = $OperatorAuthentication.CertificateExpirationDate
}
}

<#
.SYNOPSIS
Get configuration about internet proxy
.DESCRIPTION
Get state, host, port and account
.EXAMPLE
Get-DCVB365InternetProxy
#>
function Get-DCVB365InternetProxy
{
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 Internet Proxy"
$InternetProxySetting = Get-VBOInternetProxySettings

$InternetProxy = [PScustomObject]@{
Enabled = $InternetProxySetting.UseInternetProxy
Host = "<N/A>"
Port = "<N/A>"
Account = "<N/A>"
}
if ($InternetProxySetting.UseInternetProxy)
{
$InternetProxy.Host = $InternetProxySetting.Host
$InternetProxy.Port = $InternetProxySetting.Port
$InternetProxy.Account = $InternetProxySetting.User
}
$InternetProxy
}

<#
.SYNOPSIS
Get configuration about SMTP
.DESCRIPTION
Get state, server, port, ssl, account
.EXAMPLE
Get-DCVB365SMTP
#>
function Get-DCVB365SMTP
{
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 SMTP configuration"

$SMTPSetting = Get-VBOEmailSettings

$SMTP = [PScustomObject]@{
Enabled = $SMTPSetting.EnableNotification
Server = "<N/A>"
Port = "<N/A>"
SSL = "<N/A>"
Account = "<N/A>"
}
if ($SMTPSetting.EnableNotification)
{
$SMTP.Server = $SMTPSetting.SMTPServer
$SMTP.Port = $SMTPSetting.Port
$SMTP.SSL = $SMTPSetting.UseSSL
if ($SMTPSetting.UseAuthentication)
{
$SMTP.Account = $SMTPSetting.Username
}
}
$SMTP
}


<#
.SYNOPSIS
Get configuration about Notifications
.DESCRIPTION
Get state, sender, receiver, notification on success, warning and failure, send only last retry notification
.EXAMPLE
Get-DCVB365Notification
#>
function Get-DCVB365Notification
{

Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 Notifications"
$NotificationSetting = (Get-VBOEmailSettings)

$Notification = [PScustomObject]@{
Enabled = $NotificationSetting.EnableNotification
Sender = "<N/A>"
Receiver = "<N/A>"
OnSuccess = "<N/A>"
OnWarning = "<N/A>"
OnFailure = "<N/A>"
OnlyLastRetry = "<N/A>"
}
if ($NotificationSetting.EnableNotification)
{
$Notification.Sender = $NotificationSetting.From -join ", "
$Notification.Receiver = $NotificationSetting.To -join ", "
$Notification.OnSuccess = $NotificationSetting.NotifyOnSuccess
$Notification.OnWarning = $NotificationSetting.NotifyOnWarning
$Notification.OnFailure = $NotificationSetting.NotifyOnFailure
$Notification.OnlyLastRetry = $NotificationSetting.SupressUntilLastRetry
}
$Notification
}


<#
.SYNOPSIS
Create array for HTML report
.DESCRIPTION
Create array with title and precontent
.EXAMPLE
CreateArray -title "my Title" -var $MyData -PreContent $MyPrecontent
#>

Function CreateArray
{
param (
$Title,
$Var,
$PreContent
)

if ($Title)
{
"<h3>$Title</h3>"
}
if ($PreContent)
{
$Var | ConvertTo-Html -Fragment -PreContent $PreContent
}
else
{
$Var | ConvertTo-Html -Fragment
}
}


<#
.SYNOPSIS
Generate HTML report
.DESCRIPTION
Use all variable to build html report with CSS style
.EXAMPLE
Get-HTMLReport -Path "C:\temp\report.html"
#>

Function Get-HTMLReport
{
[CmdletBinding()]

Param
(
#HTML file path
[Parameter(Mandatory)]
[System.IO.FileInfo]$Path,

#HTML file name
[string] $FileName = "VBM365Audit.html"
)
Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm") - VBM365 Building HTML"
#region HTML
# chrisdent: STYLE: In the code below `CreateArray` is something of a misleading function name.
# chrisdent: ENHANCEMENT: Perhaps consider using a string builder.
@"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>$HTMLTitle</title>
$HTMLCSS
</head>
<body>
<br><br><br><br>

<h1>VEEAM Backup for Microsoft 365 Report</h1>

$(CreateArray -title "Summary" -var $DCVB365Summary)
$(CreateArray -title "License" -var $DCVB365License)
$(CreateArray -title "SMTP" -var $DCVB365SMTP)
$(CreateArray -title "Notifications" -var $DCVB365Notification)
$(CreateArray -title "Internet Proxy" -var $DCVB365InternetProxy)

$(CreateArray -title "REST API" -var $DCVB365RestAPI)
$(CreateArray -title "Restore portal" -var $DCVB365RestorePortal)
$(CreateArray -title "Operator Authentication" -var $DCVB365OperatorAuthentication )

$(CreateArray -title "Repositories" -var $DCVB365Repository)
$(CreateArray -title "Object Repositories" -var $DCVB365ObjectRepository)

$(CreateArray -title "Proxies" -var $DCVB365Proxy)

$(CreateArray -title "Organizations" -var $DCVB365Organization)
$(CreateArray -title "Backup jobs" -var $DCVB365BackupJob)
$(CreateArray -title "Backup copy jobs" -var $DCVB365BackupCopyJob)



$(CreateArray -title "Restore operators" -var $DCVB365RestoreOperator )

</body>
"@ | Out-File -Encoding utf8 $HTMLReportPath

Invoke-Item $HTMLReportPath


}
#endregion

#Write here all function that need to be displayed in all reports types

$DCVB365Summary = Get-DCVB365Summary
$DCVB365Organization = Get-DCVB365Organization
$DCVB365BackupJob = Get-DCVB365BackupJob
$DCVB365Proxy = Get-DCVB365Proxy
$DCVB365Repository = Get-DCVB365Repository
$DCVB365License = Get-DCVB365License
$DCVB365RestoreOperator = Get-DCVB365RestoreOperator
$DCVB365RestAPI = Get-DCVB365RestAPI
$DCVB365RestorePortal = Get-DCVB365RestorePortal
$DCVB365OperatorAuthentication = Get-DCVB365OperatorAuthentication
$DCVB365InternetProxy = Get-DCVB365InternetProxy
$DCVB365SMTP = Get-DCVB365SMTP
$DCVB365Notification = Get-DCVB365Notification
$DCVB365ObjectRepository = Get-DCVB365ObjectRepository
$DCVB365BackupCopyJob = Get-DCVB365BackupCopyJob


#Create HTML Report
Get-HTMLReport -Path "$ReportPath\$domain"


Disconnect-VBOServer


 

Hi Damien,

Very nice job and script.

In my case I have only “,” for job with Group Selection Base.

So I modify your script to retreive AD Group.

 

if ($Job.SelectedItems.Type -eq "SecurityGroup")
{
$InclObject = $Job.SelectedItems.Group.DisplayName -join ", "
}
else
{
$InclObject = $Job.SelectedItems -join ", "
}
if ($Job.ExcludedItems.Type -eq "SecurityGroup")
{
$ExclObject = $Job.ExcludedItems.Group.DisplayName -join ", "
}
else
{
$ExclObject = $Job.ExcludedItems -join ", "
}
[PScustomObject]@{
Name = $Job.Name
Type = $Job.JobBackupType
InclObject = $InclObject
ExclObject = $ExclObject
Repository = $Job.Repository
Proxy = (Get-VBOProxy -id (Get-VBORepository -Name $Job.Repository).ProxyID).Hostname
Schedule = $JobSchedule
Enabled = $Job.IsEnabled
}

 

 

Regards

Userlevel 4
Badge +1

Thanks for the feedback and fix :)

Comment