Created
March 3, 2026 10:02
-
-
Save AndreasHassing/53bb78d72914868f1b2b0a8dd252a1a8 to your computer and use it in GitHub Desktop.
Get Azure Blob Storage Emulator tree view with PowerShell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Show-AzuriteStorageTree.ps1 | |
| # Tree-view for Azurite (devstoreaccount1) Blob/Queue/Table. | |
| # Folders are virtual: derived from "/" in blob names. [1](https://stackoverflow.com/questions/76901822/how-to-use-tree-command-on-powershell-to-show-files-as-well-as-directories) | |
| # AI-generated, but useful. | |
| [CmdletBinding()] | |
| param( | |
| [ValidateSet('Blobs','Queues','Tables','All')] | |
| [string]$Services = 'All', | |
| # Optional: if you want to force explicit connection string instead of -Local | |
| [string]$ConnectionString, | |
| # Safety valve for huge containers | |
| [int]$MaxBlobsPerContainer = 5000, | |
| # If you prefer ASCII style (+---) rather than Unicode (├──) | |
| [switch]$Ascii | |
| ) | |
| Set-StrictMode -Version Latest | |
| $ErrorActionPreference = 'Stop' | |
| # -------------------------- | |
| # Tree data structure helpers | |
| # -------------------------- | |
| function New-Node { | |
| param( | |
| [Parameter(Mandatory)][string]$Name, | |
| [bool]$IsFolder = $true | |
| ) | |
| [pscustomobject]@{ | |
| Name = $Name | |
| IsFolder = $IsFolder | |
| # Children keyed by "D|name" or "F|name" | |
| Children = @{} | |
| } | |
| } | |
| function Get-OrAddChild { | |
| param( | |
| [Parameter(Mandatory)]$Parent, | |
| [Parameter(Mandatory)][string]$Name, | |
| [Parameter(Mandatory)][bool]$IsFolder | |
| ) | |
| $key = ('{0}|{1}' -f ($(if($IsFolder){'D'}else{'F'})), $Name) | |
| if (-not $Parent.Children.ContainsKey($key)) { | |
| $Parent.Children[$key] = New-Node -Name $Name -IsFolder:$IsFolder | |
| } | |
| $Parent.Children[$key] | |
| } | |
| function Add-BlobToTree { | |
| param( | |
| [Parameter(Mandatory)]$ContainerNode, | |
| [Parameter(Mandatory)][string]$BlobName | |
| ) | |
| if ([string]::IsNullOrWhiteSpace($BlobName)) { return } | |
| $isFolderMarker = $BlobName.EndsWith('/') | |
| $segments = $BlobName -split '/' | Where-Object { $_.Trim().Length -gt 0 } | |
| if ($segments.Count -eq 0) { return } | |
| $node = $ContainerNode | |
| # All but last segment are folders | |
| for ($i = 0; $i -lt ($segments.Count - 1); $i++) { | |
| $node = Get-OrAddChild -Parent $node -Name $segments[$i] -IsFolder:$true | |
| } | |
| # Last segment: folder if blob endswith '/', else file | |
| $leafName = $segments[$segments.Count - 1] | |
| [void](Get-OrAddChild -Parent $node -Name $leafName -IsFolder:$isFolderMarker) | |
| } | |
| # -------------------------- | |
| # Tree rendering | |
| # -------------------------- | |
| function Get-SortedChildren { | |
| param([Parameter(Mandatory)]$Node) | |
| $Node.Children.Values | | |
| Sort-Object @{Expression={$_.IsFolder};Descending=$true}, | |
| @{Expression={$_.Name};Ascending=$true} | |
| } | |
| function Write-TreeLines { | |
| param( | |
| [Parameter(Mandatory)]$Node, | |
| [string]$Prefix = '', | |
| [bool]$IsLast = $true, | |
| [bool]$IsRoot = $false | |
| ) | |
| if ($IsRoot) { | |
| Write-Output $Node.Name | |
| } | |
| else { | |
| if ($Ascii) { | |
| $branch = if ($IsLast) { '\--- ' } else { '+--- ' } | |
| } else { | |
| $branch = if ($IsLast) { '└── ' } else { '├── ' } | |
| } | |
| Write-Output ($Prefix + $branch + $Node.Name) | |
| } | |
| $children = @(Get-SortedChildren -Node $Node) | |
| if ($children.Count -eq 0) { return } | |
| for ($i = 0; $i -lt $children.Count; $i++) { | |
| $child = $children[$i] | |
| $lastChild = ($i -eq ($children.Count - 1)) | |
| $nextPrefix = | |
| if ($IsRoot) { | |
| '' | |
| } | |
| else { | |
| if ($Ascii) { | |
| $Prefix + $(if ($IsLast) { ' ' } else { '| ' }) | |
| } else { | |
| $Prefix + $(if ($IsLast) { ' ' } else { '│ ' }) | |
| } | |
| } | |
| Write-TreeLines -Node $child -Prefix $nextPrefix -IsLast:$lastChild -IsRoot:$false | |
| } | |
| } | |
| # -------------------------- | |
| # Connect to Azurite | |
| # -------------------------- | |
| # New-AzStorageContext -Local is the emulator shortcut. [4](https://outlook.office365.com/owa/?ItemID=AAMkADI5ZWNkZjcyLTZlZTUtNGViMC04N2RmLWVjYWI5OTFjYTFmZgBGAAAAAAAE8r5GwEoaSbLsP%2fgKGGRkBwCbQY81HgjDT4do75kiiXF6AAAAAAEJAACbQY81HgjDT4do75kiiXF6AAOvZefvAAA%3d&exvsurl=1&viewmodel=ReadMessageItem) | |
| $ctx = | |
| if ($ConnectionString) { | |
| New-AzStorageContext -ConnectionString $ConnectionString | |
| } | |
| else { | |
| New-AzStorageContext -Local | |
| } | |
| $root = New-Node -Name 'Azurite (devstoreaccount1)' -IsFolder:$true | |
| $wantBlobs = ($Services -eq 'All' -or $Services -eq 'Blobs') | |
| $wantQueues = ($Services -eq 'All' -or $Services -eq 'Queues') | |
| $wantTables = ($Services -eq 'All' -or $Services -eq 'Tables') | |
| # -------------------------- | |
| # Blobs: containers -> folders -> files | |
| # -------------------------- | |
| if ($wantBlobs) { | |
| $blobsNode = Get-OrAddChild -Parent $root -Name 'Blobs' -IsFolder:$true | |
| # List containers. [2](https://petri.com/powertree-powershell-visualize-file-structure/) | |
| $containers = @(Get-AzStorageContainer -Context $ctx) | |
| if ($containers.Count -eq 0) { | |
| [void](Get-OrAddChild -Parent $blobsNode -Name '(no containers)' -IsFolder:$false) | |
| } | |
| else { | |
| foreach ($c in $containers) { | |
| $containerNode = Get-OrAddChild -Parent $blobsNode -Name $c.Name -IsFolder:$true | |
| # List blobs for container. [3](https://learn.microsoft.com/en-gb/answers/questions/1428061/find-files-or-directory-using-powershell-in-both-f) | |
| $blobs = @(Get-AzStorageBlob -Container $c.Name -Context $ctx | Select-Object -First $MaxBlobsPerContainer) | |
| if ($blobs.Count -eq 0) { | |
| [void](Get-OrAddChild -Parent $containerNode -Name '(empty)' -IsFolder:$false) | |
| continue | |
| } | |
| foreach ($b in $blobs) { | |
| Add-BlobToTree -ContainerNode $containerNode -BlobName ([string]$b.Name) | |
| } | |
| } | |
| } | |
| } | |
| # -------------------------- | |
| # Queues (optional) | |
| # -------------------------- | |
| if ($wantQueues) { | |
| # Azurite supports queues. [5](https://stackoverflow.com/questions/59615330/connecting-to-azurite-using-a-hostname-fails) | |
| $queuesNode = Get-OrAddChild -Parent $root -Name 'Queues' -IsFolder:$truequeues. [6](https://github.com/Azure/azure-powershell/blob/main/src/Storage/Storage.Management/help/Get-AzStorageQueue.md) | |
| $queues = @(Get-AzStorageQueue -Context $ctx) | |
| if ($queues.Count -eq 0) { | |
| [void](Get-OrAddChild -Parent $queuesNode -Name '(no queues)' -IsFolder:$false) | |
| } | |
| else { | |
| foreach ($q in $queues) { | |
| [void](Get-OrAddChild -Parent $queuesNode -Name $q.Name -IsFolder:$false) | |
| } | |
| } | |
| } | |
| # -------------------------- | |
| # Tables (optional) | |
| # -------------------------- | |
| if ($wantTables) { | |
| # Azurite supports tables. [5](https://stackoverflow.com/questions/59615330/connecting-to-azurite-using-a-hostname-fails) | |
| $tablesNode = Get-OrAddChild -Parent $root -Name 'Tables' -IsFolder:$true | |
| # Get-AzStorageTable lists tables..powershellgallery.com/packages/Az.Storage/3.7.0) | |
| $tables = @(Get-AzStorageTable -Context $ctx) | |
| if ($tables.Count -eq 0) { | |
| [void](Get-OrAddChild -Parent $tablesNode -Name '(no tables)' -IsFolder:$false) | |
| } | |
| else { | |
| foreach ($t in $tables) { | |
| [void](Get-OrAddChild -Parent $tablesNode -Name $t.Name -IsFolder:$false) | |
| } | |
| } | |
| } | |
| Write-TreeLines -Node $root -IsRoot:$true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment