Src/Private/Get-AbrVb365JobRepositoryName.ps1

function Get-AbrVb365PowerShellProcessPath {
    [CmdletBinding()]
    param (
    )

    if ($env:ABR_VB365_REPOSITORY_MAP_POWERSHELL_PATH -and (Test-Path -LiteralPath $env:ABR_VB365_REPOSITORY_MAP_POWERSHELL_PATH)) {
        return $env:ABR_VB365_REPOSITORY_MAP_POWERSHELL_PATH
    }

    try {
        $CurrentProcessPath = [System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName
        if ($CurrentProcessPath -and (Test-Path -LiteralPath $CurrentProcessPath)) {
            return $CurrentProcessPath
        }
    } catch { Out-Null }

    foreach ($CommandName in @('pwsh', 'powershell.exe', 'powershell')) {
        $Command = Get-Command -Name $CommandName -ErrorAction SilentlyContinue | Select-Object -First 1
        if ($Command -and $Command.Source) {
            return $Command.Source
        }
    }
}

function Set-AbrVb365JobRepositoryMetadata {
    [CmdletBinding()]
    [OutputType([string], [pscustomobject])]
    param (
        [Parameter(Mandatory = $true)]
        [object] $Job,

        [Parameter(Mandatory = $false)]
        [hashtable] $RepositoryLookup,

        [Parameter(Mandatory = $false)]
        [switch] $AllowLiveLookup
    )

    if ($null -eq $Job) {
        return '--'
    }

    $JobName = Get-AbrVb365PropertyValue -InputObject $Job -Name 'Name' -Default 'Unknown'
    $JobId = Get-AbrVb365PropertyValue -InputObject $Job -Name 'Id'
    $JobIdKey = ConvertTo-AbrVb365LookupKey -Id $JobId
    $RepositoryName = $null

    if ($RepositoryLookup) {
        if ($JobIdKey -and $RepositoryLookup.ContainsKey("id:$JobIdKey")) {
            $RepositoryName = $RepositoryLookup["id:$JobIdKey"]
        } elseif ($JobName -and $RepositoryLookup.ContainsKey("name:$JobName")) {
            $RepositoryName = $RepositoryLookup["name:$JobName"]
        }
    }

    if (-not $RepositoryName -and $AllowLiveLookup) {
        $Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
        Write-PScriboMessage -Message "Starting backup job '$JobName' Repository live value."
        try {
            $Repository = $Job.Repository
            $RepositoryName = if ($Repository) { $Repository.ToString() } else { '--' }
        } catch {
            Write-PScriboMessage -IsWarning -Message "Backup Job Repository live lookup '$JobName': $($_.Exception.Message)"
        } finally {
            $Stopwatch.Stop()
            Write-PScriboMessage -Message ("Completed backup job '{0}' Repository live value in {1:n2}s." -f $JobName, $Stopwatch.Elapsed.TotalSeconds)
        }
    }

    if (-not $RepositoryName) {
        $RepositoryName = '--'
    }

    $RepositorySummary = [pscustomobject]@{
        Name = $RepositoryName
        Path = '--'
        Capacity = $null
        FreeSpace = $null
        RetentionType = '--'
        RetentionPeriod = '--'
        IsSummaryOnly = $true
    }

    $Job | Add-Member -NotePropertyName 'AbrRepositoryName' -NotePropertyValue $RepositoryName -Force
    $Job | Add-Member -NotePropertyName 'AbrRepositorySummary' -NotePropertyValue $RepositorySummary -Force

    return $RepositoryName
}

function Get-AbrVb365ExternalJobRepositoryMap {
    [CmdletBinding()]
    [OutputType([hashtable])]
    param (
    )

    if ($script:BackupJobRepositoryLookup) {
        return $script:BackupJobRepositoryLookup
    }

    $Lookup = @{}
    $ConfiguredMapPath = $env:ABR_VB365_JOB_REPOSITORY_MAP_PATH
    if ($ConfiguredMapPath -and (Test-Path -LiteralPath $ConfiguredMapPath)) {
        try {
            Write-PScriboMessage -Message "Loading Veeam VB365 Backup Job repository map from '$ConfiguredMapPath'."
            $Rows = Get-Content -LiteralPath $ConfiguredMapPath -Raw | ConvertFrom-Json
            foreach ($Row in ($Rows | Where-Object { $_ })) {
                if ($Row.Id) {
                    $Lookup["id:$($Row.Id)"] = $Row.Repository
                }
                if ($Row.Job) {
                    $Lookup["name:$($Row.Job)"] = $Row.Repository
                }
            }
            $script:BackupJobRepositoryLookup = $Lookup
            return $script:BackupJobRepositoryLookup
        } catch {
            Write-PScriboMessage -IsWarning -Message "Backup Job Repository map load failed: $($_.Exception.Message)"
        }
    }

    $PowerShellProcessPath = Get-AbrVb365PowerShellProcessPath
    if (-not $PowerShellProcessPath) {
        Write-PScriboMessage -IsWarning -Message 'Backup Job Repository map skipped because a PowerShell executable was not found.'
        $script:BackupJobRepositoryLookup = $Lookup
        return $script:BackupJobRepositoryLookup
    }

    $TimeoutSeconds = $env:ABR_VB365_REPOSITORY_MAP_TIMEOUT_SECONDS -as [int]
    if ($TimeoutSeconds -le 0) {
        $TimeoutSeconds = 180
    }

    $TempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ('AbrVb365RepoMap-{0}' -f ([guid]::NewGuid().ToString('N')))
    $ScriptPath = Join-Path $TempRoot 'Get-Vb365JobRepositoryMap.ps1'
    $CredentialPath = Join-Path $TempRoot 'credential.xml'
    $OutputPath = Join-Path $TempRoot 'repository-map.json'
    $StdoutPath = Join-Path $TempRoot 'repository-map.out'
    $StderrPath = Join-Path $TempRoot 'repository-map.err'

    try {
        [void] (New-Item -Path $TempRoot -ItemType Directory -Force)
        $Credential | Export-Clixml -LiteralPath $CredentialPath

        $CaptureScript = @'
param (
    [Parameter(Mandatory = $true)]
    [string] $Server,
 
    [Parameter(Mandatory = $true)]
    [int] $Port,
 
    [Parameter(Mandatory = $true)]
    [string] $CredentialPath,
 
    [Parameter(Mandatory = $true)]
    [string] $OutputPath
)
 
$ErrorActionPreference = 'Stop'
$Credential = Import-Clixml -LiteralPath $CredentialPath
 
try {
    $manualModulePath = 'C:\Program Files\Veeam\Backup365\Veeam.Archiver.PowerShell\Veeam.Archiver.PowerShell.psd1'
    if (Test-Path -LiteralPath $manualModulePath) {
        Import-Module $manualModulePath -ErrorAction Stop
    } else {
        Import-Module Veeam.Archiver.PowerShell -ErrorAction Stop
    }
} catch {
    Import-Module Veeam.Archiver.PowerShell -ErrorAction Stop
}
 
Connect-VBOServer -Server $Server -Credential $Credential -Port $Port
 
$orgs = Get-VBOOrganization
$jobs = foreach ($org in $orgs) {
    Get-VBOJob -Organization $org
}
 
$rows = foreach ($j in $jobs) {
    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    try {
        $repo = $j.Repository
        $sw.Stop()
        [pscustomobject]@{
            Id = if ($j.Id) { $j.Id.ToString() } else { $null }
            Job = $j.Name
            Repository = if ($repo) { $repo.ToString() } else { '--' }
            Seconds = [math]::Round($sw.Elapsed.TotalSeconds, 3)
        }
    } catch {
        $sw.Stop()
        [pscustomobject]@{
            Id = if ($j.Id) { $j.Id.ToString() } else { $null }
            Job = $j.Name
            Repository = '--'
            Seconds = [math]::Round($sw.Elapsed.TotalSeconds, 3)
            Error = $_.Exception.Message
        }
    }
}
 
$rows | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $OutputPath -Encoding UTF8
 
'@

        Set-Content -LiteralPath $ScriptPath -Value $CaptureScript -Encoding UTF8

        Write-PScriboMessage -Message 'Collecting Veeam VB365 Backup Job repository map in a separate PowerShell process.'
        $ArgumentList = @(
            '-NoLogo',
            '-NoProfile',
            '-ExecutionPolicy',
            'Bypass',
            '-File',
            $ScriptPath,
            '-Server',
            $System,
            '-Port',
            [string] $Options.BackupServerPort,
            '-CredentialPath',
            $CredentialPath,
            '-OutputPath',
            $OutputPath
        )
        $Process = Start-Process -FilePath $PowerShellProcessPath -ArgumentList $ArgumentList -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath -WindowStyle Hidden -PassThru
        if (-not $Process.WaitForExit($TimeoutSeconds * 1000)) {
            try {
                $Process.Kill()
            } catch { Out-Null }
            Write-PScriboMessage -IsWarning -Message "Backup Job Repository map timed out after $TimeoutSeconds seconds."
            $script:BackupJobRepositoryLookup = $Lookup
            return $script:BackupJobRepositoryLookup
        }

        if ($Process.ExitCode -ne 0) {
            $ErrorText = if (Test-Path -LiteralPath $StderrPath) { (Get-Content -LiteralPath $StderrPath -Raw).Trim() } else { '' }
            Write-PScriboMessage -IsWarning -Message "Backup Job Repository map process exited with code $($Process.ExitCode). $ErrorText"
            $script:BackupJobRepositoryLookup = $Lookup
            return $script:BackupJobRepositoryLookup
        }

        if (Test-Path -LiteralPath $OutputPath) {
            $Rows = Get-Content -LiteralPath $OutputPath -Raw | ConvertFrom-Json
            foreach ($Row in ($Rows | Where-Object { $_ })) {
                if ($Row.Id) {
                    $Lookup["id:$($Row.Id)"] = $Row.Repository
                }
                if ($Row.Job) {
                    $Lookup["name:$($Row.Job)"] = $Row.Repository
                }
            }
            Write-PScriboMessage -Message "Collected Veeam VB365 Backup Job repository map with $($Lookup.Count) lookup keys."
        } else {
            Write-PScriboMessage -IsWarning -Message 'Backup Job Repository map did not produce an output file.'
        }
    } catch {
        Write-PScriboMessage -IsWarning -Message "Backup Job Repository map failed: $($_.Exception.Message)"
    } finally {
        if (-not $env:ABR_VB365_KEEP_REPOSITORY_MAP_CAPTURE -and (Test-Path -LiteralPath $TempRoot)) {
            Remove-Item -LiteralPath $TempRoot -Recurse -Force -ErrorAction SilentlyContinue
        }
    }

    $script:BackupJobRepositoryLookup = $Lookup
    return $script:BackupJobRepositoryLookup
}

function Get-AbrVb365ExternalBackupCopyJobRepositoryMap {
    [CmdletBinding()]
    [OutputType([hashtable])]
    param (
    )

    if ($script:BackupCopyJobRepositoryLookup) {
        return $script:BackupCopyJobRepositoryLookup
    }

    $Lookup = @{}
    $ConfiguredMapPath = $env:ABR_VB365_COPY_JOB_REPOSITORY_MAP_PATH
    if ($ConfiguredMapPath -and (Test-Path -LiteralPath $ConfiguredMapPath)) {
        try {
            Write-PScriboMessage -Message "Loading Veeam VB365 Backup Copy Job repository map from '$ConfiguredMapPath'."
            $Rows = Get-Content -LiteralPath $ConfiguredMapPath -Raw | ConvertFrom-Json
            foreach ($Row in ($Rows | Where-Object { $_ })) {
                if ($Row.Id) {
                    $Lookup["id:$($Row.Id)"] = $Row.Repository
                }
                if ($Row.Job) {
                    $Lookup["name:$($Row.Job)"] = $Row.Repository
                }
            }
            $script:BackupCopyJobRepositoryLookup = $Lookup
            return $script:BackupCopyJobRepositoryLookup
        } catch {
            Write-PScriboMessage -IsWarning -Message "Backup Copy Job Repository map load failed: $($_.Exception.Message)"
        }
    }

    $PowerShellProcessPath = Get-AbrVb365PowerShellProcessPath
    if (-not $PowerShellProcessPath) {
        Write-PScriboMessage -IsWarning -Message 'Backup Copy Job Repository map skipped because a PowerShell executable was not found.'
        $script:BackupCopyJobRepositoryLookup = $Lookup
        return $script:BackupCopyJobRepositoryLookup
    }

    $TimeoutSeconds = $env:ABR_VB365_COPY_REPOSITORY_MAP_TIMEOUT_SECONDS -as [int]
    if ($TimeoutSeconds -le 0) {
        $TimeoutSeconds = 180
    }

    $TempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ('AbrVb365CopyRepoMap-{0}' -f ([guid]::NewGuid().ToString('N')))
    $ScriptPath = Join-Path $TempRoot 'Get-Vb365CopyJobRepositoryMap.ps1'
    $CredentialPath = Join-Path $TempRoot 'credential.xml'
    $OutputPath = Join-Path $TempRoot 'copy-repository-map.json'
    $StdoutPath = Join-Path $TempRoot 'copy-repository-map.out'
    $StderrPath = Join-Path $TempRoot 'copy-repository-map.err'

    try {
        [void] (New-Item -Path $TempRoot -ItemType Directory -Force)
        $Credential | Export-Clixml -LiteralPath $CredentialPath

        $CaptureScript = @'
param (
    [Parameter(Mandatory = $true)]
    [string] $Server,
 
    [Parameter(Mandatory = $true)]
    [int] $Port,
 
    [Parameter(Mandatory = $true)]
    [string] $CredentialPath,
 
    [Parameter(Mandatory = $true)]
    [string] $OutputPath
)
 
$ErrorActionPreference = 'Stop'
$Credential = Import-Clixml -LiteralPath $CredentialPath
 
try {
    $manualModulePath = 'C:\Program Files\Veeam\Backup365\Veeam.Archiver.PowerShell\Veeam.Archiver.PowerShell.psd1'
    if (Test-Path -LiteralPath $manualModulePath) {
        Import-Module $manualModulePath -ErrorAction Stop
    } else {
        Import-Module Veeam.Archiver.PowerShell -ErrorAction Stop
    }
} catch {
    Import-Module Veeam.Archiver.PowerShell -ErrorAction Stop
}
 
Connect-VBOServer -Server $Server -Credential $Credential -Port $Port
 
$jobs = Get-VBOCopyJob
$rows = foreach ($j in $jobs) {
    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    try {
        $repo = $j.Repository
        $sw.Stop()
        [pscustomobject]@{
            Id = if ($j.Id) { $j.Id.ToString() } else { $null }
            Job = $j.Name
            Repository = if ($repo) { $repo.ToString() } else { '--' }
            Seconds = [math]::Round($sw.Elapsed.TotalSeconds, 3)
        }
    } catch {
        $sw.Stop()
        [pscustomobject]@{
            Id = if ($j.Id) { $j.Id.ToString() } else { $null }
            Job = $j.Name
            Repository = '--'
            Seconds = [math]::Round($sw.Elapsed.TotalSeconds, 3)
            Error = $_.Exception.Message
        }
    }
}
 
$rows | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $OutputPath -Encoding UTF8
'@

        Set-Content -LiteralPath $ScriptPath -Value $CaptureScript -Encoding UTF8

        Write-PScriboMessage -Message 'Collecting Veeam VB365 Backup Copy Job repository map in a separate PowerShell process.'
        $ArgumentList = @(
            '-NoLogo',
            '-NoProfile',
            '-ExecutionPolicy',
            'Bypass',
            '-File',
            $ScriptPath,
            '-Server',
            $System,
            '-Port',
            [string] $Options.BackupServerPort,
            '-CredentialPath',
            $CredentialPath,
            '-OutputPath',
            $OutputPath
        )
        $Process = Start-Process -FilePath $PowerShellProcessPath -ArgumentList $ArgumentList -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath -WindowStyle Hidden -PassThru
        if (-not $Process.WaitForExit($TimeoutSeconds * 1000)) {
            try {
                $Process.Kill()
            } catch { Out-Null }
            Write-PScriboMessage -IsWarning -Message "Backup Copy Job Repository map timed out after $TimeoutSeconds seconds."
            $script:BackupCopyJobRepositoryLookup = $Lookup
            return $script:BackupCopyJobRepositoryLookup
        }

        if ($Process.ExitCode -ne 0) {
            $ErrorText = if (Test-Path -LiteralPath $StderrPath) { (Get-Content -LiteralPath $StderrPath -Raw).Trim() } else { '' }
            Write-PScriboMessage -IsWarning -Message "Backup Copy Job Repository map process exited with code $($Process.ExitCode). $ErrorText"
            $script:BackupCopyJobRepositoryLookup = $Lookup
            return $script:BackupCopyJobRepositoryLookup
        }

        if (Test-Path -LiteralPath $OutputPath) {
            $Rows = Get-Content -LiteralPath $OutputPath -Raw | ConvertFrom-Json
            foreach ($Row in ($Rows | Where-Object { $_ })) {
                if ($Row.Id) {
                    $Lookup["id:$($Row.Id)"] = $Row.Repository
                }
                if ($Row.Job) {
                    $Lookup["name:$($Row.Job)"] = $Row.Repository
                }
            }
            Write-PScriboMessage -Message "Collected Veeam VB365 Backup Copy Job repository map with $($Lookup.Count) lookup keys."
        } else {
            Write-PScriboMessage -IsWarning -Message 'Backup Copy Job Repository map did not produce an output file.'
        }
    } catch {
        Write-PScriboMessage -IsWarning -Message "Backup Copy Job Repository map failed: $($_.Exception.Message)"
    } finally {
        if (-not $env:ABR_VB365_KEEP_REPOSITORY_MAP_CAPTURE -and (Test-Path -LiteralPath $TempRoot)) {
            Remove-Item -LiteralPath $TempRoot -Recurse -Force -ErrorAction SilentlyContinue
        }
    }

    $script:BackupCopyJobRepositoryLookup = $Lookup
    return $script:BackupCopyJobRepositoryLookup
}

function Get-AbrVb365JobRepositoryName {
    [CmdletBinding()]
    [OutputType([string], [pscustomobject])]
    param (
        [Parameter(Mandatory = $true)]
        [object] $Job,

        [Parameter(Mandatory = $false)]
        [int] $TimeoutSeconds = 10
    )

    if ($null -eq $Job) {
        return '--'
    }

    $RepositoryNameProperty = $Job.PSObject.Properties['AbrRepositoryName']
    if ($RepositoryNameProperty -and $RepositoryNameProperty.Value) {
        return $RepositoryNameProperty.Value
    }

    return Set-AbrVb365JobRepositoryMetadata -Job $Job
}