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 } |