Public/Get-TaxEditorBlob.ps1
|
# Copyright (c) 2026 Jeffrey Snover. All rights reserved. # Licensed under the MIT License. See LICENSE file in the project root. function Get-TaxEditorBlob { <# .SYNOPSIS Lists blobs in the taxonomy editor storage account. .DESCRIPTION Queries Azure Blob Storage for blobs in user content containers. With -Deleted, shows only soft-deleted blobs available for recovery. Requires az CLI logged in with storage account access. .PARAMETER StorageAccount Storage account name. Default: auto-detected from resource group. .PARAMETER Container Filter by container name (e.g., 'user-content', 'community', 'analytics'). .PARAMETER Deleted Show only soft-deleted blobs available for recovery. .PARAMETER ResourceGroup Azure resource group name. Default: ai-triad. .EXAMPLE Get-TaxEditorBlob .EXAMPLE Get-TaxEditorBlob -Container 'user-content' -Deleted .LINK Show-AITriadHelp .LINK Get-TaxEditorDataCommit .LINK Restore-TaxEditorBlob .LINK Restore-TaxEditorKnownGood .LINK Set-TaxEditorKnownGood .LINK Sync-TaxEditorData .LINK Undo-TaxEditorDataCommit #> [CmdletBinding()] param( [Parameter()] [string]$StorageAccount, [Parameter()] [string]$Container, [Parameter()] [switch]$Deleted, [Parameter()] [string]$ResourceGroup = 'ai-triad' ) Set-StrictMode -Version Latest $CallerName = 'Get-TaxEditorBlob' # ── Validate az CLI ────────────────────────────────────────────────── $AzCmd = Get-Command az -ErrorAction SilentlyContinue if (-not $AzCmd) { throw (New-ActionableError ` -Goal 'List storage blobs' ` -Problem 'Azure CLI (az) not found on PATH' ` -Location $CallerName ` -NextSteps @('Install Azure CLI: https://aka.ms/installazurecli')) } $AccountJson = & az account show --output json 2>$null if ($LASTEXITCODE -ne 0 -or -not $AccountJson) { throw (New-ActionableError ` -Goal 'List storage blobs' ` -Problem 'Azure CLI is not logged in' ` -Location $CallerName ` -NextSteps @('Run: az login', 'Verify subscription: az account show')) } # ── Resolve storage account ────────────────────────────────────────── if (-not $StorageAccount) { $AccountListJson = & az storage account list -g $ResourceGroup --query "[?starts_with(name,'staitriad')].name" -o json 2>$null if ($LASTEXITCODE -ne 0 -or -not $AccountListJson) { throw (New-ActionableError ` -Goal 'Resolve storage account' ` -Problem "No storage accounts found in resource group '$ResourceGroup'" ` -Location $CallerName ` -NextSteps @('Pass -StorageAccount explicitly', "Check resource group: az storage account list -g $ResourceGroup")) } $Accounts = $AccountListJson | ConvertFrom-Json if (@($Accounts).Count -eq 0) { throw (New-ActionableError ` -Goal 'Resolve storage account' ` -Problem "No 'staitriad*' storage accounts found in '$ResourceGroup'" ` -Location $CallerName ` -NextSteps @('Pass -StorageAccount explicitly')) } $StorageAccount = $Accounts[0] } # ── Determine containers to query ──────────────────────────────────── $Containers = @() if ($Container) { $Containers = @($Container) } else { $ContainerListJson = & az storage container list --account-name $StorageAccount --auth-mode login --query "[].name" -o json 2>$null if ($LASTEXITCODE -eq 0 -and $ContainerListJson) { $Containers = @($ContainerListJson | ConvertFrom-Json) } if (@($Containers).Count -eq 0) { $Containers = @('user-content', 'community', 'analytics') } } # ── Query blobs ────────────────────────────────────────────────────── foreach ($ContainerName in $Containers) { $AzArgs = @( 'storage', 'blob', 'list' '--account-name', $StorageAccount '--container-name', $ContainerName '--auth-mode', 'login' '--output', 'json' ) if ($Deleted) { $AzArgs += '--include' $AzArgs += 'd' } $BlobJson = & az @AzArgs 2>$null if ($LASTEXITCODE -ne 0 -or -not $BlobJson) { Write-Verbose "No blobs or access denied for container '$ContainerName'." continue } $Blobs = $BlobJson | ConvertFrom-Json foreach ($Blob in $Blobs) { $IsDeleted = $false $DeletedAt = $null if ($Blob.PSObject.Properties['deleted']) { $IsDeleted = [bool]$Blob.deleted } if ($Blob.PSObject.Properties['properties'] -and $Blob.properties.PSObject.Properties['deletedTime']) { $RawVal = $Blob.properties.deletedTime $DeletedAt = if ($RawVal -is [datetime]) { $RawVal.ToString('o') } else { [string]$RawVal } } if ($Deleted -and -not $IsDeleted) { continue } $Size = 0 $LastModified = '' if ($Blob.PSObject.Properties['properties']) { if ($Blob.properties.PSObject.Properties['contentLength']) { $Size = [long]$Blob.properties.contentLength } if ($Blob.properties.PSObject.Properties['lastModified']) { $LastModified = $Blob.properties.lastModified } } $Obj = [BlobInfo]::new() $Obj.Name = $Blob.name $Obj.Container = $ContainerName $Obj.Size = $Size $Obj.LastModified = $LastModified $Obj.Deleted = $IsDeleted $Obj.DeletedAt = $DeletedAt $Obj } } } |