<# .SYNOPSIS Exchange on-prem inventarisatie t.b.v. migratie – rapport in Word (fallback HTML) + CSV's Inclusief: werkelijke benodigde mailboxruimte (som TotalItemSize van on-prem mailboxen, excl. whitespace). .PARAMETER OutputFolder Doelmap voor outputbestanden. Default: .\Exchange-Inventory- .PARAMETER TopMailboxCount Aantal grootste mailboxen in rapport (default 30) .PARAMETER IncludeCSVs Exporteer detail-CSV's (default $true) .PARAMETER ProgressThrottleMs Minimale interval (ms) tussen progress updates in intensieve loops (default 75) .NOTES - Uitvoeren in Exchange Management Shell met passende RBAC. - Getest op Exchange 2013/2016/2019. Secties zonder cmdlets worden overgeslagen. - Gebruik: # Exchange Management Shell .\Exchange-Inventory.ps1 -OutputFolder "D:\Reports\" -TopMailboxCount 50 -IncludeCSVs $true # Optioneel trager/sneller progress-throttle: .\Exchange-Inventory.ps1 -ProgressThrottleMs 50 #> [CmdletBinding()] param( [string]$OutputFolder = (Join-Path -Path (Get-Location) -ChildPath ("Exchange-Inventory-" + (Get-Date -Format "yyyyMMdd-HHmm"))), [int]$TopMailboxCount = 30, [bool]$IncludeCSVs = $true, [int]$ProgressThrottleMs = 75 ) # ------------------------- Helpers: Progress / Output / Word/HTML ------------------------- function NowTag { (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") } $sections = @( "Voorbereiding","Organisatie & Servers","Client Access & VDirs","POP/IMAP", "Certificaten","Domeinen & Policies","Transport & Mailflow","Databases & DAG's", "Database Copies","Mailboxen (on-prem)","Mailbox Stats","Mailbox Join", "Benodigde mailboxruimte","Top Mailboxen","Retention & Compliance","Mobile Device Policies", "Addressing (AL/GAL/OAB)","Public Folders","Throttling Policies", "Hybrid/Federation/OAuth","Mail Queues","Export CSV","Afronden" ) [int]$sectionIndex = 0 [int]$totalSections = $sections.Count function Show-Progress([string]$status, [int]$percent = -1) { $activity = "Exchange Inventory – $($sections[$sectionIndex])" if($percent -lt 0){ Write-Progress -Activity $activity -Status $status } else { Write-Progress -Activity $activity -Status $status -PercentComplete ([Math]::Min(100,[Math]::Max(0,$percent))) } } function Next-Section { if($sectionIndex -lt $totalSections-1){ $sectionIndex++ } $msg = "[{0}] Start sectie: {1} ({2}/{3})" -f (NowTag), $sections[$sectionIndex], ($sectionIndex+1), $totalSections Write-Host $msg Write-Progress -Activity ("Exchange Inventory – {0}" -f $sections[$sectionIndex]) -Status "Bezig..." -PercentComplete ([int](($sectionIndex+1)/$totalSections*100)) } # Outputmap New-Item -ItemType Directory -Path $OutputFolder -Force | Out-Null $ReportDocx = Join-Path $OutputFolder "Exchange-Inventory.docx" $ReportHtml = Join-Path $OutputFolder "Exchange-Inventory.html" # Word COM of HTML fallback $script:Word = $null $script:Doc = $null $script:UseHtml = $false $script:HtmlSb = New-Object System.Text.StringBuilder function Start-Report { try { $script:Word = New-Object -ComObject Word.Application $script:Word.Visible = $false $script:Doc = $script:Word.Documents.Add() Add-Heading "Exchange Inventarisatie" 1 Add-Paragraph ("Gegenereerd: " + (Get-Date).ToString("yyyy-MM-dd HH:mm")) } catch { $script:UseHtml = $true [void]$script:HtmlSb.AppendLine("Exchange Inventarisatie") [void]$script:HtmlSb.AppendLine("") [void]$script:HtmlSb.AppendLine("

Exchange Inventarisatie

") [void]$script:HtmlSb.AppendLine("

Gegenereerd: " + (Get-Date).ToString("yyyy-MM-dd HH:mm") + "

") } } function Add-Heading([string]$Text, [int]$Level=2) { if($script:UseHtml){ [void]$script:HtmlSb.AppendLine("$Text") } else { $range = $script:Doc.Range() $range.Collapse(0) | Out-Null $range.Text = "$Text`r" $style = switch($Level){ 1 {'Heading 1'} 2 {'Heading 2'} 3 {'Heading 3'} default {'Heading 2'}} $range.set_Style($style) $range.InsertParagraphAfter() | Out-Null } } function Add-Paragraph([string]$Text){ if($script:UseHtml){ $enc = [System.Web.HttpUtility]::HtmlEncode($Text) [void]$script:HtmlSb.AppendLine("

$enc

") } else { $range = $script:Doc.Range() $range.Collapse(0) | Out-Null $range.Text = $Text $range.InsertParagraphAfter() | Out-Null } } function Convert-ToCellText([object]$v){ if($null -eq $v){ return "" } if($v -is [DateTime]){ return $v.ToString("yyyy-MM-dd HH:mm") } if($v -is [string]){ return $v } if($v -is [System.Collections.IEnumerable] -and -not ($v -is [string])){ $items = @() foreach($i in $v){ $items += [string]$i } return ($items -join ", ") } return [string]$v } function Add-Table([object]$Objects, [string[]]$PropOrder, [string]$Title){ if($null -eq $Objects){ Add-Paragraph ("{0}: geen data." -f $Title) return } $arr = @() if($Objects -is [System.Collections.IEnumerable] -and -not ($Objects -is [string])){ foreach($o in $Objects){ $arr += $o } } else { $arr = @($Objects) } if($arr.Count -eq 0){ Add-Paragraph ("{0}: geen data." -f $Title) return } Add-Heading $Title 3 if($script:UseHtml){ [void]$script:HtmlSb.AppendLine("") foreach($p in $PropOrder){ [void]$script:HtmlSb.AppendLine("") } [void]$script:HtmlSb.AppendLine("") foreach($o in $arr){ [void]$script:HtmlSb.AppendLine("") foreach($p in $PropOrder){ $cell = Convert-ToCellText ($o.$p) [void]$script:HtmlSb.AppendLine("") } [void]$script:HtmlSb.AppendLine("") } [void]$script:HtmlSb.AppendLine("
$([System.Web.HttpUtility]::HtmlEncode($p))
$([System.Web.HttpUtility]::HtmlEncode($cell))
") } else { $rows = $arr.Count $cols = $PropOrder.Count $range = $script:Doc.Range() $table = $script:Doc.Tables.Add($range, [Math]::Max(1,$rows)+1, $cols) for($c=1; $c -le $cols; $c++){ $table.Cell(1,$c).Range.Text = $PropOrder[$c-1] } $table.Rows.Item(1).Range.Bold = $true for($r=0; $r -lt $rows; $r++){ for($c=0; $c -lt $cols; $c++){ $prop = $PropOrder[$c] $cell = Convert-ToCellText ($arr[$r].$prop) $table.Cell($r+2, $c+1).Range.Text = $cell } } $table.AutoFitBehavior(2) | Out-Null $range.InsertParagraphAfter() | Out-Null } } function End-Report { if($script:UseHtml){ [void]$script:HtmlSb.AppendLine("") $script:HtmlSb.ToString() | Out-File -LiteralPath $ReportHtml -Encoding UTF8 Write-Host ("[{0}] HTML-rapport: {1}" -f (NowTag), $ReportHtml) } else { $wdFormatXMLDocument = 12 $script:Doc.SaveAs([ref]$ReportDocx, [ref]$wdFormatXMLDocument) $script:Doc.Close(); $script:Word.Quit() Write-Host ("[{0}] Word-rapport: {1}" -f (NowTag), $ReportDocx) } Write-Progress -Activity "Exchange Inventory – Gereed" -Completed } function Export-IfEnabled($data, $name){ if($IncludeCSVs -and $data){ $file = Join-Path $OutputFolder ($name + ".csv") $data | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $file Write-Host ("[{0}] CSV geexporteerd: {1}" -f (NowTag), $file) } } # ------------------------- Start ------------------------- Write-Host ("[{0}] Inventarisatie gestart..." -f (NowTag)) Show-Progress "Initialisatie..." 1 Start-Report # 1) Organisatie & Servers Next-Section Show-Progress "Get-OrganizationConfig / Get-ExchangeServer..." try { $org = Get-OrganizationConfig -ErrorAction SilentlyContinue } catch { $org = $null } $servers = Get-ExchangeServer -ErrorAction SilentlyContinue | Sort-Object Name $serversOut = $servers | Select-Object Name, Edition, AdminDisplayVersion, IsClientAccessServer, IsMailboxServer, IsHubTransportServer, ServerRole, Site, Fqdn, *Build* Add-Heading "Organisatie & Servers" 2 if($org){ $orgRow = [pscustomobject]@{ Name = $org.Name DomainName = $org.OriginatingServer AddressBookPolicyRouting = $org.AddressBookPolicyRoutingEnabled OAuth2ClientProfile = $org.OAuth2ClientProfileEnabled PublicFoldersEnabled = $org.PublicFoldersEnabled IsDehydrated = $org.IsDehydrated } Add-Table $orgRow @("Name","DomainName","AddressBookPolicyRouting","OAuth2ClientProfile","PublicFoldersEnabled","IsDehydrated") "Organisatieconfig" } Add-Table $serversOut @("Name","Edition","AdminDisplayVersion","ServerRole","Site","Fqdn") "Exchange Servers" # 2) Client Access & Virtual Directories Next-Section Show-Progress "Virtual directories ophalen..." try { $owa = Get-OWAVirtualDirectory -ErrorAction Stop | Select-Object Server,Name,InternalUrl,ExternalUrl,BasicAuthentication } catch { $owa = Get-OWAVirtualDirectory -ErrorAction SilentlyContinue | Select-Object Server,Name,InternalUrl,ExternalUrl } $ecp = Get-EcpVirtualDirectory -ErrorAction SilentlyContinue | Select-Object Server,Name,InternalUrl,ExternalUrl $ews = Get-WebServicesVirtualDirectory -ErrorAction SilentlyContinue | Select-Object Server,Name,InternalUrl,ExternalUrl $oabvd= Get-OabVirtualDirectory -ErrorAction SilentlyContinue | Select-Object Server,Name,InternalUrl,ExternalUrl $autod= Get-AutodiscoverVirtualDirectory -ErrorAction SilentlyContinue | Select-Object Server,Name,InternalUrl,ExternalUrl $as = Get-ActiveSyncVirtualDirectory -ErrorAction SilentlyContinue | Select-Object Server,Name,InternalUrl,ExternalUrl $mapi= Get-MapiVirtualDirectory -ErrorAction SilentlyContinue | Select-Object Server,Name,InternalUrl,ExternalUrl, IISAuthenticationMethods $psvd= Get-PowerShellVirtualDirectory -ErrorAction SilentlyContinue | Select-Object Server,Name,InternalUrl,ExternalUrl Add-Table $owa @("Server","Name","InternalUrl","ExternalUrl") "OWA" Add-Table $ecp @("Server","Name","InternalUrl","ExternalUrl") "ECP" Add-Table $ews @("Server","Name","InternalUrl","ExternalUrl") "EWS" Add-Table $oabvd@("Server","Name","InternalUrl","ExternalUrl") "OAB" Add-Table $autod@("Server","Name","InternalUrl","ExternalUrl") "Autodiscover" Add-Table $as @("Server","Name","InternalUrl","ExternalUrl") "ActiveSync" Add-Table $mapi @("Server","Name","InternalUrl","ExternalUrl","IISAuthenticationMethods") "MAPI/HTTP" Add-Table $psvd @("Server","Name","InternalUrl","ExternalUrl") "Remote PowerShell" # 3) POP/IMAP Next-Section Show-Progress "POP/IMAP settings..." $pop = Get-PopSettings -ErrorAction SilentlyContinue | Select-Object Server, LoginType, X509CertificateName, SSLBindings, UnencryptedOrTLSBindings, ProtocolLogEnabled $imap= Get-ImapSettings -ErrorAction SilentlyContinue | Select-Object Server, LoginType, X509CertificateName, SSLBindings, UnencryptedOrTLSBindings, ProtocolLogEnabled Add-Table $pop @("Server","LoginType","X509CertificateName","SSLBindings","UnencryptedOrTLSBindings","ProtocolLogEnabled") "POP" Add-Table $imap @("Server","LoginType","X509CertificateName","SSLBindings","UnencryptedOrTLSBindings","ProtocolLogEnabled") "IMAP" # 4) Certificaten Next-Section Show-Progress "Exchange certificaten..." $certs = @() Get-ExchangeCertificate -ErrorAction SilentlyContinue | ForEach-Object { $days = ($_.NotAfter - (Get-Date)).TotalDays $status = if($days -lt 0){"Verlopen"} elseif($days -lt 90){"Verloopt <90d"} else {"OK"} $certs += [pscustomobject]@{ Thumbprint = $_.Thumbprint Services = ($_.Services -join ",") Subject = $_.Subject FriendlyName = $_.FriendlyName NotBefore = $_.NotBefore NotAfter = $_.NotAfter Status = $status } } Add-Table $certs @("Thumbprint","Services","Subject","FriendlyName","NotBefore","NotAfter","Status") "Exchange Certificates" # 5) Domeinen & Policies Next-Section Show-Progress "Accepted/Remote domains, EAP..." $accDom = Get-AcceptedDomain -ErrorAction SilentlyContinue | Select-Object Name,DomainName,DomainType,Default $remDom = Get-RemoteDomain -ErrorAction SilentlyContinue | Select-Object Name,DomainName,AllowedOOFType,AutoReplyEnabled,AutoForwardEnabled $emailp = Get-EmailAddressPolicy -ErrorAction SilentlyContinue | Select-Object Name,Priority,EnabledPrimaryAddressTemplate,RecipientFilter Add-Table $accDom @("Name","DomainName","DomainType","Default") "Accepted Domains" Add-Table $remDom @("Name","DomainName","AllowedOOFType","AutoReplyEnabled","AutoForwardEnabled") "Remote Domains" Add-Table $emailp @("Name","Priority","EnabledPrimaryAddressTemplate","RecipientFilter") "Email Address Policies" # 6) Transport & Mailflow Next-Section Show-Progress "Transportconfig, rules, connectors..." try { $tconf = Get-TransportConfig -ErrorAction Stop } catch { $tconf = $null } if($tconf){ $trow = [pscustomobject]@{ TLSReceiveDomainSecureList = ($tconf.TLSReceiveDomainSecureList -join ",") TlsSendDomainSecureList = ($tconf.TLSSendDomainSecureList -join ",") ExternalDNSAdapterEnabled = $tconf.ExternalDNSAdapterEnabled InternalSMTPServers = ($tconf.InternalSMTPServers -join ",") MaxSendSize = $tconf.MaxSendSize MaxReceiveSize = $tconf.MaxReceiveSize } Add-Table $trow @("TLSReceiveDomainSecureList","TlsSendDomainSecureList","ExternalDNSAdapterEnabled","InternalSMTPServers","MaxSendSize","MaxReceiveSize") "Transport Config" } $trules = Get-TransportRule -ErrorAction SilentlyContinue | Select-Object Name,Priority,State,Mode Add-Table $trules @("Name","Priority","State","Mode") "Transport Rules" $send = Get-SendConnector -ErrorAction SilentlyContinue | Select-Object Name,AddressSpaces,Enabled,SmartHosts,UseExternalDNSServersEnabled,SourceTransportServers $recv = Get-ReceiveConnector -ErrorAction SilentlyContinue | Select-Object Server,Name,TransportRole,Bindings,RemoteIPRanges,AuthMechanism,TlsDomainCapabilities,PermissionGroups Add-Table $send @("Name","AddressSpaces","Enabled","SmartHosts","UseExternalDNSServersEnabled","SourceTransportServers") "Send Connectors" Add-Table $recv @("Server","Name","TransportRole","Bindings","RemoteIPRanges","AuthMechanism","TlsDomainCapabilities","PermissionGroups") "Receive Connectors" Export-IfEnabled $send "SendConnectors" Export-IfEnabled $recv "ReceiveConnectors" # 7) Databases & DAG's Next-Section Show-Progress "DAG's, DAG-netwerken, databases..." $dag = Get-DatabaseAvailabilityGroup -ErrorAction SilentlyContinue | Select-Object Name,WitnessServer,WitnessDirectory,OperationalServers,Servers,AutoDagTotalNumberOfServers Add-Table $dag @("Name","WitnessServer","WitnessDirectory","OperationalServers","Servers","AutoDagTotalNumberOfServers") "Database Availability Groups" $dagNet = Get-DatabaseAvailabilityGroupNetwork -ErrorAction SilentlyContinue | Select-Object Name,Identity,Description,Subnets,ReplicationEnabled Add-Table $dagNet @("Name","Identity","Description","Subnets","ReplicationEnabled") "DAG Networks" $dbs = Get-MailboxDatabase -Status -ErrorAction SilentlyContinue | Sort-Object Name | Select-Object Name,Server,EDBFilePath,LogFolderPath,Mounted,CircularLoggingEnabled,DatabaseSize,AvailableNewMailboxSpace,Recovery Add-Table $dbs @("Name","Server","EDBFilePath","LogFolderPath","Mounted","CircularLoggingEnabled","DatabaseSize","AvailableNewMailboxSpace","Recovery") "Mailbox Databases" # 8) Database Copies Next-Section Show-Progress "Database copies ophalen... 0%" $dbCopies = @() if($dbs){ $dbCount = $dbs.Count $lastTick = Get-Date for($i=0; $i -lt $dbCount; $i++){ $db = $dbs[$i] $copies = Get-MailboxDatabaseCopyStatus $db.Name -ErrorAction SilentlyContinue $m = Get-MailboxDatabase $db.Name -ErrorAction SilentlyContinue foreach($c in $copies){ $mbxSrv = $c.Name.Split("\")[-1] $dbCopies += [pscustomobject]@{ Database = $db.Name MailboxServer = $mbxSrv Status = $c.Status ContentIndexState = $c.ContentIndexState CopyQueueLength = $c.CopyQueueLength ReplayQueueLength = $c.ReplayQueueLength LastInspectedLogTime = $c.LastInspectedLogTime ActivationPreference = ($m.ActivationPreference | Where-Object {$_.Key -eq $mbxSrv}).Value } } if(((Get-Date) - $lastTick).TotalMilliseconds -ge $ProgressThrottleMs){ $pct = [int](($i+1)/$dbCount*100) Show-Progress ("Database copies... {0}/{1}" -f ($i+1), $dbCount) $pct $lastTick = Get-Date } } } Add-Table $dbCopies @("Database","MailboxServer","Status","ContentIndexState","CopyQueueLength","ReplayQueueLength","LastInspectedLogTime","ActivationPreference") "Database Copies" # 9) Mailboxen (on-prem) – filter Remote* en vereis Database Next-Section Show-Progress "Mailboxen (on-prem) ophalen..." $mailboxes = Get-Mailbox -ResultSize Unlimited -ErrorAction SilentlyContinue | Where-Object { $_.RecipientTypeDetails -notmatch "^Remote" -and $_.Database } | Select-Object DisplayName,PrimarySmtpAddress,RecipientTypeDetails,Database,ArchiveStatus,RetentionPolicy, LitigationHoldEnabled,WhenCreated Add-Heading "Mailboxen (on-premises)" 2 # 10) Mailbox Stats (per database, voorkomt prompt om Identity) Next-Section Show-Progress "Mailbox statistics per database..." $mbStats = @() $statsDbs = ($mailboxes.Database | Sort-Object -Unique) $sdCount = ($statsDbs | Measure-Object).Count if($sdCount -gt 0){ $idx=0; $lastTick = Get-Date foreach($dbName in $statsDbs){ try{ Get-MailboxStatistics -Database $dbName -ErrorAction SilentlyContinue | Select-Object DisplayName,Database,TotalItemSize,ItemCount,LastLogonTime,LastLoggedOnUserAccount | ForEach-Object { $mbStats += $_ } } catch {} $idx++ if(((Get-Date) - $lastTick).TotalMilliseconds -ge $ProgressThrottleMs){ Show-Progress ("Mailbox statistics... {0}/{1} databases" -f $idx,$sdCount) ([int]($idx/$sdCount*100)) $lastTick = Get-Date } } } # 11) Mailbox Join Next-Section Show-Progress "Mailbox eigenschappen koppelen..." $sizeLookup = @{} foreach($s in $mbStats){ $sizeLookup[$s.DisplayName] = $s } $mbForTop = @() if($mailboxes){ $mbCount = $mailboxes.Count $k=0; $lastTick = Get-Date foreach($m in $mailboxes){ $s = $sizeLookup[$m.DisplayName] $mbForTop += [pscustomobject]@{ DisplayName = $m.DisplayName PrimarySmtpAddress = $m.PrimarySmtpAddress RecipientTypeDetails = $m.RecipientTypeDetails Database = $m.Database ArchiveStatus = $m.ArchiveStatus RetentionPolicy = $m.RetentionPolicy LitigationHoldEnabled= $m.LitigationHoldEnabled TotalItemSize = if($s){ $s.TotalItemSize } else { $null } ItemCount = if($s){ $s.ItemCount } else { $null } LastLogonTime = if($s){ $s.LastLogonTime } else { $null } } $k++ if($mbCount -gt 0 -and ((Get-Date) - $lastTick).TotalMilliseconds -ge $ProgressThrottleMs){ Show-Progress ("Join mailboxen... {0}/{1}" -f $k,$mbCount) ([int]($k/$mbCount*100)) $lastTick = Get-Date } } } # 12) Benodigde mailboxruimte (excl. whitespace) – totaal en per database Next-Section Show-Progress "Benodigde mailboxruimte berekenen..." # Helper: haal bytes op uit TotalItemSize veilig function Get-BytesFromSize($tis){ try { if($null -eq $tis){ return 0 } if($tis.Value -and ($tis.Value.PSObject.Properties.Name -contains "ToBytes")){ return [int64]$tis.Value.ToBytes() } # fallback op string parse (alleen indien nodig) $s = [string]$tis if($s -match "([\d\.,]+)\s*(KB|MB|GB|TB)"){ $num = ($matches[1] -replace '\.','.' -replace ',','.') switch($matches[2].ToUpper()){ "KB" { return [double]$num * 1KB } "MB" { return [double]$num * 1MB } "GB" { return [double]$num * 1GB } "TB" { return [double]$num * 1TB } } } } catch { } return 0 } # Totaal bytes $totalBytes = 0 foreach($row in $mbForTop){ $totalBytes += [int64](Get-BytesFromSize $row.TotalItemSize) } $totalGB = [math]::Round($totalBytes / 1GB, 2) # Per database aggregatie $byDb = @() $groups = $mbForTop | Group-Object -Property Database foreach($g in $groups){ $b = 0 foreach($m in $g.Group){ $b += [int64](Get-BytesFromSize $m.TotalItemSize) } $byDb += [pscustomobject]@{ Database = $g.Name MailboxCount = $g.Count TotalGB = [math]::Round($b / 1GB, 2) } } $totalsRow = [pscustomobject]@{ TotaalMailboxen = ($mbForTop | Measure-Object).Count TotaleInhoudGB = $totalGB } Add-Table $totalsRow @("TotaalMailboxen","TotaleInhoudGB") "Werkelijke benodigde mailboxruimte (excl. whitespace)" Add-Table ($byDb | Sort-Object -Property Database) @("Database","MailboxCount","TotalGB") "Benodigde mailboxruimte per database" Export-IfEnabled ($mbForTop | Select-Object DisplayName,PrimarySmtpAddress,Database,TotalItemSize,ItemCount) "MailboxContentSizes" Export-IfEnabled ($byDb | Sort-Object Database) "MailboxContentPerDatabase" # 13) Samenvatting + Top Mailboxen Next-Section Show-Progress "Samenvatting mailboxen..." $sum = [pscustomobject]@{ TotaalMailboxen = ($mailboxes | Measure-Object).Count MetArchive = ($mailboxes | Where-Object {$_.ArchiveStatus -ne "None"} | Measure-Object).Count LitigationHold = ($mailboxes | Where-Object {$_.LitigationHoldEnabled} | Measure-Object).Count UniekeDatabases = (($mailboxes.Database | Sort-Object -Unique) -join ", ") } Add-Table $sum @("TotaalMailboxen","MetArchive","LitigationHold","UniekeDatabases") "Samenvatting (on-prem)" function Parse-Size([string]$val){ if(-not $val){ return 0 } if($val -match "([\d\.,]+)\s*(KB|MB|GB|TB)"){ $num = ($matches[1] -replace '\.','.' -replace ',','.') switch($matches[2].ToUpper()){ "KB" { return [double]$num * 1KB } "MB" { return [double]$num * 1MB } "GB" { return [double]$num * 1GB } "TB" { return [double]$num * 1TB } } } return 0 } Next-Section Show-Progress "Bepalen top mailboxen..." $topSrc = $mbForTop | Where-Object { $_.TotalItemSize } $topCount = ($topSrc | Measure-Object).Count $mbTop = @() if($topCount -gt 0){ $sorted = $topSrc | Sort-Object @{Expression={ Parse-Size ([string]$_.TotalItemSize) };Descending=$true} $chunks = 10 for($c=1;$c -le $chunks;$c++){ Start-Sleep -Milliseconds ([Math]::Min($ProgressThrottleMs,150)) Show-Progress ("Sorteren... stap {0}/{1}" -f $c,$chunks) ([int]($c/$chunks*100)) } $mbTop = $sorted | Select-Object -First $TopMailboxCount } Add-Table $mbTop @("DisplayName","PrimarySmtpAddress","Database","TotalItemSize","ItemCount","LastLogonTime","ArchiveStatus","RetentionPolicy","LitigationHoldEnabled") ("Top " + $TopMailboxCount + " grootste mailboxen (on-prem)") Export-IfEnabled $mbForTop "AllMailboxes" # on-prem only Export-IfEnabled $mbTop "TopMailboxes" # on-prem only # 14) Retention & Compliance Next-Section Show-Progress "Retention policies & tags..." $rets = Get-RetentionPolicy -ErrorAction SilentlyContinue | Select-Object Name,RetentionPolicyTagLinks,IsDefault $tags = Get-RetentionPolicyTag -ErrorAction SilentlyContinue | Select-Object Name,Type,RetentionAction,AgeLimitForRetention,RetentionEnabled $holds= Get-Mailbox -ResultSize Unlimited -Filter "LitigationHoldEnabled -eq 'True'" -ErrorAction SilentlyContinue | Select-Object DisplayName,PrimarySmtpAddress,WhenCreated Add-Table $rets @("Name","RetentionPolicyTagLinks","IsDefault") "Retention Policies" Add-Table $tags @("Name","Type","RetentionAction","AgeLimitForRetention","RetentionEnabled") "Retention Tags" Add-Table $holds @("DisplayName","PrimarySmtpAddress","WhenCreated") "Mailboxen met Litigation Hold" # 15) Mobile Device Policies Next-Section Show-Progress "Mobile Device policies..." $mdpol = Get-MobileDeviceMailboxPolicy -ErrorAction SilentlyContinue | Select-Object Name,AllowNonProvisionableDevices,AlphanumericDevicePasswordRequired,MaxInactivityTimeDeviceLock,MinDevicePasswordLength,PasswordRecoveryEnabled Add-Table $mdpol @("Name","AllowNonProvisionableDevices","AlphanumericDevicePasswordRequired","MaxInactivityTimeDeviceLock","MinDevicePasswordLength","PasswordRecoveryEnabled") "Mobile Device Policies" # 16) Addressing (AL/GAL/OAB) Next-Section Show-Progress "Address lists, GAL, OAB..." $als = Get-AddressList -ErrorAction SilentlyContinue | Select-Object Name,RecipientFilter $gals= Get-GlobalAddressList -ErrorAction SilentlyContinue | Select-Object Name,RecipientFilter $oab = Get-OfflineAddressBook -ErrorAction SilentlyContinue | Select-Object Name,AddressLists,IsDefault Add-Table $als @("Name","RecipientFilter") "Address Lists" Add-Table $gals @("Name","RecipientFilter") "Global Address Lists" Add-Table $oab @("Name","AddressLists","IsDefault") "Offline Address Books" # 17) Public Folders Next-Section Show-Progress "Public Folders..." try { $pfmailboxes = Get-Mailbox -PublicFolder -ErrorAction SilentlyContinue | Select-Object Name,Database,IsRootPublicFolderMailbox if($pfmailboxes){ Add-Table $pfmailboxes @("Name","Database","IsRootPublicFolderMailbox") "Public Folder Mailboxes (Modern PFs)" } } catch { } try { $pfdb = Get-PublicFolderDatabase -ErrorAction SilentlyContinue | Select-Object Name,Server,DeletedItemRetention,DistinguishedName if($pfdb){ Add-Table $pfdb @("Name","Server","DeletedItemRetention","DistinguishedName") "Public Folder Database (Legacy)" } } catch { } # 18) Throttling Policies Next-Section Show-Progress "Throttling policies..." $throt = Get-ThrottlingPolicy -ErrorAction SilentlyContinue | Select-Object Name,IsDefault,GlobalThrottlingPolicy Add-Table $throt @("Name","IsDefault","GlobalThrottlingPolicy") "Throttling Policies" # 19) Hybrid / Federation / OAuth Next-Section Show-Progress "Hybrid/Federation/OAuth..." try { $fed = Get-FederationTrust -ErrorAction SilentlyContinue | Select-Object Name,ApplicationUri,TokenIssuerUri,OrgContact } catch { $fed = $null } try { $auth= Get-AuthConfig -ErrorAction SilentlyContinue | Select-Object ServiceName,Issuer,EvoStsMetadataUrl,CurrentCertificateThumbprint,PreviousCertificateThumbprint } catch { $auth = $null } try { $hyb = Get-HybridConfiguration -ErrorAction SilentlyContinue | Select-Object Features,Domains,ClientAccessServers,ReceivingTransportServers,SendingTransportServers } catch { $hyb = $null } if($fed){ Add-Table $fed @("Name","ApplicationUri","TokenIssuerUri","OrgContact") "Federation Trust" } if($auth){ Add-Table $auth @("ServiceName","Issuer","EvoStsMetadataUrl","CurrentCertificateThumbprint","PreviousCertificateThumbprint") "OAuth Config" } if($hyb){ Add-Table $hyb @("Features","Domains","ClientAccessServers","ReceivingTransportServers","SendingTransportServers") "Hybrid Configuration" } # 20) Mail Queues Next-Section Show-Progress "Mail queues per server..." $qdata = foreach($sv in $servers){ try{ $qs = Get-Queue -Server $sv.Name -ErrorAction Stop | Measure-Object -Property MessageCount -Sum [pscustomobject]@{ Server=$sv.Name; TotalQueued=$qs.Sum } } catch { [pscustomobject]@{ Server=$sv.Name; TotalQueued="N/A" } } } Add-Table $qdata @("Server","TotalQueued") "Queue per server" # 21) CSV Export Next-Section Show-Progress "CSV export..." Export-IfEnabled $dbs "MailboxDatabases" Export-IfEnabled $serversOut "Servers" Export-IfEnabled $dbCopies "DatabaseCopies" # 22) Afronden Next-Section Show-Progress "Rapport opslaan..." End-Report Write-Host ("[{0}] Inventarisatie gereed. Map: {1}" -f (NowTag), $OutputFolder)