Private/Report/Get-TcReportExposure.ps1
|
function Get-TcReportExposure { <# .SYNOPSIS Computes what the limits of the tools touch in the sources, from the snapshot alone. .DESCRIPTION A value is empty when its table was not read, so a gap never reads as a zero. A zero is a real zero: the table was read and holds nothing. Each line says which sources it could not see, and its text says so too: it never concludes on what was not read. #> [CmdletBinding()] [OutputType([hashtable])] param( [Parameter(Mandatory)][System.Collections.IDictionary]$Snapshot, [Parameter(Mandatory)][System.Collections.IDictionary]$Catalog, [Parameter(Mandatory)][AllowEmptyCollection()][string[]]$Source, [Parameter(Mandatory)][System.Collections.IDictionary]$TableState, [Parameter(Mandatory)][AllowEmptyCollection()][object[]]$Measure, [Parameter(Mandatory)][System.Collections.IDictionary]$Identity, [Parameter(Mandatory)][System.Collections.IDictionary]$Name ) $tenants = $Snapshot['tenants'] $states = $TableState $measures = $Measure $rowsOf = { param([string]$Role, [string]$Table) if ($states[$Role][$Table]['state'] -in 'NotCollected', 'NotFullyRead') { return $null } , @(Get-TcProperty (Get-TcProperty (Get-TcProperty $tenants $Role) 'tables') $Table @()) } # Calls a block on the rows of a table, for each source. An unread table gives an empty value. $perSource = { param([string]$Table, [scriptblock]$Read) $values = [ordered]@{} foreach ($role in $Source) { $rows = & $rowsOf $role $Table $values[$role] = if ($null -eq $rows) { $null } else { & $Read $rows } } $values } $count = { param($Rows) @($Rows).Count } $largest = & $perSource 'mailboxUsage' { param($Rows) $sizes = @($Rows | ForEach-Object { Get-TcRowNumber $_ 'Size GB' }) if ($sizes.Count) { [math]::Round((($sizes | Measure-Object -Maximum).Maximum), 1) } else { 0 } } $bigSites = & $perSource 'siteUsage' { param($Rows) @($Rows | Where-Object { (Get-TcRowNumber $_ 'Storage GB') -ge 100 }).Count } $bigVolume = & $perSource 'siteUsage' { param($Rows) $sum = (@($Rows | ForEach-Object { Get-TcRowNumber $_ 'Storage GB' } | Where-Object { $_ -ge 100 }) | Measure-Object -Sum).Sum [math]::Round([double]$sum) } $duplicates = [ordered]@{} foreach ($role in $Source) { $line = $Identity[$role] $duplicates[$role] = if ($line -and $line['drafted']) { [int]$line['counts']['Duplicate'] } else { $null } } $fromMeasure = { param([string]$Label) $line = @($measures | Where-Object { $_['label'] -eq $Label }) | Select-Object -First 1 $values = [ordered]@{} foreach ($role in $Source) { $values[$role] = if ($line) { Get-TcProperty $line['values'] $role } else { $null } } $values } $values = [ordered]@{ largestMailbox = $largest largeSites = $bigSites likelyDuplicates = $duplicates bookings = & $perSource 'mailboxes' { param($Rows) @($Rows | Where-Object { (Get-TcProperty $_ 'Type') -eq 'SchedulingMailbox' }).Count } sharedChannels = & $fromMeasure 'Shared channels' plannerPlans = & $perSource 'plannerPlans' $count microsoft365Groups = & $perSource 'microsoft365Groups' $count archives = & $perSource 'mailboxes' { param($Rows) @($Rows | Where-Object { Test-TcRowTrue $_ 'Archive' }).Count } holds = & $perSource 'mailboxes' { param($Rows) @($Rows | Where-Object { (Test-TcRowTrue $_ 'Litigation hold') -or (Get-TcProperty $_ 'Holds') }).Count } frontline = & $perSource 'licenses' { param($Rows) $seats = @($Rows | Where-Object { [string](Get-TcProperty $_ 'SKU') -match '(?i)KIOSK|DESKLESS|SPE_F1|M365_F1|M365_F3|SPE_F3' } | ForEach-Object { Get-TcRowNumber $_ 'Consumed' }) [int](($seats | Measure-Object -Sum).Sum) } userMailboxes = & $fromMeasure 'User mailboxes' sharedMailboxes = & $fromMeasure 'Shared mailboxes' oneDriveAccounts = & $fromMeasure 'OneDrive accounts' sites = & $fromMeasure 'Sites' teams = & $fromMeasure 'Teams' privateChannels = & $fromMeasure 'Private channels' autopilotDevices = & $fromMeasure 'Autopilot devices' } $facts = foreach ($line in $Catalog['exposure']) { $id = $line['id'] $mine = $values[$id] $read = @($Source | Where-Object { $null -ne $mine[$_] }) $unread = @($Source | Where-Object { $null -eq $mine[$_] }) $names = @($unread | ForEach-Object { $Name[$_] }) -join ', ' $numbers = @($read | ForEach-Object { [double]$mine[$_] }) $highest = if ($numbers.Count) { ($numbers | Measure-Object -Maximum).Maximum } else { 0 } $total = if ($numbers.Count) { ($numbers | Measure-Object -Sum).Sum } else { 0 } if (-not $read.Count) { $tone = 'gap'; $text = 'Not read: nothing can be said here.' } elseif ($total -gt 0) { $volume = (@($read | ForEach-Object { [double]$bigVolume[$_] }) | Measure-Object -Sum).Sum $tone = $line['someTone'] $text = ([string]$line['some']).Replace('{hours}', [string][math]::Round($highest / 1.5)).Replace('{volume}', [string][math]::Round([double]$volume)) if ($unread.Count) { $text += " Not read: $names, so this is a minimum." } } elseif ($unread.Count) { $tone = 'gap'; $text = "None in what was read. Not read: $names." } else { $tone = $line['noneTone']; $text = $line['none'] } [ordered]@{ id = $id; label = $line['label']; unit = $line['unit']; source = $line['source']; values = $mine; unread = $unread; tone = $tone; text = $text } } @{ Values = $values; Facts = @($facts) } } |