Skip to content

Instantly share code, notes, and snippets.

@joerodgers
Last active April 8, 2025 13:31
Show Gist options
  • Save joerodgers/2b525109d2153611ddc219f53d63b2a7 to your computer and use it in GitHub Desktop.
Save joerodgers/2b525109d2153611ddc219f53d63b2a7 to your computer and use it in GitHub Desktop.
Reports Teams recordings (.mp4) analytics in all onedrives
#requires -Modules @{ ModuleName="PnP.PowerShell"; ModuleVersion="2.4.0" }
function New-Recording
{
[CmdletBinding()]
param
(
[parameter(mandatory=$true)]
[AllowEmptyString()]
[string]
$SiteUrl,
[parameter(mandatory=$true)]
[AllowEmptyString()]
[string]
$FileName,
[parameter(mandatory=$true)]
[AllowEmptyString()]
[string]
$ContentType,
[parameter(mandatory=$true)]
[AllowNull()]
[int]
$ActorCount,
[parameter(mandatory=$true)]
[AllowNull()]
[int]
$ActionCount
)
if( [string]::IsNullOrEmpty( $ContentType ) )
{
$ContentType = "Unknown"
}
[PSCustomObject] @{
SiteUrl = $SiteUrl
FileName = $FileName
ContentType = $ContentType
ActorCount = $ActorCount
ActionCount = $ActionCount
}
}
function Get-RecordingMetadata
{
[CmdletBinding()]
param
(
)
begin
{
}
process
{
try
{
$web = Invoke-PnPSPRestMethod -Method GET -Url '/_api/web?$select=ServerRelativeUrl' -ErrorAction Stop
}
catch
{
Write-Error "Failed to retrieve ServerRelativeUrl. Error: $_"
return
}
$serverRelativeUrl = $web.ServerRelativeUrl
# get the first 5000 recordings from the /Documents/Recordings folder
$filter = '{{
"parameters": {{
"FolderServerRelativeUrl" : "{0}/Documents/Recordings",
"ViewXml" : "<View Scope=\"RecursiveAll\"><Query><Where><Eq><FieldRef Name=''File_x0020_Type''/><Value Type=''text''>mp4</Value></Eq></Where></Query><RowLimit Paged=\"TRUE\">5000</RowLimit></View>",
"RenderOptions" : 4099
}}
}}' -f $serverRelativeUrl
# check if the Documents/Recordings folder exists
try
{
$folderExistsUrl = "/_api/web/GetFolderByServerRelativeUrl('{0}')/Exists" -f "$serverRelativeUrl/Documents/Recordings"
$response = Invoke-PnPSPRestMethod -Method GET -Url $folderExistsUrl -ErrorAction Stop
if( -not $response.value ){ return } # folder does not exist
}
catch
{
Write-Error "Failed to determine if the Recordings folder exists. Error: $_"
return
}
# pull first 5k .mp4 files from /Documents/Recordings folder
try
{
$listItemsUrl = '/_api/web/GetListByTitle(''Documents'')/RenderListDataAsStream'
$response = Invoke-PnPSPRestMethod -Method POST -Url $listItemsUrl -Content $filter -ErrorAction Stop
if( -not $response.ListData.Row ){ return }
}
catch
{
Write-Error "Failed to read .mp4 files at $serverRelativeUrl/Documents/Recordings. Error: $_"
return
}
# enumerate .mp4 files
$recordings = foreach( $row in $response.ListData.Row )
{
$uri = [uri]$row.'.spItemUrl'
if( -not $uri )
{
Write-Error "Failed to determine recording drive url."
return
}
# fetch media properties
try
{
$uri = [uri]$row.'.spItemUrl' # list item drive url
$mediaUrl = '{0}&select=media' -f $uri.AbsoluteUri
$media = Invoke-PnPSPRestMethod -Method GET -Url $mediaUrl -Accept "application/json" -ErrorAction Stop
}
catch
{
Write-Error "Failed to media properties for $($uri.AbsoluteUri). Error: $_"
}
# fetch file analytics
try
{
$analyticsUrl = '{0}/analytics/allTime' -f $uri.AbsolutePath
$analytics = Invoke-PnPSPRestMethod -Method GET -Url $analyticsUrl -ErrorAction Stop
}
catch
{
Write-Error "Failed to analytics properties for $($uri.AbsoluteUri). Error: $_"
}
New-Recording -SiteUrl $serverRelativeUrl -FileName $row.FileLeafRef -ContentType $media.media.mediasource.contentType -ActorCount $analytics.access.actorCount -ActionCount $analytics.access.actionCount
}
return $recordings
}
end
{
}
}
# requires SharePoint > Application > Sites.FullControl.All
Connect-PnPOnline -Url "https://$env:CDX_TENANT-admin.sharepoint.com" `
-ClientId $env:CDX_CLIENTID `
-Thumbprint $env:CDX_THUMBPRINT `
-Tenant $env:CDX_TENANTID `
-ErrorAction Stop
$timestamp = Get-Date -Format FileDateTime
$exportPath = "C:\_temp\recording_analytics_$timestamp.csv"
$connection = Get-PnPConnection -ErrorAction Stop
$drives = Get-PnPTenantSite -IncludeOneDriveSites -Filter "Url -like '.sharepoint.com/personal/'" -ErrorAction Stop
$counter = 0
foreach( $drive in $drives )
{
$counter++
Write-Host "[$(Get-Date)] - ($counter/$($drives.Count)) - Scanning drive: $($drive.Url)"
try
{
# connect to the onedrive
Connect-PnPOnline -Url $drive.Url `
-ClientId $connection.ClientId `
-Thumbprint $connection.Certificate.Thumbprint `
-Tenant $connection.Tenant `
-ErrorAction Stop
if( $recordings = Get-RecordingMetadata -ErrorAction Stop )
{
$recordings
$recordings | Export-Csv -Path $exportPath -NoTypeInformation -Append
}
}
catch
{
Write-Host "Failed to scan site: $($drive.Url). Error: $_"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment