GinShell.Azure/Public/Send-GsAzureSnapshotsToStorage.ps1

function Send-GsAzureSnapshotsToStorage {
    <#
    .SYNOPSIS
        Copies Azure snapshots to a blob storage container (supports cross-tenant).
    .DESCRIPTION
        Generates SAS tokens for each snapshot and initiates async blob copy.
        Optionally monitors progress until all copies complete.
    .PARAMETER Snapshot
        One or more Azure snapshot objects.
    .PARAMETER StorageAccountName
        Destination storage account name.
    .PARAMETER StorageAccountKey
        Access key for the destination storage account.
    .PARAMETER ContainerName
        Destination blob container name.
    .PARAMETER TimeOutSec
        SAS token duration in seconds. Default: 86400 (24 hours).
    .PARAMETER PollIntervalSec
        Seconds between progress checks when -WaitForComplete is used. Default: 30.
    .PARAMETER WaitForComplete
        Block and monitor copy progress until all blobs finish.
    .EXAMPLE
        Send-GsAzureSnapshotsToStorage -Snapshot $snaps -StorageAccountName 'mystorage' -StorageAccountKey $key -ContainerName 'snapshots' -WaitForComplete
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter(Mandatory)]
        [object[]]$Snapshot,

        [Parameter(Mandatory)]
        [string]$StorageAccountName,

        [Parameter(Mandatory)]
        [string]$StorageAccountKey,

        [Parameter(Mandatory)]
        [string]$ContainerName,

        [int]$TimeOutSec = 86400,

        [int]$PollIntervalSec = 30,

        [switch]$WaitForComplete
    )

    try {
        Write-GsLog -Message "Creating storage context for '$StorageAccountName'" -Type Info
        $context = New-AzStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $StorageAccountKey -ErrorAction Stop
    }
    catch {
        Write-GsLog -Message "Failed to create storage context: $($_.Exception.Message)" -Type Error
        throw
    }

    $blobNames = @()

    foreach ($snap in $Snapshot) {
        if (-not $PSCmdlet.ShouldProcess($snap.Name, "Copy snapshot to blob '$ContainerName'")) { continue }

        try {
            # Ensure public access is enabled
            if (($snap.PublicNetworkAccess -ne 'Enabled') -or ($snap.NetworkAccessPolicy -ne 'AllowAll')) {
                Write-GsLog -Message "Public network access is not enabled for snapshot '$($snap.Name)'" -Type Warning
                Enable-GsAzureSnapshotPublicAccess -Snapshot $snap
            }

            Write-GsLog -Message "Generating SAS URL for snapshot '$($snap.Name)'" -Type Debug
            $sas = Grant-AzSnapshotAccess -ResourceGroupName $snap.ResourceGroupName -SnapshotName $snap.Name -DurationInSecond $TimeOutSec -Access Read -ErrorAction Stop

            Write-GsLog -Message "Starting blob copy for snapshot '$($snap.Name)' to container '$ContainerName'" -Type Action
            Start-AzStorageBlobCopy -AbsoluteUri $sas.AccessSAS -DestContainer $ContainerName -DestContext $context -DestBlob $snap.Name -ErrorAction Stop | Out-Null

            $blobNames += $snap.Name
        }
        catch {
            Write-GsLog -Message "Failed to start copy for snapshot '$($snap.Name)': $($_.Exception.Message)" -Type Error
            throw
        }
    }

    if ($WaitForComplete -and $blobNames.Count -gt 0) {
        Write-GsLog -Message "Monitoring snapshot copy progress..." -Type Info
        $inProgress = $true

        while ($inProgress) {
            $inProgress = $false

            foreach ($blobName in $blobNames) {
                $copyState = Get-AzStorageBlobCopyState -Blob $blobName -Container $ContainerName -Context $context

                if ($copyState.Status -ne 'Success') {
                    $inProgress = $true
                    $percent = if ($copyState.TotalBytes -gt 0) {
                        [math]::Round(100 * $copyState.BytesCopied / $copyState.TotalBytes, 2)
                    } else { 0 }
                    Write-GsLog -Message "Snapshot '$blobName' copy: $($copyState.Status) - ${percent}% completed" -Type Debug
                }
                else {
                    Write-GsLog -Message "Snapshot '$blobName' copy completed successfully" -Type Success
                }
            }

            if ($inProgress) { Start-Sleep -Seconds $PollIntervalSec }
        }
    }

    Write-GsLog -Message "Fetching blob references from container '$ContainerName'" -Type Info
    $blobs = foreach ($blobName in $blobNames) {
        try {
            $blob = Get-AzStorageBlob -Blob $blobName -Container $ContainerName -Context $context -ErrorAction Stop
            $blob.ICloudBlob
        }
        catch {
            Write-GsLog -Message "Failed to retrieve blob '$blobName': $($_.Exception.Message)" -Type Warning
        }
    }

    return $blobs
}