Entra / Microsoft 365 · Teams
Analyze teams external chats
Analyze User chats to find chats with people outside the tenant.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -NoWelcome -ClientId $AppId -CertificateThumbprint $CertificateThumbPrint -TenantId $HomeTenantId
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
param([string] $AppId = "")Function Add-MessageRecipients {# Function to build an addressee list to send email[cmdletbinding()]Param([array]$ListOfAddresses )ForEach ($SMTPAddress in $ListOfAddresses) {@{emailAddress = @{address = $SMTPAddress}}}}# Start of processing# Define the home tenant where the job will run$HomeTenantId = 'a662313f-14fc-43a2-9a7a-d2e27f4f3478'$CSVFile = "c:\temp\TeamsExternalAccessScan.csv"# Define the settings used for certificate-based authentication for the registered app you want to use# Thumbprint for the X.509 certificate uploaded to the registered app$CertificateThumbPrint = "F79286DB88C21491110109A0222348FACF694CBD"# Application (client) identifier for the registered app. Get this value from the app properties# Connect to the Graph and Teams endpoints using certificate-based authentication# Certificate-based authentication depends on the tenant id, app id, and certificate thumbprint. All must be# defined and accurate as otherwise authentication will failWrite-Host "Connecting to the Graph SDK endpoint..."Connect-MgGraph -NoWelcome -ClientId $AppId -CertificateThumbprint $CertificateThumbPrint -TenantId $HomeTenantIdWrite-Host "Now connecting to the Microsoft Teams endpoint..."$Status = Connect-MicrosoftTeams -TenantId $HomeTenantId -ApplicationId $AppId -Certificate $CertificateThumbPrintIf (!($Status.Account -ne $AppId)) {Write-Host "Whoops... can't connect!"; break} Else {Write-Host "All connected. Now setting things up..."}# Fetch organization information$HomeTenant = Get-MgOrganization# Find domains in the tenant so that we can discard chats with local people[array]$Domains = Get-MgDomain | Select-Object -ExpandProperty Id# Create hash table to store information about external tenants$ExternalTenants = @{}# Find user accounts to check[array]$Users = Get-MgUser -Filter "assignedLicenses/`$count ne 0 and userType eq 'Member'" `-ConsistencyLevel eventual -CountVariable Records -All | Sort-Object displayNameWrite-Output ("Found {0} user accounts to process..." -f $users.Count)$ChatReport = [System.Collections.Generic.List[Object]]::new()ForEach ($User in $Users) {Write-Output ("Looking for chats for user {0}..." -f $User.DisplayName)# Find the OneOnOne chats for this user[array]$Chats = Get-MgBetaUserChat -Userid $User.Id -All -Filter "ChatType eq 'oneOnOne'"If ($Chats) {Write-Output ("Found {0} one-on-one chats for {1}" -f $Chats.count, $User.DisplayName)ForEach ($Chat in $Chats) {# Find participantsTry {[array]$Participants = Get-MgChatMember -ChatId $Chat.Id# Remove the user we're processing[array]$OtherParticipant = $Participants | `Where-Object {$_.additionalProperties.email -ne $User.userPrincipalName}} Catch {Write-Output ("Can't fetch participant information for chat {0}" -f $Chat.Id)}If ($OtherParticipant.displayName) {$TenantName = $Null# See if we can get a domain name for the other participantTry {$EmailDomain = $OtherParticipant.additionalProperties.email.split('@')[1]} Catch {$EmailDomain = $Null}# Only process a chat if the other participant comes from a domain outside the home tenantIf ($EmailDomain -notin $Domains) {# Check if the owning domain for the chat is the home or the external tenant$ExternalTenantId = $OtherParticipant.additionalProperties.tenantId.ToString()If ($ExternalTenantId -ne $HomeTenantId) {Try {$TenantName = $ExternalTenants[$ExternalTenantId] }Catch {Write-Output ("Resolving tenant id {0}" -f $ExternalTenantId)}$ChatCount = "N/A"If (!($TenantName)) {# Couldn't find the tenant, so resolve its identifier and record its details$Uri = ("https://graph.microsoft.com/beta/tenantRelationships/findTenantInformationByTenantId(tenantId='{0}')" -f `$ExternalTenantId)$ExternalTenantData = Invoke-MgGraphRequest -Uri $Uri -Method Get$TenantName = $ExternalTenantData.displayName$ExternalTenants.Add($ExternalTenantId, $TenantName)}}If ($ExternalTenantId -eq $HomeTenantId) {$TenantName = $HomeTenant.DisplayName[array]$ChatMessages = Get-MgChatMessage -ChatId $Chat.id -All$ChatCount = $ChatMessages.Count}$Reportline = [PsCustomObject]@{Chat = $Chat.IdUser = $User.displayNameUPN = $User.userPrincipalNameParticipant = $OtherParticipant.displayName'Participant Email' = $OtherParticipant.additionalProperties.emailDomain = $EmailDomain'Owning Tenant Name' = $TenantNameRole = $OtherParticipant.RolesChatCreated = $Chat.CreatedDateTimeLastUpdated = $Chat.LastUpdatedDateTime'Chat Messages' = $ChatCount}$ChatReport.Add($ReportLine)# Slight pause before continuing... wouldn't want to be throttledStart-Sleep -Milliseconds 500}}}}}Write-Output ""Write-Output ("{0} chats found with external people in the {1} tenant" -f $ChatReport.count, $HomeTenant.displayName)Write-Output ""Write-Output "Checking the Teams External Access configuration for the tenant..."# Find if we need to update the external access configuration[array]$ExternalDomains = $ChatReport.Domain | Sort-Object -Unique$DomainConfiguration = Get-CsTenantFederationConfiguration | Select-Object -ExpandProperty AllowedDomains[array]$ConfiguredDomains = $DomainConfiguration.AllowedDomain.DomainIf (!($ConfiguredDomains)) {Write-Output ("The {0} tenant doesn't have any configured domains for external access" -f $HomeTenant.displayName)} Else {[array]$MissingDomains = $ExternalDomains | Where-Object {$_ -notin $ConfiguredDomains}If ($MissingDomains) {$MissingDomainsOutput = $MissingDomains -Join ", "Write-Output ("The following external domains are not configured for external access: {0}" -f $MissingDomainsOutput)} Else {Write-Output ("External Access is configured for all external domains found in the scan")}}# Email the results$EmailRecipient = "Tony.Redmond@office365itpros.com"# Send a message from the shared mailbox$MsgFrom = "Azure.Management.Account@office365itpros.com"# Add your recipient address here$ToRecipientList = @( $EmailRecipient )[array]$MsgToRecipients = Add-MessageRecipients -ListOfAddresses $ToRecipientList$MsgSubject = "Teams external access review"$HtmlHead = "<h2>Teams external chat participant report</h2><p>The following chats have been found to involve external participants.</p>"$HtmlBody = $ChatReport | Select-Object User, UPN, 'Participant Email', Domain, ChatCreated, 'Chat Messages' | `ConvertTo-Html -Fragment# Add a line about missing domains if there are any in the external access configuration, else just send the# data about external chatsIf ($MissingDomains) {$HtmlBody2 = "<h2>The following domains are not in the tenant external access configuration: </h2>" + `"<p>" + $MissingDomainsOutput + "</p>"$HtmlMsg = "</body></html><p>" + $HtmlHead + $Htmlbody + "<p>" + $HtmlBody2 + "<p>"} Else {$HtmlMsg = "</body></html><p>" + $HtmlHead + $Htmlbody + "<p>"}# Construct the message body$MsgBody = @{Content = "$($HtmlMsg)"ContentType = 'html'}$Message = @{subject = $MsgSubject}$Message += @{toRecipients = $MsgToRecipients}$Message += @{body = $MsgBody}$Params = @{'message' = $Message}$Params += @{'saveToSentItems' = $True}$Params += @{'isDeliveryReceiptRequested' = $True}# And send the message using the parameters that we've filled inSend-MgUserMail -UserId $MsgFrom -BodyParameter $ParamsWrite-Output ("Message containing external chat information sent to {0}!" -f $EmailRecipient)$ChatReport | Export-Csv -NoTypeInformation $CSVFileWrite-Output ("The complete data file is available in {0}" -f $CSVFile)
Parameters
ParameterDefaultNotes
-AppId""Application (client) ID for the app registration used to connect.Attribution
Author
Office365itpros