Maintain control of your Microsoft 365 data
matteu
Veeam Legend
Posts: 702
Liked: 113 times
Joined: May 11, 2018 8:42 am
Contact:

Veeam 365 v6 audit script

Post by matteu » 11 people like this post

Hello,

This is the script I just created to audit Veeam Backup for microsoft 365.
It's not complete but feel free to ask me what you need :)

Code: Select all

# =======================================================
# 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
Mike Resseler
Product Manager
Posts: 8044
Liked: 1263 times
Joined: Feb 08, 2013 3:08 pm
Full Name: Mike Resseler
Location: Belgium
Contact:

Re: VBM365 powershell script Audit

Post by Mike Resseler »

Thanks for putting it here! A lot of people will like this.
matteu
Veeam Legend
Posts: 702
Liked: 113 times
Joined: May 11, 2018 8:42 am
Contact:

Re: VBM365 powershell script Audit

Post by matteu »

Thanks, I'm waiting the answer for the repository hostname and I will put new version with backup copy + storage object part.
jorgedlcruz
Veeam Software
Posts: 1355
Liked: 613 times
Joined: Jul 17, 2015 6:54 pm
Full Name: Jorge de la Cruz
Contact:

Re: VBM365 powershell script Audit

Post by jorgedlcruz »

Hello Matteu,
Great job, some quick feedback after running it, it was so fast and smooth, awesome job.

The header is pointing to a UK Internet Services Company or something?
Image

This is the build version in logs, which is okay for more internal consumption I guess (thanks Polina for pointing out), it might be the compatible version with VBR, or the Explorers version, but not the VB365 server. I usually get this from the new API endpoint /v6/ServiceInstance
Image

The Repositories section is great, but it could be better, as my Repository is Object Storage:
Image

So there is a big part that is not shown here, the Object Repo part, and the settings of that object repo:
Image

Hope feedback is useful! Keep them coming!
Jorge de la Cruz
Senior Product Manager | Veeam ONE @ Veeam Software

@jorgedlcruz
https://www.jorgedelacruz.es / https://jorgedelacruz.uk
vExpert 2014-2024 / InfluxAce / Grafana Champion
matteu
Veeam Legend
Posts: 702
Liked: 113 times
Joined: May 11, 2018 8:42 am
Contact:

Re: VBM365 powershell script Audit

Post by matteu »

Hello
Ecellent feedback :)
It s exactly what I hope people do :)

Right for title it s my company ^^. I will fix it tomorow

Oh yes version should be 6 something I didnt notice it. I will try to fix it. I know how to use simple PS command but I don t know how to manage REST api correctly and need to find some documentation to learn it.

I added object storage on the script 2hours after I make this topic for s3 compatible and azure. I don t know if it will work with Amazon I can t test it because I don t have tenan.
I will post new version with the others fix tomorow :)
matteu
Veeam Legend
Posts: 702
Liked: 113 times
Joined: May 11, 2018 8:42 am
Contact:

Re: VBM365 powershell script Audit

Post by matteu »

OK, so I update the code for the title but I'm waiting an answer on an other topic I create here to find the correct version 6.0.0.385 without rest api because I would like my script to work without any prerequesite.
jorgedlcruz
Veeam Software
Posts: 1355
Liked: 613 times
Joined: Jul 17, 2015 6:54 pm
Full Name: Jorge de la Cruz
Contact:

Re: VBM365 powershell script Audit

Post by jorgedlcruz »

Awesome work, Matteu,
I have amended my response as well with a link to a KB where you can see the VB365 version, and the logs version, worse case you can always add a case where version is 11.xx whatever then is VB365 v6.x whatever, but wait for Polina, I am sure she can point you to a better direction than that.

Great work, mate
Jorge de la Cruz
Senior Product Manager | Veeam ONE @ Veeam Software

@jorgedlcruz
https://www.jorgedelacruz.es / https://jorgedelacruz.uk
vExpert 2014-2024 / InfluxAce / Grafana Champion
matteu
Veeam Legend
Posts: 702
Liked: 113 times
Joined: May 11, 2018 8:42 am
Contact:

Re: VBM365 powershell script Audit

Post by matteu »

Feel free with more feedback :)
Then, I'm not english and I probably do some "error" don't hesitate to tell me :)
New version with all fixes and object part (and different green color) :

Code: Select all

# =======================================================
# 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
jorgedlcruz
Veeam Software
Posts: 1355
Liked: 613 times
Joined: Jul 17, 2015 6:54 pm
Full Name: Jorge de la Cruz
Contact:

Re: VBM365 powershell script Audit

Post by jorgedlcruz »

Amazing work, Damien,
I just ran it, and I can not see my schedule, it appears N/A:
Image

Other than that, looking awesome, cheers.
Jorge de la Cruz
Senior Product Manager | Veeam ONE @ Veeam Software

@jorgedlcruz
https://www.jorgedelacruz.es / https://jorgedelacruz.uk
vExpert 2014-2024 / InfluxAce / Grafana Champion
matteu
Veeam Legend
Posts: 702
Liked: 113 times
Joined: May 11, 2018 8:42 am
Contact:

Re: VBM365 powershell script Audit

Post by matteu » 4 people like this post

Thanks again :)

I did it on backup copy but forget to use it on backup job.

Fixed :

Code: Select all

# =======================================================
# 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"

$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 = [string]$obj.SchedulePolicy.DailyTime + " " + $obj.SchedulePolicy.DailyType
        }
        if ($obj.schedulepolicy.EnableSchedule -and $obj.schedulepolicy.type -eq "Periodically")
        {
             $JobSchedule = $obj.SchedulePolicy.PeriodicallyEvery
        }
        $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
Mahong
Veeam Software
Posts: 22
Liked: 1 time
Joined: Dec 19, 2017 8:01 am
Full Name: Ma Hong
Contact:

Re: VBM365 powershell script Audit

Post by Mahong »

@matteu awesome tools,I love it. Would you pls to post a sample audit report, so I can follow the script quickly. Thanks!
tsmith_co
VeeaMVP
Posts: 132
Liked: 41 times
Joined: Dec 12, 2013 1:23 pm
Full Name: Tim Smith
Location: Ohio
Contact:

Re: VBM365 powershell script Audit

Post by tsmith_co »

@matteu This looks great! I'll try it out in my lab this week! My only pre-testing feedback is that if you wanted to standardize of the acronym, we use the VB365, since VBM is the metadata file extension for VBR backup chains.
Tim Smith
https://tsmith.co
@tsmith_co
dwrandolph
Novice
Posts: 7
Liked: 2 times
Joined: Dec 12, 2012 12:19 am
Full Name: Donald Randolph
Contact:

Re: VBM365 powershell script Audit

Post by dwrandolph »

A small pet peeve. Use ISO 8601 "yyyy-MM-dd HH:mm" for date formats. Avoids confusion with different regions dd-mm-yyyy or mm-dd-yyyy. Is easier to sort. And can be fed back into PowerShell for later calculations.
matteu
Veeam Legend
Posts: 702
Liked: 113 times
Joined: May 11, 2018 8:42 am
Contact:

Re: VBM365 powershell script Audit

Post by matteu »

Thanks for all these feedback, I update the last version of the script soon and I post it again :)
I did some code improvement because of powershell comunity advices !
matteu
Veeam Legend
Posts: 702
Liked: 113 times
Joined: May 11, 2018 8:42 am
Contact:

Re: VBM365 powershell script Audit

Post by matteu » 3 people like this post

Sample here :

Image

Image

Image

New code version with what you asked :) (I hope)

Code: Select all

# =======================================================
# NAME: VBM365audit.ps1
# AUTHOR: Commenge Damien, Axians Cloud Builder
# DATE: 11/07/2022
#
# VERSION 1.02
# 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
# =======================================================
#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-DCVBMSummary
 #>
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-DCVBMOrganization
 #>
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-DCVBMBackupJob
 #>
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-DCVBMBackupCopyJob
 #>
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-DCVBMProxy
 #>
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-DCVBMRepository
 #>
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-DCVBMRepository
 #>
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-DCVBMLicense
 #>
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-DCVBMRestoreOperator
 #>
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-DCVBMRestAPI
 #>
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-DCVBMRestorePortal
 #>
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-DCVBMOperatorAuthentication
 #>
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-DCVBMInternetProxy
 #>
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-DCVBMSMTP
 #>
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-DCVBMNotification
 #>
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
    @"
    <!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 "Get-DCVBMOperatorAuthentication" -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

BaptisteT
VeeaMVP
Posts: 528
Liked: 87 times
Joined: Mar 15, 2019 4:21 pm
Full Name: Baptiste Tellier
Contact:

Re: VBM365 powershell script Audit

Post by BaptisteT »

hi @matteu,

Perhaps once you have enough feedback, considerer uploading your nice report on https://github.com/VeeamHub/powershell

Great job !
maximeg
Service Provider
Posts: 8
Liked: 4 times
Joined: Jul 15, 2019 3:30 pm
Full Name: Maxime Guiraud
Contact:

Re: VBM365 powershell script Audit

Post by maximeg »

Hi Matteu and BaptisteT
Thanks for this script !
I ve just try it in our lab, maybe you can modify 2 points:

-- VBM365 License
It s not an important improvement, because this script is for production purpose, but in our lab we have a Community edition license, this version doesn't have an expiration date so the function Get-DCVB365License generate an error message.
2022-07-20 14:54 - VBM365 License
You cannot call a method on a null-valued expression.

PS C:\> Get-VBOLicense
Status : Valid
ExpirationDate :
Type : Community
LicensedTo :
ContactPerson :
TotalNumber : 10 (3 used)
SupportExpirationDate :

-- VBM365 REST API
Restore Portal is installed on a dedicated machine which act also as a proxy, so the Veeam.Archiver.RESTful.Service is running on this machine instead of the VBO server

PS C:\>Get-VBORestAPISettings
Get-VBORestAPISettings : Service not found: Veeam.Archiver.RESTful.Service

I hope we meet each other soon ;)
matteu
Veeam Legend
Posts: 702
Liked: 113 times
Joined: May 11, 2018 8:42 am
Contact:

Re: VBM365 powershell script Audit

Post by matteu »

Hello,
Thank you for your feedback.

For Veeam community you can have some error message yes because I don't use it and you're right, it's for production purpose ^^

For the remote Rest API, unfortunately, I don't find any documentation about how to get it with powershell.
chris.childerhose
Veeam Vanguard
Posts: 572
Liked: 132 times
Joined: Aug 13, 2014 6:03 pm
Full Name: Chris Childerhose
Location: Toronto, ON
Contact:

Re: VBM365 powershell script Audit

Post by chris.childerhose »

I like this script for auditing. I will provide feedback once I run it some more.
-----------------------
Chris Childerhose
Veeam Vanguard / Veeam Legend / Veeam Ceritified Architect / VMCE
vExpert / VCAP-DCA / VCP8 / MCITP
Personal blog: https://just-virtualization.tech
Twitter: @cchilderhose
matteu
Veeam Legend
Posts: 702
Liked: 113 times
Joined: May 11, 2018 8:42 am
Contact:

Re: VBM365 powershell script Audit

Post by matteu » 1 person likes this post

Perfect, thanks :)
I sent mail to know how to put it on the github veeam repo community I m waiting an answer :)
RubinCompServ
Service Provider
Posts: 259
Liked: 65 times
Joined: Mar 16, 2015 4:00 pm
Full Name: David Rubin
Contact:

Re: VBM365 powershell script Audit

Post by RubinCompServ »

This looks great, but I have a couple of questions:

1. Was this written for v6? Because I'm running v5 and the script is throwing "Cmdlet not recognized" errors for 'Get-VBORbacRole', 'Get-VBORestorePortalSettings', & 'Get-VBOOperatorAuthenticationSettings'.
2. Is there a way to include the space that is consumed in each repository?
3. Also, it would be nice if the Repos and Jobs were sorted by name, to make it easier to actually audit against a customer list.

I think that what you have put together is amazing, just from a collection standpoint, and that you made it into a nice-looking HTML document is just icing on the cake!
dwrandolph
Novice
Posts: 7
Liked: 2 times
Joined: Dec 12, 2012 12:19 am
Full Name: Donald Randolph
Contact:

Re: VBM365 powershell script Audit

Post by dwrandolph » 1 person likes this post

Minor suggestion, ISO 801 for date formats. Dates as "yyyy-MM-dd" will sort, and will remove ambiguity when working across regions where some folk use "day month" vs "month day".
matteu
Veeam Legend
Posts: 702
Liked: 113 times
Joined: May 11, 2018 8:42 am
Contact:

Re: VBM365 powershell script Audit

Post by matteu » 1 person likes this post

Hello everyone, sorry for late answer :)
Don't hesitate if you have some feedback !

@RubinCompserv :
1. Yes, the script is for version 6 minimum.
2. Added on the new version
3. Added on the new version

@dwrandolph : Added on the new version

I also add the new Veeam microsoft 365 build.

Code: Select all

# =======================================================
# NAME: VBM365audit.ps1
# AUTHOR: Commenge Damien, Axians Cloud Builder
# DATE: 11/07/2022
#
# VERSION 1.04
# 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
# 12/12/2022 : Sort some results, use yyyy/MM/dd display, display repository size, add new build versions
# =======================================================
#Requires -Modules @{ ModuleName="Veeam.Archiver.PowerShell"; ModuleVersion="6.0" }
#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 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"}
        "11.1.0.400"    {"6.0.0.400"}
        "11.2.0.222"    {"6.1.0.222"}
        "11.2.0.254"    {"6.1.0.254"}
        "11.2.0.423"    {"6.1.0.423"}
        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
         }
         [int]$UsedStorage = ($Repository.Capacity - $Repository.FreeSpace) / 1GB
         [int]$TotalStorage = $Repository.Capacity / 1GB
         [PScustomObject]@{
             Name                = $Repository.Name
             Proxy               = $Proxy
             Path                = $Repository.Path
             ObjectRepository    = $ObjectName
             Retention           = $Retention
             Encryption          = $Repository.EnableObjectStorageEncryption
             'Storage(GB)'             = [String]$UsedStorage + "/" +  $TotalStorage
         }
     }
 }
 

 <#
 .SYNOPSIS
    Get configuration about object repository configuration
 .DESCRIPTION
    Get repository name, folder, type, UsedSpace, 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
             'UsedSpace(GB)'   = [int]($ObjectStorage.UsedSpace / 1GB)
             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.ToUniversalTime().ToString("yyyy/MM/dd")
        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.ToUniversalTime().ToString("yyyy/MM/dd")
        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.ToUniversalTime().ToString("yyyy/MM/dd")
        AzureApplicationID  = $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      = "InFutureVersion"
        CertFriendlyName    = $OperatorAuthentication.CertificateFriendlyName
        CertExpiration      = $OperatorAuthentication.CertificateExpirationDate.ToUniversalTime().ToString("yyyy/MM/dd")
    }
}

 <#
 .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>"
        Success     = "<N/A>"
        Warning     = "<N/A>"
        Failure     = "<N/A>"
        LastRetry   = "<N/A>"
    }
    if ($NotificationSetting.EnableNotification)
    {
        $Notification.Sender     = $NotificationSetting.From -join ", "
        $Notification.Receiver   = $NotificationSetting.To -join ", "
        $Notification.Success    = $NotificationSetting.NotifyOnSuccess
        $Notification.Warning    = $NotificationSetting.NotifyOnWarning
        $Notification.Failure    = $NotificationSetting.NotifyOnFailure
        $Notification.LastRetry  = $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 "Notification" -var $DCVB365Notification)
    $(CreateArray -title "Proxy web" -var $DCVB365InternetProxy)
    
    $(CreateArray -title "REST API" -var $DCVB365RestAPI)

    $(CreateArray -title "Restore portal" -var $DCVB365RestorePortal)

    $(CreateArray -title "Operator Authentication" -var $DCVB365OperatorAuthentication )

    $(CreateArray -title "Repository" -var $DCVB365Repository)
    $(CreateArray -title "Object Repository" -var $DCVB365ObjectRepository)
        
    $(CreateArray -title "Proxy" -var $DCVB365Proxy)

    $(CreateArray -title "Organisation" -var $DCVB365Organization)

    $(CreateArray -title "Backup job" -var $DCVB365BackupJob)

    $(CreateArray -title "Backup copy job" -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 | Sort-object Name
$DCVB365BackupJob                 = Get-DCVB365BackupJob | Sort-object Name
$DCVB365Proxy                     = Get-DCVB365Proxy | Sort-object Name
$DCVB365Repository                = Get-DCVB365Repository | Sort-object Name
$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 | Sort-object Name
$DCVB365BackupCopyJob             = Get-DCVB365BackupCopyJob | Sort-object Name 

#Create HTML Report
Get-HTMLReport -Path $HTMLReportPath

Disconnect-VBOServer
mhavens
Novice
Posts: 3
Liked: 1 time
Joined: Mar 16, 2015 5:42 pm
Full Name: Mark Havens
Contact:

Re: VBM365 powershell script Audit

Post by mhavens »

Hi -

On lines that say: $Proxy = (Get-VBOProxy -id (Get-VBORepository -name $Repository.Name).ProxyID).Hostname
it displays a warning:
WARNING: The ‘ProxyId’ property is obsolete. Use the ‘Proxy’ property instead.
WARNING: The default value of the ExtendedView switch option will be changed to 'False' in the next version.

I tried changing it to just 'Proxy', but that threw errors...

Mark
Polina
Veeam Software
Posts: 2939
Liked: 681 times
Joined: Oct 21, 2011 11:22 am
Full Name: Polina Vasileva
Contact:

Re: VBM365 powershell script Audit

Post by Polina »

Hi Mark,

Try using $Proxy = $Repository.Proxy instead of the $Proxy = (Get-VBOProxy -id (Get-VBORepository -name $Repository.Name).ProxyID).Hostname
matteu
Veeam Legend
Posts: 702
Liked: 113 times
Joined: May 11, 2018 8:42 am
Contact:

Re: VBM365 powershell script Audit

Post by matteu »

Hello,

Something working is :

(Get-VBOProxy -id (Get-VBORepository -name $Repository.Name).Proxy.ID).Hostname

But if we can have the same with just $repository.proxy it's easier :)

I'll check it soon because I'm updating the script to be working with V7 without any warning. If some of you need some more feature, just ask and I will try to integrate them :)
matteu
Veeam Legend
Posts: 702
Liked: 113 times
Joined: May 11, 2018 8:42 am
Contact:

Re: VBM365 powershell script Audit

Post by matteu »

Hello,
I'm updating this script for V7 and I would like to put it into Veeam repository but I totally ignore what I have to do.

I don't know enough github for it. Can someone explains me how to perform this task pls ?

It will be easier for everyone to get it from there and to open case to add features or solve issues.
chris.childerhose
Veeam Vanguard
Posts: 572
Liked: 132 times
Joined: Aug 13, 2014 6:03 pm
Full Name: Chris Childerhose
Location: Toronto, ON
Contact:

Re: VBM365 powershell script Audit

Post by chris.childerhose »

Yes, on Github will be great. Watching for a link. :)
-----------------------
Chris Childerhose
Veeam Vanguard / Veeam Legend / Veeam Ceritified Architect / VMCE
vExpert / VCAP-DCA / VCP8 / MCITP
Personal blog: https://just-virtualization.tech
Twitter: @cchilderhose
flomp
Enthusiast
Posts: 31
Liked: 2 times
Joined: Oct 24, 2018 6:15 pm
Contact:

Re: VBM365 powershell script Audit

Post by flomp »

matteu wrote: Aug 09, 2023 3:11 pmCan someone explains me how to perform this task pls ?
Basically you have to do the following:
- Install git
- setup your name and email
- `git config --global user.name "John Doe"`
- `git config --global user.email johndoe@example.com`
- Create a github account
- open https://github.com/VeeamHub/powershell
- click on the "fork" button (you must be signed in)
- Clone the repository: `git clone https://github.com/YOURUSERNAME/powershell.git`
- Read https://github.com/VeeamHub/powershell/ ... IBUTING.md
- Place your files where you want them to be
- use `git add FILENAME` to add it to the "index" (you can also use `git add .`)
- use `git commit` to commit your changes
- use `git push` to push them to your repo
- file a merge request on github

This is super short. I hope I did not miss anything important. I can only encourage you to learn git. It is not as simple as subversion, but it's not rocket science either and it is far superior. The official book on https://git-scm.com/book/en/v2 is very good. It is enough to work through the first 3 chapters. I guess that will take about 4 hours or so.
matteu
Veeam Legend
Posts: 702
Liked: 113 times
Joined: May 11, 2018 8:42 am
Contact:

Re: VBM365 powershell script Audit

Post by matteu »

Thanks for your help.
It takes me arround more than 1 hour to do all this task with visual studio code. I already have git account but I just use it to sync my job into my personnal private account.
I think I do what is needed. I have a pull request waiting for approval to merge.
If it's approved, I will put the link here :)
Post Reply

Who is online

Users browsing this forum: No registered users and 19 guests