Public/Export-PCOciSnapshot.ps1

function Export-PCOciSnapshot {
    <#
    .SYNOPSIS
        Reads the tenancy's compartments, instances, volumes, backups, backup policies, networks, Cloud Guard problems, agent plugin state, Kubernetes clusters and load balancers into one snapshot file.

    .DESCRIPTION
        Every family in scope, or the ones named with -Include, from every subscribed region or the ones named
        with -Region, below the tenancy root or the compartment named with -CompartmentId. Records are joined by
        id. Cloud Guard is read once, from its reporting region, for every region at once: the active problems
        below the root and the tenancy's security and risk scores (0.2.0). Networks (0.3.0) are the VCNs, subnets,
        security lists and network security groups with their rules, route tables, gateways, dynamic routing
        gateways with their attachments, site-to-site VPN connections with their tunnels' state (never a shared
        secret) and the reserved public addresses, per region. The fleet (0.5.0): the running state of each
        running instance's agent plugins beside what its configuration asks, the container engine's clusters and
        node pools with their nodes (which are instances), and the load balancers of both services with their
        listeners and backend sets, each backend joined to its instance. A family that cannot be read in a
        compartment is recorded in the run report and the run continues; the snapshot is then marked
        complete: false and a warning is written. No field exists for instance metadata. The run report is
        written beside the snapshot.

    .PARAMETER Path
        The snapshot file. Default: .\snapshot.json.

    .PARAMETER Include
        Families to read: Compartments, Instances, Volumes, Backups, BackupPolicies, CloudGuard, Networks, Plugins, Containers, LoadBalancers, Scanning, OsManagement. Default: all.

    .PARAMETER Parts
        The folder where each region's records are written as soon as the region has been read, so a run
        interrupted or refused in its last minute keeps what it had. Default: <snapshot>.parts beside the snapshot.

    .PARAMETER NoParts
        Write no part files.

    .PARAMETER Resume
        Reuse a region's part from the folder instead of reading the region again, when the part is younger than
        -ResumeHours, complete, and holds every family asked for. The report names the parts reused.

    .PARAMETER ResumeHours
        How old a part may be and still be reused. Default 24.

    .PARAMETER Parallel
        How many regions are read at once. Default 4; 1 reads them in turn.

    .PARAMETER Deterministic
        Omit the generation time, so two reads of the same state are byte-identical.

    .PARAMETER PassThru
        Return the snapshot object instead of the summary.

    .EXAMPLE
        Connect-PCOci -ProfileName READONLY; Export-PCOciSnapshot -Path .\data\snapshot.json -Deterministic
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([pscustomobject], [PowerCraft.OCI.Core.Snapshot.TenancySnapshot])]
    param(
        [PowerCraft.OCI.Core.Identity.OciContext]$Context,
        [string]$Path = (Join-Path (Get-Location).Path 'snapshot.json'),
        [string[]]$Region,
        [string]$CompartmentId,
        [ValidateSet('Compartments', 'Instances', 'Volumes', 'Backups', 'BackupPolicies', 'CloudGuard', 'Networks', 'Plugins', 'Containers', 'LoadBalancers', 'Scanning', 'OsManagement')][string[]]$Include,
        [switch]$IncludeDeleted,
        [string]$Parts,
        [switch]$NoParts,
        [switch]$Resume,
        [ValidateRange(1, 8760)][double]$ResumeHours = 24,
        [ValidateRange(1, 16)][int]$Parallel = 4,
        [switch]$Deterministic,
        [string]$ReportPath,
        [switch]$PassThru,
        [switch]$Force
    )

    $ctx = Get-PCOciContextOrThrow -Context $Context
    if ((Test-Path -LiteralPath $Path) -and -not $Force -and -not $Resume) { throw "A file already exists at '$Path'. Use -Force to overwrite it, or -Resume to finish an earlier run into it." }
    if (-not $ReportPath) { $ReportPath = [IO.Path]::ChangeExtension($Path, '.report.json') }
    if (-not $Parts -and -not $NoParts) { $Parts = [IO.Path]::ChangeExtension($Path, '.parts') }
    if (-not $PSCmdlet.ShouldProcess($Path, "Read tenancy '$($ctx.TenancyName)' into a snapshot")) { return }

    $options = [PowerCraft.OCI.Core.Snapshot.SnapshotOptions]::new()
    if ($Region) { $options.Regions.AddRange([string[]]$Region) }
    if ($CompartmentId) { $options.RootCompartmentId = $CompartmentId }
    $options.IncludeDeleted = [bool]$IncludeDeleted
    $options.Families = [PowerCraft.OCI.Core.Snapshot.SnapshotOptions]::ParseFamilies([string[]]$Include)
    if ($Parts -and -not $NoParts) { $options.PartsFolder = $Parts }
    $options.Resume = [bool]$Resume -and -not $NoParts
    $options.ResumeMaxAge = [TimeSpan]::FromHours($ResumeHours)
    $options.Parallelism = $Parallel

    $report = [PowerCraft.OCI.Core.Report.RunReport]::new()
    $report.Verb = 'Export-PCOciSnapshot'
    $started = [DateTimeOffset]::UtcNow
    $report.StartedAt = $started
    Write-Verbose "Reading $($options.Families) from $(if ($Region) { $Region -join ', ' } else { 'every subscribed region' })"
    $snapshot = [PowerCraft.OCI.Core.Gateways.OciSession]::ExportSnapshot($ctx, $options, $report)
    $duration = [long]([DateTimeOffset]::UtcNow - $started).TotalMilliseconds
    $report.DurationMs = $duration

    [PowerCraft.OCI.Core.Snapshot.SnapshotWriter]::Write($snapshot, $Path, [bool]$Deterministic)
    $report.Write($ReportPath, [bool]$Deterministic)
    foreach ($w in $report.Warnings) { Write-Warning $w }
    if (-not $snapshot.Complete) {
        Write-Warning ("The snapshot is incomplete: {0} failure(s). See the report at '{1}'." -f $report.Failures.Count, $ReportPath)
    }

    if ($PassThru) { return $snapshot }
    [pscustomobject]@{
        SnapshotPath   = (Resolve-Path -LiteralPath $Path).Path
        ReportPath     = (Resolve-Path -LiteralPath $ReportPath).Path
        Complete       = $snapshot.Complete
        Regions        = @($snapshot.Regions)
        Compartments   = $snapshot.Compartments.Count
        Instances      = $snapshot.Instances.Count
        Volumes        = $snapshot.Volumes.Count
        Backups        = $snapshot.Backups.Count
        BackupPolicies = $snapshot.BackupPolicies.Count
        PartsReused    = @($report.PartsReused)
        Vcns           = $(if ($snapshot.Networks) { $snapshot.Networks.Vcns.Count } else { $null })
        Subnets        = $(if ($snapshot.Networks) { $snapshot.Networks.Subnets.Count } else { $null })
        Plugins        = $snapshot.Plugins.Count
        Clusters       = $(if ($snapshot.Oke) { $snapshot.Oke.Clusters.Count } else { $null })
        LoadBalancers  = $(if ($snapshot.LoadBalancers) { $snapshot.LoadBalancers.Count } else { $null })
        HostScans      = $(if ($snapshot.Scanning) { $snapshot.Scanning.HostScans.Count } else { $null })
        ManagedInstances = $(if ($snapshot.OsManagement) { $snapshot.OsManagement.ManagedInstances.Count } else { $null })
        CloudGuardProblems = $snapshot.CloudGuardProblems.Count
        SecurityScore  = $(if ($snapshot.CloudGuard) { $snapshot.CloudGuard.SecurityScore } else { $null })
        Failures       = $report.Failures.Count
        DurationMs     = $duration
    }
}