Entra / Microsoft 365 · Users & guests
Find Loop app users
Find licensed users who are actively using the Microsoft Loop app based on audit log activity.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-ExchangeOnline -SkipLoadingCmdletHelpConnect-MgGraph -NoWelcome -Scopes User.Read.All
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
param([int] $LookbackDays = 15,[string] $StartDate = (Get-Date).AddDays(-$LookbackDays),[string] $EndDate = (Get-Date).AddDays(1))[array]$Modules = Get-Module | Select-Object -ExpandProperty NameIf ("ExchangeOnlineManagement" -notin $Modules) {Write-Host "Connecting to Exchange Online" -ForegroundColor RedConnect-ExchangeOnline -SkipLoadingCmdletHelp}# To read user information from Entra IDConnect-MgGraph -NoWelcome -Scopes User.Read.All$CSVOutPutFile = "c:\temp\LoopUserInformation.csv"# This command finds user accounts with a license containing the Loop app. It specifies the SKU identifers for# Microsoft 365 Business Standard, Microsoft 365 Business Premium, Microsoft 365 E3, amd Microsoft 365 E5.# There are other variants of these SKUs # for government and academic use, so it's important to pass the SKU# identifiers in use within your tenant. The service plan for Loop is MICROSOFT_LOOP (c4b8c31a-fb44-4c65-9837-a21f55fcabda)Write-Host "Finding user accounts to check..."[array]$LoopLicensedUsers = Get-MgUser -Filter "assignedLicenses/any(s:s/skuId eq cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46) `or assignedLicenses/any(s:s/skuid eq f245ecc8-75af-4f8e-b61f-27d8114de5f3) `or assignedLicenses/any(s:s/skuid eq 05e9a617-0261-4cee-bb44-138d3ef5d965) `or assignedLicenses/any(s:s/skuid eq 06ebc4ee-1bb5-47dd-8120-11324bc54e06)" `-ConsistencyLevel Eventual -CountVariable Licenses -All -Sort 'displayName' `-Property Id, displayName, signInActivity, userPrincipalName, department, jobtitle, countryIf ($LoopLicensedUsers.count -eq 0) {Write-Host "No users can be found eligble to use the Loop app - exiting" -ForegroundColor Redbreak}# Now find licensed user accounts so that we can detect who might be using the Loop app without a supported license# (OK until 30 June 2024)[Array]$Users = Get-MgUser -Filter "assignedLicenses/`$count ne 0 and userType eq 'Member'" `-ConsistencyLevel eventual -CountVariable Records -All `-Property id, displayName, userPrincipalName, country, department, assignedlicenses, `licenseAssignmentStates, createdDateTime, jobTitle, signInActivity, companyName | `Sort-Object DisplayNameWrite-Host ("{0} user accounts have eligible licenses to use the Loop app out of {1} licensed accounts" -f $LoopLicensedUsers.count, $Users.count)# Figure out licensed user accounts that can't use the Loop app (but might)[array]$UsersNotLicensedForLoop = $Users | Where-Object {$_.id -notin $LoopLicensedUsers.id}# Set parameters for the unified audit log search# Define the set of SharePoint file audit records we are interested in[array]$Operations = "FileModified", "FileModifiedExtended", "FileUploaded", "FileAccessed"Write-Host "Searching the unified audit log for file operations performed using the Loop app..."[array]$Records = Search-UnifiedAuditLog -Operations $Operations -StartDate $StartDate -EndDate $EndDate -Formatted `-SessionCommand ReturnLargeSet -ResultSize 5000If (!($Records)) {Write-Host "No audit records can be found - exiting"Break} Else {# Remove any duplicate records and make sure that everything is sorted in date order$Records = $Records | Sort-Object Identity -Unique$Records = $Records | Sort-Object {$_.CreationDate -as [datetime]}}Write-Host ("Analyzing {0} audit records to identify users of the Loop app..." -f $Records.count)$LoopRecords = [System.Collections.Generic.List[Object]]::new()ForEach ($Rec in $Records) {$AuditData = $Rec.AuditData | ConvertFrom-JSONIf ($Auditdata.Applicationid -eq 'a187e399-0c36-4b98-8f04-1edc167a0996' -and $AuditData.Operation -ne 'UserLoggedIn') {$ReportLine = [PSCustomObject][Ordered]@{UserPrincipalName = $Rec.UserIdsTimestamp = $Rec.CreationDatefileName = $AuditData.SourceFileNameOperation = $Rec.operationsObjectId = $AuditData.ObjectId}$LoopRecords.Add($ReportLine)}}$LoopActiveUsers = $LoopRecords | Sort-Object UserPrincipalName -Unique# First run through identifies user accounts who have a license that allows them to use the Loop app# and are either active or not$LicensedLoopUsersActivity = [System.Collections.Generic.List[Object]]::new()ForEach ($User in $LoopLicensedUsers) {$AuditRecord = $null# Fetch user information from the data we have already retrieved$AuditRecord = $LoopActiveUsers | Where-Object userPrincipalName -match $User.userPrincipalNameIf ($null -eq $AuditRecord) {Write-Host ("Can't find usage of the Loop app by {0}" -f $User.displayname)$LoopReportLine = [PSCustomObject][Ordered]@{User = $User.UserPrincipalNameName = $User.displayName'Job title' = $User.jobTitleDepartment = $User.departmentCountry = $User.countryLicensed = "True"Active = "False"'Last Active' = "N/A"}$LicensedLoopUsersActivity.Add($LoopReportLine)} Else {Write-Host ("User {0} last used the Loop app on {1}" -f $User.displayName, $AuditRecord.TimeStamp)$LoopReportLine = [PSCustomObject][Ordered]@{User = $User.UserPrincipalNameName = $User.displayName'Job title' = $User.jobTitleDepartment = $User.departmentCountry = $User.countryLicensed = "True"Active = "True"'Last Active' = $AuditRecord.TimeStamp}$LicensedLoopUsersActivity.Add($LoopReportLine)}}# Now check for unlicensed use of the Loop appForEach ($User in $UsersNotLicensedForLoop) {$AuditRecord = $null# Fetch user information from the data we have already retrieved$AuditRecord = $LoopActiveUsers | Where-Object userPrincipalName -match $User.userPrincipalNameIf ($AuditRecord) {Write-Host ("Unlicensed user {0} last used the Loop app on {1}" -f $User.displayName, $AuditRecord.TimeStamp) -ForegroundColor Red$LoopReportLine = [PSCustomObject][Ordered]@{User = $User.UserPrincipalNameName = $User.displayName'Job title' = $User.jobTitleDepartment = $User.departmentCountry = $User.countryLicensed = "False"Active = "True"'Last Active' = $AuditRecord.TimeStamp}$LicensedLoopUsersActivity.Add($LoopReportLine)}}$LicensedLoopUsersActivity | Out-GridView$LicensedLoopUsersActivity | Export-CSV -Path $CSVOutPutFile -NoTypeInformation -Encoding utf8Write-Host ("Loop user activity information is available in {0}" -f $CSVOutPutFile)
Parameters
ParameterDefaultNotes
-LookbackDays15Number of days back to search Loop app audit records.-StartDate(Get-Date).AddDays(-15)Start of the audit log search window.-EndDate(Get-Date).AddDays(1)End of the audit log search window.Attribution
Author
Office365itpros