Entra / Microsoft 365 · Applications
Add owners to apps
Finds Entra ID app registrations without owners and assigns the last modifier or creator as owner, using unified audit log records to identify who last managed each app.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -Scopes Application.ReadWrite.All, User.ReadBasic.All, Directory.Read.All
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
param([int] $LookbackDays = 180)Connect-MgGraph -Scopes Application.ReadWrite.All, User.ReadBasic.All, Directory.Read.All$Modules = Get-Module | Select-Object -ExpandProperty NameIf ("ExchangeOnlineManagement" -notin $Modules) {Write-Host "connecting to Exchange Online..."Connect-ExchangeOnline -ShowProgress $false -ErrorAction Stop}Write-Host "Looking for audit records for application management..."$Operations = "Update application.", "Add application."[array]$Records = Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-$LookbackDays) -EndDate (Get-Date) -RecordType AzureActiveDirectory `-Operations $Operations -ResultSize 5000 -SessionCommand ReturnLargesetIf (!$Records) {Write-Host "No audit records found"Break} Else {Write-Host ("{0} records found" -f $Records.count)}$Report = [System.Collections.Generic.List[Object]]::new()ForEach ($Rec in $Records) {$AuditData = $Rec.AuditData | ConvertFrom-Json$ReportLine = [PSCustomObject][Ordered]@{Timestamp = $Rec.CreationDateOperation = $Rec.OperationsUPN = $Rec.UserIdsUserId = $Auditdata.Actor[4].IdAppId = $Auditdata.Target[4].IdAppname = $Auditdata.Target[3].IdObjectId = $AuditData.ObjectId.Split("_")[1]}$Report.Add($ReportLine)}# Make sure the output is sorted by date$Report = $Report | Sort-Object {$_.CreationDate -as [datetime]} -DescendingWrite-Host ("Audit records found for {0} applications" -f $UniqueApps.count)# Group by AppId and select the most recent record for each application[array]$UniqueApps = $Report | Group-Object -Property AppId | ForEach-Object {$_.Group | Sort-Object -Property Timestamp -Descending | Select-Object -First 1}# Find the set of app registrations for the tenant[array]$Apps = Get-MgApplication -All -Property displayName, Id, AppId, Owners, CreatedDateTime, SigninAudience, PublisherDomainWrite-Host ("{0} registered applications found in Entra ID - now checking each app" -f $Apps.count)# If you want just apps with no owners, use this command:# [array]$Apps = Get-MgApplication -Filter "owners/`$count eq 0" -CountVariable CountVar -ConsistencyLevel eventual -All -Property displayName, Id, AppId, Owners, CreatedDateTime, SigninAudience, PublisherDomain[int]$UpdatedApps = 0$AppReport = [System.Collections.Generic.List[Object]]::new()ForEach ($App in $Apps) {$AppOwners = $null; $AppOwnersReport = $null[array]$AppOwners = Get-MgApplication -ApplicationId $App.Id -ExpandProperty Owners | Select-Object -ExpandProperty OwnersIf (-not $AppOwners) {Write-Host ("No owners found for application {0} ({1})" -f $App.DisplayName, $App.AppId)$LastUpdateRecord = $UniqueApps | Where-Object { $_.AppId -eq $App.AppId }If ($LastUpdateRecord) {$UserId = $LastUpdateRecord.UserId$UPN = $LastUpdateRecord.UPNWrite-Host ("Adding {0} as owner for application {1}" -f $UserId, $App.DisplayName)Try {$OwnerRef = ("https://graph.microsoft.com/v1.0/directoryObjects/{0}" -f $UserId)$OwnerId = @{}$OwnerId.Add("@odata.id", $OwnerRef)New-MgApplicationOwnerByRef -ApplicationId $App.Id -BodyParameter $OwnerIdWrite-Host ("Successfully added {0} as owner for application {1}" -f $UPN, $App.DisplayName) -ForegroundColor Yellow$AppOwnersReport = $UPN$UpdatedApps++} Catch {Write-Host ("Failed to add owner for application {0}: {1}" -f $App.DisplayName, $_.Exception.Message)}} Else {Write-Host ("No update record found for application {0}" -f $App.DisplayName)}} Else {$AppOwnersReport = $AppOwners.additionalProperties.userPrincipalName -join ", "Write-Host ("Owners exist for application {0} ({1})" -f $App.DisplayName, $App.AppId) -ForegroundColor Green}$AppReportLine = [PSCustomObject]@{AppId = $App.AppIdAppName = $App.DisplayNameOwners = $AppOwnersReportCreated = $App.CreatedDateTimeSigninAudience = $App.SigninAudiencePublisherDomain = $App.PublisherDomain}$AppReport.Add($AppReportLine)}Write-Host ""Write-Host ("Successfully updated {0} apps with owner details" -f $UpdatedApps)Write-Host ""Write-Host "These applications are still ownerless" -ForegroundColor RedWrite-Host "----------------------------------------" -ForegroundColor Red$AppReport | Sort-Object {$_.Created -as [datetime]} -Descending | Where-Object { $null -eq $_.Owners} | Format-Table AppName, Created, AppId -AutoSize$AppReport | Out-GridView -Title "Entra ID App Registration Owners Report"
Parameters
ParameterDefaultNotes
-LookbackDays180How many days back to search unified audit log records for app management events.Attribution
Author
Office365itpros