Private/Collect/Get-TcOtherServiceCollector.ps1
|
function Get-TcOtherServiceCollector { <# .SYNOPSIS Lists the other services collectors, in the order they run. #> [CmdletBinding()] [OutputType([hashtable])] param() @( @{ Id = 'AUT-01' Workload = 'Other services' Title = 'Microsoft 365 activity per user and report privacy' Service = 'Graph' Collect = { Invoke-TcCollectorPart 'Report concealment setting' { $s = Get-TcGraphObject 'v1.0/admin/reportSettings'; Add-TcTable 'reportSettings' @([ordered]@{ 'Setting' = 'Names concealed in reports'; 'Value' = [bool](Get-TcProperty $s 'displayConcealedNames') }) } Add-TcTable 'userActivity' @(foreach ($r in Get-TcUsageReport 'getOffice365ActiveUserDetail' | Where-Object { (Get-TcProperty $_ 'Is Deleted') -ne 'True' }) { [ordered]@{ 'UPN' = (Get-TcProperty $r 'User Principal Name') 'Name' = (Get-TcProperty $r 'Display Name') 'Exchange' = (Get-TcProperty $r 'Exchange Last Activity Date') 'OneDrive' = (Get-TcProperty $r 'OneDrive Last Activity Date') 'SharePoint' = (Get-TcProperty $r 'SharePoint Last Activity Date') 'Teams' = (Get-TcProperty $r 'Teams Last Activity Date') 'Viva Engage' = (Get-TcProperty $r 'Yammer Last Activity Date') 'Products' = (Get-TcProperty $r 'Assigned Products') } }) } } @{ Id = 'AUT-02' Workload = 'Other services' Title = 'Microsoft Secure Score and recommended actions' Service = 'Graph' Collect = { $s = @((Get-TcProperty (Get-TcGraphObject "v1.0/security/secureScores?`$top=1") 'value')) | Select-Object -First 1 if (-not $s) { Add-TcCollectorNote 'No Secure Score has been calculated for this tenant.'; return } Add-TcTable 'secureScore' @([ordered]@{ 'Setting' = 'Current score'; 'Value' = [math]::Round([double](Get-TcProperty $s 'currentScore'), 1) }, [ordered]@{ 'Setting' = 'Maximum score'; 'Value' = [math]::Round([double](Get-TcProperty $s 'maxScore'), 1) }, [ordered]@{ 'Setting' = 'Percentage'; 'Value' = [math]::Round(100 * [double](Get-TcProperty $s 'currentScore') / [math]::Max(1, [double](Get-TcProperty $s 'maxScore'))) }, [ordered]@{ 'Setting' = 'Date'; 'Value' = (Format-TcValue (Get-TcProperty $s 'createdDateTime')) }) $profiles = @{} try { foreach ($p in Get-TcGraphList "v1.0/security/secureScoreControlProfiles?`$top=200") { $profiles[[string](Get-TcProperty $p 'id')] = $p } } catch { Add-TcCollectorWarning 'Control profiles' $_ } Add-TcTable 'secureScoreActions' @(@(foreach ($c in @((Get-TcProperty $s 'controlScores'))) { $p = $profiles[[string](Get-TcProperty $c 'controlName')]; $max = [double](Get-TcProperty $p 'maxScore' 0) if ($max -and [double](Get-TcProperty $c 'score') -ge $max) { continue } [ordered]@{ 'Control' = $(if ($p) { (Get-TcProperty $p 'title') } else { (Get-TcProperty $c 'controlName') }) 'Category' = (Get-TcProperty $c 'controlCategory') 'Score' = [math]::Round([double](Get-TcProperty $c 'score'), 1) 'Maximum' = $max 'Possible gain' = [math]::Round($max - [double](Get-TcProperty $c 'score'), 1) 'Cost' = (Get-TcProperty $p 'implementationCost') 'User impact' = (Get-TcProperty $p 'userImpact') 'Service' = (Get-TcProperty $p 'service') } }) | Sort-Object { - (Get-TcProperty $_ 'Possible gain') }) } } @{ Id = 'AUT-03' Workload = 'Other services' Title = 'Planner (plans of Microsoft 365 groups)' Service = 'Graph' Depth = 'Complete' Requires = @('groups') Collect = { $groups = @(Limit-TcCollectorRow 'Microsoft 365 groups' @(Get-TcTable 'groups' | Where-Object Type -EQ 'Microsoft 365') ([int](Get-TcCollectionSetting 'maxPlannerGroups'))) $denied = 0 Add-TcTable 'plannerPlans' @(foreach ($g in $groups) { try { foreach ($p in Get-TcGraphList "v1.0/groups/$(Get-TcProperty $g 'Id')/planner/plans") { [ordered]@{ 'Group' = (Get-TcProperty $g 'Name') 'Plan' = (Get-TcProperty $p 'title') 'Created' = (Format-TcValue (Get-TcProperty $p 'createdDateTime')) } } } catch { $denied++; if ($denied -le 3) { Add-TcCollectorWarning "Plans of $(Get-TcProperty $g 'Name')" $_ } } }) if ($denied) { Add-TcCollectorNote ('Plans of {0} of {1} groups could not be read with delegated access (the account must be a member): application mode is recommended for a full Planner inventory.' -f $denied, $groups.Count) } } } @{ Id = 'AUT-04' Workload = 'Other services' Title = 'Bookings and Viva Engage' Service = 'Graph' Collect = { Invoke-TcCollectorPart 'Bookings' { Add-TcTable 'bookings' @(foreach ($b in Get-TcGraphList 'v1.0/solutions/bookingBusinesses') { [ordered]@{ 'Name' = (Get-TcProperty $b 'displayName') 'Email' = (Get-TcProperty $b 'email') 'Type' = (Get-TcProperty $b 'businessType') 'Published' = (Get-TcProperty $b 'isPublished') } }) } Invoke-TcCollectorPart 'Viva Engage communities' { $c = try { Get-TcGraphList 'v1.0/employeeExperience/communities' } catch { Get-TcGraphList 'beta/employeeExperience/communities' } Add-TcTable 'vivaCommunities' @(foreach ($x in $c) { [ordered]@{ 'Name' = (Get-TcProperty $x 'displayName') 'Privacy' = (Get-TcProperty $x 'privacy') 'Description' = (Limit-TcText ([string](Get-TcProperty $x 'description')) 200) } }) } } } @{ Id = 'AUT-05' Workload = 'Other services' Title = 'Products with no inventory API (checklist)' Service = 'None' Collect = { Add-TcTable 'manualProducts' @( [ordered]@{ 'Product' = 'Microsoft Forms' 'Inventory' = 'No administration API' 'Migration' = 'Not covered by the usual copy tools' 'To do' = 'Have owners list their active forms, export the responses to Excel, then recreate or duplicate the forms after cutover' }, [ordered]@{ 'Product' = 'Loop' 'Inventory' = 'Workspaces stored in SharePoint Embedded' 'Migration' = 'Not supported by most tools' 'To do' = 'Find the Loop workspaces in use, export the important pages, and confirm with the chosen tool' }, [ordered]@{ 'Product' = 'Whiteboard' 'Inventory' = '.whiteboard files in OneDrive' 'Migration' = 'Follows OneDrive (files)' 'To do' = 'Check that a sample opens after the copy' }, [ordered]@{ 'Product' = 'To Do' 'Inventory' = 'Per user, in the mailbox' 'Migration' = 'Follows the mailbox if the tool copies tasks' 'To do' = 'Confirm with the chosen tool, otherwise each user exports their own' }, [ordered]@{ 'Product' = 'OneNote' 'Inventory' = 'Notebooks in SharePoint and OneDrive' 'Migration' = 'Follows the files, check internal links' 'To do' = 'Test opening and sync after the copy' }, [ordered]@{ 'Product' = 'Stream (on SharePoint)' 'Inventory' = 'Videos in SharePoint and OneDrive' 'Migration' = 'Follows the files, check transcripts and chapters' 'To do' = 'Sample the Teams meeting recordings' }, [ordered]@{ 'Product' = 'Teams recordings and chats' 'Inventory' = '1:1 and group chats in mailboxes' 'Migration' = 'Depends on the tool (often a read-only history)' 'To do' = 'Decide how to keep the history' }, [ordered]@{ 'Product' = 'Copilot and agents' 'Inventory' = 'Licenses and agents per tenant' 'Migration' = 'Agents and connectors must be recreated' 'To do' = 'List Copilot Studio agents and Graph connectors before the source tenant closes' }) } } ) } |