As a Microsoft 365 admin, keeping tabs on your licensed administrators isn't just good practice — it's essential for security, compliance, and license optimization. In this post, we'll explain 2 ways using which you can track licensed Microsoft 365 administrators effortlessly — either via Graph PowerShell or a ready-to-use GUI-based WinForms tool.
A Licensed Microsoft 365 Administrator is:
Examples of such role include: Global Administrator, Exchange Administrator, Teams Adminstrator, SharePoint Administrator etc.,
Not all role holders are automatically licensed — this is why you need to validate.
Here are five reasons:
There are two ways to do this: You can use a Graph PowerShell script or opt for a GUI tool if you want a friendlier experience.
If you're comfortable with PowerShell and scripting, this method gives you full control. We’ve published a full article and script walkthrough here: Graph PowerShell Script for Fetching Licensed M365 Admins
This script:
If you prefer not to work in the command line, we’ve built a simple, click-and-run GUI tool using PowerShell + WinForms. Here's the full script you can save and run as .ps1:
Note: This works only for Windows users.
PowerShell Script: LicensedM365Admins_GUI.ps1
Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing $form = New-Object System.Windows.Forms.Form $form.Text = "Licensed Microsoft 365 Admins" $form.Size = New-Object System.Drawing.Size(800, 500) $form.StartPosition = "CenterScreen" $fetchButton = New-Object System.Windows.Forms.Button $fetchButton.Text = "Fetch Licensed Admins" $fetchButton.Location = New-Object System.Drawing.Point(20, 20) $fetchButton.Size = New-Object System.Drawing.Size(160, 30) $form.Controls.Add($fetchButton) $exportButton = New-Object System.Windows.Forms.Button $exportButton.Text = "Export to CSV" $exportButton.Location = New-Object System.Drawing.Point(200, 20) $exportButton.Size = New-Object System.Drawing.Size(120, 30) $exportButton.Enabled = $false $form.Controls.Add($exportButton) $dataGrid = New-Object System.Windows.Forms.DataGridView $dataGrid.Location = New-Object System.Drawing.Point(20, 70) $dataGrid.Size = New-Object System.Drawing.Size(740, 360) $dataGrid.ReadOnly = $true $dataGrid.AllowUserToAddRows = $false $dataGrid.AutoSizeColumnsMode = "Fill" $form.Controls.Add($dataGrid) $script:LicensedAdmins = @() $fetchButton.Add_Click({ $fetchButton.Enabled = $false $exportButton.Enabled = $false $dataGrid.DataSource = $null try { Connect-MgGraph -Scopes "Directory.Read.All", "User.Read.All" -ErrorAction Stop $roles = (Invoke-MgGraphRequest -Method GET -Uri "v1.0/directoryRoles").value $licensedAdminsMap = @{} foreach ($role in $roles) { $roleId = $role.id $roleName = $role.displayName try { $members = (Invoke-MgGraphRequest -Method GET -Uri "v1.0/directoryRoles/$roleId/members").value } catch { continue } foreach ($member in $members) { if (-not $member.userPrincipalName) { continue } try { $licenseInfo = Invoke-MgGraphRequest -Method GET -Uri "v1.0/users/$($member.id)/licenseDetails" } catch { continue } if ($licenseInfo.value.Count -eq 0) { continue } $upn = $member.userPrincipalName if (-not $licensedAdminsMap.ContainsKey($upn)) { $licensedAdminsMap[$upn] = @{ DisplayName = $member.displayName UserPrincipalName = $upn LicenseStatus = "Licensed" Roles = @($roleName) JobTitle = $member.jobTitle } } else { $licensedAdminsMap[$upn].Roles += $roleName } } } $script:LicensedAdmins = $licensedAdminsMap.Values | ForEach-Object { [PSCustomObject]@{ "Admin Name" = $_.DisplayName "User Principal Name" = $_.UserPrincipalName "License Status" = $_.LicenseStatus "Job Title" = $_.JobTitle "Admin Roles" = ($_.Roles -join ", ") } } if ($script:LicensedAdmins.Count -gt 0) { $dataTable = New-Object System.Data.DataTable $script:LicensedAdmins[0].psobject.properties.name | ForEach-Object { [void]$dataTable.Columns.Add($_) } foreach ($obj in $script:LicensedAdmins) { $row = $dataTable.NewRow() foreach ($col in $dataTable.Columns) { $row[$col.ColumnName] = $obj.($col.ColumnName) } $dataTable.Rows.Add($row) } $dataGrid.DataSource = $dataTable $exportButton.Enabled = $true } else { [System.Windows.Forms.MessageBox]::Show("No licensed admins found.", "Info", "OK", "Information") } } catch { [System.Windows.Forms.MessageBox]::Show("Error: $($_.Exception.Message)", "Error", "OK", "Error") } $fetchButton.Enabled = $true }) $exportButton.Add_Click({ $saveDialog = New-Object System.Windows.Forms.SaveFileDialog $saveDialog.Filter = "CSV Files (*.csv)|*.csv" $saveDialog.Title = "Save Report" $saveDialog.FileName = "LicensedM365Admins.csv" if ($saveDialog.ShowDialog() -eq "OK") { $script:LicensedAdmins | Export-Csv -Path $saveDialog.FileName -NoTypeInformation [System.Windows.Forms.MessageBox]::Show("Export completed.", "Export", "OK", "Information") } }) [void]$form.ShowDialog()
Click and Play the GIF to see the tool in action
Tracking licensed Microsoft 365 administrators is a smart move for every IT team. Whether you prefer working with Graph PowerShell or a simple GUI interface, you now have two efficient ways to gather and export the data.
Stay proactive. Stay compliant. And most importantly — make administration easier with automation.
Did You Know? Managing Microsoft 365 applications is even easier with automation. Try our Graph PowerShell scripts to automate tasks like generating reports, cleaning up inactive Teams, or assigning licenses efficiently.
Ready to get the most out of Microsoft 365 tools? Explore our free Microsoft 365 administration tools to simplify your administrative tasks and boost productivity.
© Your Site Name. All Rights Reserved. Design by HTML Codex