Public/Get-M365LicenseActivity.ps1
|
function Get-M365LicenseActivity { <# .SYNOPSIS Retrieves license change audit log entries from Microsoft Graph. .OUTPUTS [PSCustomObject[]] One object per audit log entry. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$AccessToken, [int]$DaysBack = 30 ) Write-M365Log "Retrieving license activity (last $DaysBack days)..." $startDate = (Get-Date).AddDays(-$DaysBack).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') $filter = "(activityDisplayName eq 'Change user license' or activityDisplayName eq 'Add license to user' or activityDisplayName eq 'Remove license from user') and activityDateTime ge $startDate" $encodedFilter = [System.Uri]::EscapeDataString($filter) $endpoint = "https://graph.microsoft.com/v1.0/auditLogs/directoryAudits?`$filter=$encodedFilter&`$orderby=activityDateTime desc" try { $auditLogs = Invoke-M365GraphRequest -Uri $endpoint -AccessToken $AccessToken -AllPages Write-M365Log "Retrieved $($auditLogs.Count) audit log entries" } catch { if ($_.Exception.Message -like '*403*' -or $_.Exception.Message -like '*Authorization*') { Write-M365Log "Permission denied for audit logs. Ensure AuditLog.Read.All permission is granted." -Level Warning return } throw } if ($auditLogs.Count -eq 0) { Write-M365Log "No license activity found in the last $DaysBack days" return } $ingestionTime = (Get-Date).ToUniversalTime().ToString('o') foreach ($log in $auditLogs) { $targetUser = $log.targetResources | Where-Object { $_.type -eq 'User' } | Select-Object -First 1 $licenseChanges = $log.targetResources | Where-Object { $_.modifiedProperties } | ForEach-Object { $_.modifiedProperties } | Where-Object { $_.displayName -like '*License*' -or $_.displayName -like '*Sku*' } $licenseChangesJson = if ($licenseChanges) { $licenseChanges | ForEach-Object { @{ property = $_.displayName oldValue = $_.oldValue newValue = $_.newValue } } | ConvertTo-Json -Compress } else { '[]' } $actorUPN = $null $actorDisplayName = $null $actorType = 'Unknown' if ($log.initiatedBy.user) { $actorUPN = $log.initiatedBy.user.userPrincipalName $actorDisplayName = $log.initiatedBy.user.displayName $actorType = 'User' } elseif ($log.initiatedBy.app) { $actorDisplayName = $log.initiatedBy.app.displayName $actorType = 'Application' } [PSCustomObject]@{ ActivityId = $log.id ActivityDateTime = $log.activityDateTime ActivityType = $log.activityDisplayName Result = $log.result ResultReason = $log.resultReason TargetUserId = if ($targetUser) { $targetUser.id } else { $null } TargetUserUpn = if ($targetUser) { $targetUser.userPrincipalName } else { $null } TargetUserDisplay = if ($targetUser) { $targetUser.displayName } else { $null } ActorUpn = $actorUPN ActorDisplayName = $actorDisplayName ActorType = $actorType LicenseChanges = $licenseChangesJson CorrelationId = $log.correlationId IngestionTime = $ingestionTime } } Write-M365Log "Emitted license activity records to pipeline" } |