Private/Interactive/Show-TBSnapshotMenu.ps1
|
function Wait-TBSnapshotInteractive { <# .SYNOPSIS Polls a snapshot job with a smooth progress display. .DESCRIPTION Decouples the display refresh rate (250ms) from the API poll interval (10s) so the spinner and elapsed timer update smoothly while waiting. .PARAMETER SnapshotId The ID of the snapshot job to poll. .PARAMETER ResourceCount Number of resource types in the snapshot (shown in progress text). #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$SnapshotId, [Parameter(Mandatory = $true)] [int]$ResourceCount ) $sw = [System.Diagnostics.Stopwatch]::StartNew() $spinIdx = 0 $snapshot = Get-TBSnapshot -SnapshotId $SnapshotId $pollIntervalMs = 10000 $displayIntervalMs = 250 $lastPollTime = [DateTime]::UtcNow while ($snapshot.Status -eq 'notStarted' -or $snapshot.Status -eq 'running') { Write-TBSnapshotProgress -Status $snapshot.Status -ResourceCount $ResourceCount ` -Stopwatch $sw -SpinIndex $spinIdx $spinIdx++ Start-Sleep -Milliseconds $displayIntervalMs $msSincePoll = ([DateTime]::UtcNow - $lastPollTime).TotalMilliseconds if ($msSincePoll -ge $pollIntervalMs) { $snapshot = Get-TBSnapshot -SnapshotId $SnapshotId $lastPollTime = [DateTime]::UtcNow } } Write-TBProgressComplete -Stopwatch $sw return $snapshot } function Invoke-TBSnapshotAction { <# .SYNOPSIS Executes a single snapshot management action by index. .DESCRIPTION Contains the action logic extracted from Show-TBSnapshotMenu's switch block. Called by both the classic submenu loop and the accordion direct-action path. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [int]$ActionIndex ) switch ($ActionIndex) { 0 { # Create snapshot - pick resource types Write-Host '' Write-Host ' -- Create Snapshot (Selected Resource Types) --' -ForegroundColor Cyan $resourceTypes = Select-TBResourceType if (-not $resourceTypes) { Write-Host ' No resource types selected. Cancelled.' -ForegroundColor Yellow Read-Host -Prompt ' Press Enter to continue' return } $displayName = Read-TBUserInput -Prompt 'Snapshot display name' -Mandatory ` -MinLength 8 -MaxLength 32 ` -Pattern '^[a-zA-Z0-9 ]{8,32}$' ` -PatternMessage 'Must be 8-32 characters, alphanumeric and spaces only.' if (-not $displayName) { return } $description = Read-TBUserInput -Prompt 'Description (optional)' $confirmed = Read-TBUserInput -Prompt ('Create snapshot "{0}" with {1} resource type(s)?' -f $displayName, $resourceTypes.Count) -Confirm if (-not $confirmed) { return } try { $params = @{ DisplayName = $displayName Resources = $resourceTypes Confirm = $false } if ($description) { $params['Description'] = $description } $result = New-TBSnapshot @params Write-Host '' Write-Host (' Snapshot created: {0}' -f $result.Id) -ForegroundColor Green Write-Host (' Status: {0}' -f $result.Status) -ForegroundColor White $waitForIt = Read-TBUserInput -Prompt 'Wait for completion?' -Confirm if ($waitForIt) { $snapshot = Wait-TBSnapshotInteractive -SnapshotId $result.Id -ResourceCount $resourceTypes.Count Write-Host (' Final status: {0}' -f $snapshot.Status) -ForegroundColor White } } catch { Write-Host (' Error: {0}' -f $_.Exception.Message) -ForegroundColor Red } Read-Host -Prompt ' Press Enter to continue' } 1 { # Create snapshot - entire workload Write-Host '' Write-Host ' -- Create Snapshot (Entire Workload) --' -ForegroundColor Cyan $registry = Get-TBResourceTypeRegistry $workloadNames = @($registry.Keys | Sort-Object) $workloadOptions = foreach ($name in $workloadNames) { $count = $registry[$name].ResourceTypes.Count '{0} ({1} resource types)' -f $name, $count } $wChoice = Show-TBMenu -Title 'Select Workload' -Options $workloadOptions -IncludeBack if ($wChoice -eq 'Back') { return } $workloadName = $workloadNames[$wChoice] $resourceTypes = @($registry[$workloadName].ResourceTypes | ForEach-Object { $_.Name }) Write-Host '' Write-Host (' {0} - {1} resource types' -f $workloadName, $resourceTypes.Count) -ForegroundColor Green $displayName = Read-TBUserInput -Prompt 'Snapshot display name' -Mandatory ` -MinLength 8 -MaxLength 32 ` -Pattern '^[a-zA-Z0-9 ]{8,32}$' ` -PatternMessage 'Must be 8-32 characters, alphanumeric and spaces only.' if (-not $displayName) { return } $description = Read-TBUserInput -Prompt 'Description (optional)' $confirmed = Read-TBUserInput -Prompt ('Create snapshot "{0}" with {1} resource type(s)?' -f $displayName, $resourceTypes.Count) -Confirm if (-not $confirmed) { return } try { $params = @{ DisplayName = $displayName Resources = $resourceTypes Confirm = $false } if ($description) { $params['Description'] = $description } $result = New-TBSnapshot @params Write-Host '' Write-Host (' Snapshot created: {0}' -f $result.Id) -ForegroundColor Green Write-Host (' Status: {0}' -f $result.Status) -ForegroundColor White $waitForIt = Read-TBUserInput -Prompt 'Wait for completion?' -Confirm if ($waitForIt) { $snapshot = Wait-TBSnapshotInteractive -SnapshotId $result.Id -ResourceCount $resourceTypes.Count Write-Host (' Final status: {0}' -f $snapshot.Status) -ForegroundColor White } } catch { Write-Host (' Error: {0}' -f $_.Exception.Message) -ForegroundColor Red } Read-Host -Prompt ' Press Enter to continue' } 2 { # Create snapshot - all workloads Write-Host '' Write-Host ' -- Create Snapshot (All Workloads) --' -ForegroundColor Cyan $registry = Get-TBResourceTypeRegistry $allTypes = [System.Collections.ArrayList]::new() foreach ($workloadName in ($registry.Keys | Sort-Object)) { foreach ($rt in $registry[$workloadName].ResourceTypes) { $null = $allTypes.Add($rt.Name) } } Write-Host (' All workloads - {0} resource types total' -f $allTypes.Count) -ForegroundColor Green $displayName = Read-TBUserInput -Prompt 'Snapshot display name' -Mandatory ` -MinLength 8 -MaxLength 32 ` -Pattern '^[a-zA-Z0-9 ]{8,32}$' ` -PatternMessage 'Must be 8-32 characters, alphanumeric and spaces only.' if (-not $displayName) { return } $description = Read-TBUserInput -Prompt 'Description (optional)' $confirmed = Read-TBUserInput -Prompt ('Create snapshot "{0}" with {1} resource type(s)?' -f $displayName, $allTypes.Count) -Confirm if (-not $confirmed) { return } try { $params = @{ DisplayName = $displayName Resources = @($allTypes) Confirm = $false } if ($description) { $params['Description'] = $description } $result = New-TBSnapshot @params Write-Host '' Write-Host (' Snapshot created: {0}' -f $result.Id) -ForegroundColor Green Write-Host (' Status: {0}' -f $result.Status) -ForegroundColor White $waitForIt = Read-TBUserInput -Prompt 'Wait for completion?' -Confirm if ($waitForIt) { $snapshot = Wait-TBSnapshotInteractive -SnapshotId $result.Id -ResourceCount $allTypes.Count Write-Host (' Final status: {0}' -f $snapshot.Status) -ForegroundColor White } } catch { Write-Host (' Error: {0}' -f $_.Exception.Message) -ForegroundColor Red } Read-Host -Prompt ' Press Enter to continue' } 3 { # List snapshot jobs Write-Host '' Write-Host ' -- Snapshot Jobs --' -ForegroundColor Cyan Write-Host '' try { $snapshots = @(Get-TBSnapshot) if ($snapshots.Count -eq 0) { Write-Host ' No snapshots found.' -ForegroundColor Yellow } else { $snapshots | Format-Table -Property @( @{ Label = 'ID'; Expression = { $_.Id } } @{ Label = 'Display Name'; Expression = { $_.DisplayName } } @{ Label = 'Status'; Expression = { $_.Status } } @{ Label = 'Created'; Expression = { $_.CreatedDateTime } } ) -AutoSize | Out-Host } } catch { Write-Host (' Error: {0}' -f $_.Exception.Message) -ForegroundColor Red } Read-Host -Prompt ' Press Enter to continue' } 4 { # View snapshot details try { $snapshots = @(Get-TBSnapshot) if ($snapshots.Count -eq 0) { Write-Host '' Write-Host ' No snapshots found.' -ForegroundColor Yellow Read-Host -Prompt ' Press Enter to continue' return } $snapshotOptions = foreach ($s in $snapshots) { '{0} - {1} ({2})' -f $s.DisplayName, $s.Id, $s.Status } $selected = Show-TBMenu -Title 'Select Snapshot' -Options $snapshotOptions -IncludeBack if ($selected -eq 'Back') { return } $snapshot = Get-TBSnapshot -SnapshotId $snapshots[$selected].Id Write-Host '' Write-Host ' -- Snapshot Details --' -ForegroundColor Cyan Write-Host (' ID: {0}' -f $snapshot.Id) -ForegroundColor White Write-Host (' Display Name: {0}' -f $snapshot.DisplayName) -ForegroundColor White Write-Host (' Status: {0}' -f $snapshot.Status) -ForegroundColor White Write-Host (' Created: {0}' -f $snapshot.CreatedDateTime) -ForegroundColor White Write-Host (' Completed: {0}' -f $snapshot.CompletedDateTime) -ForegroundColor White if ($snapshot.Resources) { Write-Host (' Resources: {0}' -f (@($snapshot.Resources) -join ', ')) -ForegroundColor White } } catch { Write-Host (' Error: {0}' -f $_.Exception.Message) -ForegroundColor Red } Read-Host -Prompt ' Press Enter to continue' } 5 { # Export snapshot try { $snapshots = @(Get-TBSnapshot) if ($snapshots.Count -eq 0) { Write-Host '' Write-Host ' No snapshots found.' -ForegroundColor Yellow Read-Host -Prompt ' Press Enter to continue' return } $snapshotOptions = foreach ($s in $snapshots) { '{0} - {1} ({2})' -f $s.DisplayName, $s.Id, $s.Status } $selected = Show-TBMenu -Title 'Select Snapshot to Export' -Options $snapshotOptions -IncludeBack if ($selected -eq 'Back') { return } $outputPath = Read-TBUserInput -Prompt 'Output file path (leave blank for default)' $exportParams = @{ SnapshotId = $snapshots[$selected].Id } if ($outputPath) { $exportParams['OutputPath'] = $outputPath } $result = Export-TBSnapshot @exportParams Write-Host '' Write-Host (' Snapshot exported to: {0}' -f $result.OutputPath) -ForegroundColor Green } catch { Write-Host (' Error: {0}' -f $_.Exception.Message) -ForegroundColor Red } Read-Host -Prompt ' Press Enter to continue' } 6 { # Delete snapshot try { $snapshots = @(Get-TBSnapshot) if ($snapshots.Count -eq 0) { Write-Host '' Write-Host ' No snapshots found.' -ForegroundColor Yellow Read-Host -Prompt ' Press Enter to continue' return } $snapshotOptions = foreach ($s in $snapshots) { '{0} - {1} ({2})' -f $s.DisplayName, $s.Id, $s.Status } $selected = Show-TBMenu -Title 'Select Snapshot to Delete' -Options $snapshotOptions -IncludeBack if ($selected -eq 'Back') { return } $snapshot = $snapshots[$selected] $confirmed = Read-TBUserInput -Prompt ('Delete snapshot "{0}"? This cannot be undone' -f $snapshot.DisplayName) -Confirm if ($confirmed) { Remove-TBSnapshot -SnapshotId $snapshot.Id -Confirm:$false Write-Host ' Snapshot deleted.' -ForegroundColor Green } else { Write-Host ' Cancelled.' -ForegroundColor Yellow } } catch { Write-Host (' Error: {0}' -f $_.Exception.Message) -ForegroundColor Red } Read-Host -Prompt ' Press Enter to continue' } } } function Show-TBSnapshotMenu { <# .SYNOPSIS Displays the snapshot management submenu. .DESCRIPTION Interactive menu for creating, listing, viewing, exporting, and deleting configuration snapshots. #> [CmdletBinding()] param( [Parameter()] [int]$DirectAction = -1 ) if ($DirectAction -ge 0) { Invoke-TBSnapshotAction -ActionIndex $DirectAction return } while ($true) { Clear-Host Write-TBMenuHeader -Subtitle 'Snapshot Management' $options = @( 'Create snapshot - pick resource types' 'Create snapshot - entire workload' 'Create snapshot - all workloads' 'List snapshot jobs' 'View snapshot details' 'Export snapshot' 'Delete snapshot' ) $choice = Show-TBMenu -Title 'Snapshot Management' -Options $options -IncludeBack if ($choice -eq 'Back') { return } Invoke-TBSnapshotAction -ActionIndex $choice } } |