Teams Filetype Statistics

I have created a script that does a count of each file type in all teams in Microsoft Teams.
It also shows when that file type was last modified in the team.

You need an Azure App registration with the Group.Read.All and Site.Read.All rights.

### Teams Filetype Statistics ###
### Version 1.1 ###
### Author: Alexander Holmeset ###
### Email: [email protected] ###
### Twitter: twitter.com/alexholmeset ###
### Blog: alexholmeset.blog ###
#Need Azure App registration with Groupd.Read.All and Site.Read.All permissions.
$TenantId = "xxxxxxxx"
$ClientID = "xxxxxxxx"
$ClientSecret = "xxxxxxxx"
$TeamsStatsTotal = [PSCustomObject]@()
$stopwatch = [system.diagnostics.stopwatch]::StartNew()
function Get-MSGraphAppToken{
<# .SYNOPSIS
Get an app based authentication token required for interacting with Microsoft Graph API
.PARAMETER TenantID
A tenant ID should be provided.
.PARAMETER ClientID
Application ID for an Azure AD application. Uses by default the Microsoft Intune PowerShell application ID.
.PARAMETER ClientSecret
Web application client secret.
.EXAMPLE
# Manually specify username and password to acquire an authentication token:
Get-MSGraphAppToken -TenantID $TenantID -ClientID $ClientID -ClientSecert = $ClientSecret
.NOTES
Author: Jan Ketil Skanke
Contact: @JankeSkanke
Created: 2020-15-03
Updated: 2020-15-03
Version history:
1.0.0 - (2020-03-15) Function created
#>
[CmdletBinding()]
param (
[parameter(Mandatory = $true, HelpMessage = "Your Azure AD Directory ID should be provided")]
[ValidateNotNullOrEmpty()]
[string]$TenantID,
[parameter(Mandatory = $true, HelpMessage = "Application ID for an Azure AD application")]
[ValidateNotNullOrEmpty()]
[string]$ClientID,
[parameter(Mandatory = $true, HelpMessage = "Azure AD Application Client Secret.")]
[ValidateNotNullOrEmpty()]
[string]$ClientSecret
)
Process {
$ErrorActionPreference = "Stop"
# Construct URI
$uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token&quot;
# Construct Body
$body = @{
client_id = $clientId
scope = "https://graph.microsoft.com/.default&quot;
client_secret = $clientSecret
grant_type = "client_credentials"
}
try {
$MyTokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing
$MyToken =($MyTokenRequest.Content | ConvertFrom-Json).access_token
If(!$MyToken){
Write-Warning "Failed to get Graph API access token!"
Exit 1
}
$MyHeader = @{"Authorization" = "Bearer $MyToken" }
}
catch [System.Exception] {
Write-Warning "Failed to get Access Token, Error message: $($_.Exception.Message)"; break
}
return $MyHeader
}
}
#Generate Graph API Token
$global:Header = Get-MSGraphAppToken -TenantID $TenantId -ClientID $ClientID -ClientSecret $ClientSecret
#URL to SharePoint list with expand fields.
$currentUri = "https://graph.microsoft.com/v1.0/groups?`$filter=resourceProvisioningOptions/Any(x:x eq 'Team')"
#Gets entrys in SharePoint list.
$Teams = while (-not [string]::IsNullOrEmpty($currentUri)) {
# API Call
# Write-Host "`r`nQuerying $currentUri..." -ForegroundColor Yellow
$apiCall = Invoke-WebRequest -Method "GET" -Uri $currentUri -ContentType "application/json" -Headers $global:Header -ErrorAction Stop -UseBasicParsing
$nextLink = $null
$currentUri = $null
if ($apiCall.Content) {
# Check if any data is left
$nextLink = $apiCall.Content | ConvertFrom-Json | Select-Object '@odata.nextLink'
$currentUri = $nextLink.'@odata.nextLink'
$apiCall.Content | ConvertFrom-Json
}
}
$Teams = $Teams.value
$count = 0
foreach($team in $teams){
If($stopwatch.Elapsed.minutes -gt 40) {
$stopwatch = [system.diagnostics.stopwatch]::StartNew()
#Generate Graph API Token
$global:Header = Get-MSGraphAppToken -TenantID $TenantId -ClientID $ClientID -ClientSecret $ClientSecret
}
$count++
$count
$filetypes = @()
$teamschannelsURL = "https://graph.microsoft.com/v1.0/teams/$($team.id)/channels&quot;
$teamchannels = (Invoke-RestMethod -Method "GET" -Uri $teamschannelsURL -ContentType "application/json" -Headers $global:Header -ErrorAction Stop -UseBasicParsing).value
foreach($channel in $teamchannels){
if($channel.membershiptype -eq "private"){
$channelURL = $teamschannelsURL+"/$($channel.id)/filesfolder"
$channelinfo = (Invoke-RestMethod -Method "GET" -Uri $channelURL -ContentType "application/json" -Headers $global:Header -ErrorAction Stop -UseBasicParsing).parentReference.driveid
$driveurl = "https://graph.microsoft.com/v1.0/drives/$channelinfo/list&quot;
$PrivateList = Invoke-RestMethod -Method "GET" -Uri $driveurl -ContentType "application/json" -Headers $global:Header -ErrorAction Stop -UseBasicParsing
$PrivateListID = $PrivateList.id
$PrivateSiteID = ((($PrivateList.parentReference).siteid).split(".com,")[1]).split(",")[0]
$PrivateSharePointDocumentsURL = "https://graph.microsoft.com/v1.0/sites/$privatesiteid/lists/$privatelistid/items&quot;
$currentUri = $PrivateSharePointDocumentsURL
#Gets entrys in SharePoint list.
$Documents = @()
$Documents = while (-not [string]::IsNullOrEmpty($currentUri)) {
# API Call
# Write-Host "`r`nQuerying $currentUri..." -ForegroundColor Yellow
$apiCall = Invoke-WebRequest -Method "GET" -Uri $currentUri -ContentType "application/json" -Headers $global:Header -ErrorAction Stop -UseBasicParsing
$nextLink = $null
$currentUri = $null
if ($apiCall.Content) {
# Check if any data is left
$nextLink = $apiCall.Content | ConvertFrom-Json | Select-Object '@odata.nextLink'
$currentUri = $nextLink.'@odata.nextLink'
$apiCall.Content | ConvertFrom-Json
}
}
$Documents = $Documents.value | Where-Object{($_.contenttype).name -eq "Document"}
$filetypes += $Documents.weburl -replace '.*\.' | select-string -Pattern '^[^\s]{3,4}$'
}
}
$TeamSharePointID = @()
$TeamSharePointID = $team.proxyAddresses | Where-Object { $_ -like 'SPO:*' }
$TeamSharePointID = ($TeamSharePointID.split("SPO:SPO_")[1]).split("@SPO_")[0]
$SharePointListsURL = "https://graph.microsoft.com/v1.0/sites/$TeamSharePointID/lists/&quot;
$SharePointList = (Invoke-RestMethod -Method "GET" -Uri $SharePointListsURL -ContentType "application/json" -Headers $global:Header -ErrorAction Stop -UseBasicParsing).value | where-object { $_.name -eq 'Shared Documents' }
$SharePointDocumentsURL = @()
$SharePointDocumentsURL = $SharePointListsURL+$SharePointList.id+"/items"
#URL to SharePoint list with expand fields.
$currentUri = $SharePointDocumentsURL
#Gets entrys in SharePoint list.
$Documents = @()
$Documents = while (-not [string]::IsNullOrEmpty($currentUri)) {
# API Call
# Write-Host "`r`nQuerying $currentUri..." -ForegroundColor Yellow
$apiCall = try{Invoke-WebRequest -Method "GET" -Uri $currentUri -ContentType "application/json" -Headers $global:Header -ErrorAction Stop -UseBasicParsing} Catch{Start-Sleep -Seconds 10;Invoke-WebRequest -Method "GET" -Uri $currentUri -ContentType "application/json" -Headers $global:Header -ErrorAction Stop -UseBasicParsing }
$nextLink = $null
$currentUri = $null
if ($apiCall.Content) {
# Check if any data is left
$nextLink = $apiCall.Content | ConvertFrom-Json | Select-Object '@odata.nextLink'
$currentUri = $nextLink.'@odata.nextLink'
$apiCall.Content | ConvertFrom-Json
}
}
$Documents = $Documents.value | Where-Object{($_.contenttype).name -eq "Document"}
$filetypes += $Documents.weburl -replace '.*\.' | select-string -Pattern '^[^\s]{3,4}$'
$filetypesUnique = $filetypes | Sort-Object -Unique
write-output "FileTypes Unique: $($filetypesUnique.count)"
foreach($filetype in $filetypesUnique){
$datemodified = @()
$datemodified = (($datemodified = $Documents | Where-Object { $_.weburl -like "*$filetype" } ).lastModifiedDateTime | Sort-Object -descending)[0]
If(!$datemodified){$datemodified = 0}
$TeamStatsObject = @()
$TeamStatsObject = [PSCustomObject]@{
Displayname = $team.displayName
ObjectID = $team.id
Type = $filetype
Count = ($filetypes | Where-Object { $_ -eq $filetype }).count
LastModified = $datemodified
}
$TeamsStatsTotal += $TeamStatsObject
}
}

Leave a comment