Private/Write-AACStorageSizeHtml.ps1
|
function Write-AACStorageSizeHtml { <# .SYNOPSIS Writes Get-AACStorageAccountContainerSize's report as an interactive HTML report: tiles and charts that filter tables of the storage accounts, the containers and the largest blobs. #> [CmdletBinding()] [OutputType([System.IO.FileInfo])] param( [Parameter(Mandatory)] [hashtable] $Report, [Parameter(Mandatory)] [string] $Path, [Parameter(Mandatory)] [string] $Title, [System.Collections.IDictionary] $Detail ) $stats = $Report.Stats $gib = [Math]::Pow(1024, 3) $toGib = { param([double] $Bytes) [Math]::Round($Bytes / $gib, 3) } $statusTones = @{ OK = 'good'; Partial = 'warn'; Failed = 'bad'; Empty = 'neutral'; 'No blob service' = 'neutral' } $tierTones = @{ Hot = 'bad'; Cool = 'info'; Cold = 'violet'; Archive = 'neutral'; None = 'neutral' } $containers = @($Report.Rows | Where-Object { $null -ne $_.Container } | ForEach-Object { [ordered]@{ StorageAccount = $_.StorageAccount; Container = $_.Container; Status = $_.Status; BlobCount = $_.BlobCount SizeGiB = & $toGib $_.Size; SizeText = $_.SizeText HotGiB = & $toGib $_.SizeHot; CoolGiB = & $toGib $_.SizeCool; ColdGiB = & $toGib $_.SizeCold; ArchiveGiB = & $toGib $_.SizeArchive; NoTierGiB = & $toGib $_.SizeNoTier Snapshots = $_.Snapshots; Versions = $_.Versions; DeletedBlobs = $_.DeletedBlobs; TotalGiB = & $toGib $_.TotalSize LastModified = $(if ($_.LastModified) { ([datetime]$_.LastModified).ToString('o') } else { '' }) PublicAccess = $_.PublicAccess; AuthMode = $_.AuthMode; Error = $_.Error ResourceGroup = $_.ResourceGroup; SubscriptionName = $_.SubscriptionName; Location = $_.Location; Kind = $_.Kind; Sku = $_.Sku ResourceId = $_.StorageAccountId; Bytes = $_.Size } }) $accounts = @($Report.Accounts | ForEach-Object { [ordered]@{ StorageAccount = $_.StorageAccount; Status = $_.Status; Containers = $_.Containers; Failed = $_.Failed; BlobCount = $_.BlobCount SizeGiB = & $toGib $_.Size; SizeText = $_.SizeText; Reason = $_.Reason Kind = $_.Kind; Sku = $_.Sku; Location = $_.Location; NetworkAccess = $_.NetworkAccess; DataLake = $(if ($_.HierarchicalNamespace) { 'Yes' } else { 'No' }) ResourceGroup = $_.ResourceGroup; SubscriptionName = $_.SubscriptionName; ResourceId = $_.StorageAccountId } }) $blobs = @($Report.Largest | ForEach-Object { [ordered]@{ Name = $_.Name; StorageAccount = $_.StorageAccount; Container = $_.Container; SizeGiB = & $toGib $_.Size; SizeText = (Format-AACByteSize -Bytes $_.Size) AccessTier = $_.AccessTier; BlobType = $_.BlobType; LastModified = $(if ($_.LastModified) { ([datetime]$_.LastModified).ToString('o') } else { '' }) } }) $notRead = $stats.Failed + $stats.Partial $tiles = @( @{ Value = '{0:N0}' -f $stats.Accounts; Label = 'storage accounts'; Tone = 'info'; Table = 'accounts' } @{ Value = '{0:N0}' -f $stats.Containers; Label = 'containers'; Tone = 'violet'; Table = 'containers' } @{ Value = '{0:N0}' -f $stats.Blobs; Label = 'blobs'; Tone = 'info'; Table = 'containers' } @{ Value = (Format-AACByteSize -Bytes $stats.Bytes); Label = 'stored'; Tone = 'good'; Table = 'containers' } @{ Value = '{0:N0}' -f $stats.Empty; Label = 'empty containers'; Tone = 'neutral'; Table = 'containers' } @{ Value = '{0:N0}' -f $notRead; Label = 'not read in full'; Tone = $(if ($notRead) { 'bad' } else { 'good' }); Table = 'containers'; Filters = @{ Status = 'Failed' } } ) $charts = @( @{ Title = 'Size by access tier (GiB)'; Kind = 'donut'; CenterLabel = 'GiB'; Format = 'N2'; Items = @($Report.Tiers.Keys | Where-Object { $Report.Tiers[$_] -gt 0 } | ForEach-Object { @{ Label = $_; Value = (& $toGib $Report.Tiers[$_]); Display = (Format-AACByteSize -Bytes $Report.Tiers[$_]); Tone = $tierTones[$_] } }) } @{ Title = 'Largest storage accounts (GiB)'; Format = 'N2'; Table = 'containers'; Column = 'StorageAccount'; Items = @($Report.Accounts | Where-Object Size | Select-Object -First 12 | ForEach-Object { @{ Label = $_.StorageAccount; Value = (& $toGib $_.Size); Display = $_.SizeText; Tone = 'info' } }) } @{ Title = 'Largest containers (GiB)'; Format = 'N2'; Table = 'containers'; Column = 'Container'; Wide = $true; Items = @($Report.Rows | Where-Object { $null -ne $_.Container -and $_.Size } | Sort-Object Size -Descending | Select-Object -First 15 | ForEach-Object { @{ Label = "$($_.StorageAccount)/$($_.Container)"; Filter = $_.Container; Value = (& $toGib $_.Size); Display = $_.SizeText; Tone = 'good' } }) } ) $tables = @( @{ Id = 'containers'; Title = 'Containers'; Note = 'Size: the current blobs. Total: with the snapshots, versions and deleted blobs listed.'; Noun = 'containers'; File = 'storage-containers' Rows = $containers; Sort = @{ Key = 'SizeGiB'; Desc = $true }; GroupBy = @('StorageAccount', 'SubscriptionName', 'Status') Columns = @( @{ Key = 'StorageAccount'; Label = 'Storage account'; Type = 'resource'; IdKey = 'ResourceId'; Facet = $true } @{ Key = 'Container'; Label = 'Container'; Type = 'mono' } @{ Key = 'Status'; Label = 'Status'; Type = 'badge'; Tones = $statusTones; Facet = $true } @{ Key = 'BlobCount'; Label = 'Blobs'; Type = 'number'; Format = 'N0'; Sum = $true } @{ Key = 'SizeGiB'; Label = 'Size (GiB)'; Type = 'number'; Format = 'N2'; Sum = $true } @{ Key = 'SizeText'; Label = 'Size' } @{ Key = 'HotGiB'; Label = 'Hot GiB'; Type = 'number'; Format = 'N2'; Sum = $true } @{ Key = 'CoolGiB'; Label = 'Cool GiB'; Type = 'number'; Format = 'N2'; Sum = $true } @{ Key = 'ColdGiB'; Label = 'Cold GiB'; Type = 'number'; Format = 'N2'; Sum = $true } @{ Key = 'ArchiveGiB'; Label = 'Archive GiB'; Type = 'number'; Format = 'N2'; Sum = $true } @{ Key = 'NoTierGiB'; Label = 'No tier GiB'; Type = 'number'; Format = 'N2'; Sum = $true; Hidden = $true } @{ Key = 'LastModified'; Label = 'Last changed'; Type = 'datetime' } @{ Key = 'PublicAccess'; Label = 'Public access'; Facet = $true } @{ Key = 'Error'; Label = 'Why not read'; Type = 'wide' } @{ Key = 'TotalGiB'; Label = 'Total GiB'; Type = 'number'; Format = 'N2'; Sum = $true; Hidden = $true } @{ Key = 'Snapshots'; Label = 'Snapshots'; Type = 'number'; Format = 'N0'; Sum = $true; Hidden = $true } @{ Key = 'Versions'; Label = 'Versions'; Type = 'number'; Format = 'N0'; Sum = $true; Hidden = $true } @{ Key = 'DeletedBlobs'; Label = 'Deleted'; Type = 'number'; Format = 'N0'; Sum = $true; Hidden = $true } @{ Key = 'AuthMode'; Label = 'Read with'; Facet = $true; Hidden = $true } @{ Key = 'ResourceGroup'; Label = 'Resource group'; Facet = $true; Hidden = $true } @{ Key = 'SubscriptionName'; Label = 'Subscription'; Facet = $true } @{ Key = 'Location'; Label = 'Region'; Facet = $true; Hidden = $true } @{ Key = 'Kind'; Label = 'Kind'; Facet = $true; Hidden = $true } @{ Key = 'Sku'; Label = 'SKU'; Facet = $true; Hidden = $true } @{ Key = 'Bytes'; Label = 'Bytes'; Type = 'number'; Format = 'N0'; Hidden = $true } ) } @{ Id = 'accounts'; Title = 'Storage accounts'; Noun = 'storage accounts'; File = 'storage-accounts'; Rows = $accounts; Sort = @{ Key = 'SizeGiB'; Desc = $true } Columns = @( @{ Key = 'StorageAccount'; Label = 'Storage account'; Type = 'resource'; IdKey = 'ResourceId' } @{ Key = 'Status'; Label = 'Status'; Type = 'badge'; Tones = $statusTones; Facet = $true } @{ Key = 'Containers'; Label = 'Containers'; Type = 'number'; Format = 'N0'; Sum = $true } @{ Key = 'Failed'; Label = 'Not read'; Type = 'number'; Format = 'N0'; Sum = $true } @{ Key = 'BlobCount'; Label = 'Blobs'; Type = 'number'; Format = 'N0'; Sum = $true } @{ Key = 'SizeGiB'; Label = 'Size (GiB)'; Type = 'number'; Format = 'N2'; Sum = $true } @{ Key = 'SizeText'; Label = 'Size' } @{ Key = 'Kind'; Label = 'Kind'; Facet = $true } @{ Key = 'Sku'; Label = 'SKU'; Facet = $true } @{ Key = 'DataLake'; Label = 'Data Lake'; Facet = $true } @{ Key = 'NetworkAccess'; Label = 'Network access'; Facet = $true } @{ Key = 'Location'; Label = 'Region'; Facet = $true } @{ Key = 'SubscriptionName'; Label = 'Subscription'; Facet = $true } @{ Key = 'ResourceGroup'; Label = 'Resource group'; Facet = $true; Hidden = $true } @{ Key = 'Reason'; Label = 'Why not read'; Type = 'wide' } ) } if ($blobs.Count) { @{ Id = 'blobs'; Title = 'Largest blobs'; Note = "The $($blobs.Count) largest blobs across every container read."; Noun = 'blobs'; File = 'largest-blobs'; Rows = $blobs; Sort = @{ Key = 'SizeGiB'; Desc = $true } Columns = @( @{ Key = 'Name'; Label = 'Blob'; Type = 'wide' } @{ Key = 'StorageAccount'; Label = 'Storage account'; Facet = $true } @{ Key = 'Container'; Label = 'Container'; Facet = $true } @{ Key = 'SizeGiB'; Label = 'Size (GiB)'; Type = 'number'; Format = 'N2' } @{ Key = 'SizeText'; Label = 'Size' } @{ Key = 'AccessTier'; Label = 'Tier'; Type = 'badge'; Tones = $tierTones; Facet = $true } @{ Key = 'BlobType'; Label = 'Type'; Facet = $true } @{ Key = 'LastModified'; Label = 'Last changed'; Type = 'datetime' } ) } } ) $notices = @( @{ Tone = 'info'; Text = ('Read {0:N0} blob(s) in {1:N1} seconds ({2:N0} a second). Sizes are the blobs'' content length, in powers of 1,024; what Azure bills can differ (metadata, minimum sizes, early deletion).' -f $stats.Blobs, $stats.Elapsed.TotalSeconds, $stats.BlobsPerSecond) } if ($notRead) { @{ Tone = 'bad'; Text = "$notRead container(s) or account(s) couldn't be read in full - see Why not read." } } ) Write-AACHtmlReport -Path $Path -Title $Title -Subtitle 'Blob containers by size - access tiers, largest blobs, what couldn''t be read' -Fact $Detail -Tile $tiles -Chart $charts -Table $tables -Notice $notices } |