Overview
Seven validated read-only scripts for tenant, user, group, license, role, stale-account, and Intune reporting.
Classification: Developer environment.
Operational scenario & objectives
Create reusable read-only administrative reporting with appropriate delegated scopes, pagination, CSV output, and evidence of troubleshooting.
Environment & technologies
Microsoft 365 E5 Developer tenant; Microsoft.Graph.Authentication, delegated Device Code authentication, Graph v1.0 GET requests, and local CSV reports.
Architecture
Microsoft 365 E5 Developer tenant; Microsoft.Graph.Authentication, delegated Device Code authentication, Graph v1.0 GET requests, and local CSV reports.
Implementation & administration
Project 13 · COMPLETE
Microsoft 365 Graph Administration Lab
Validated read-only Microsoft Graph administration and reporting automation across Microsoft 365, Entra ID, and Intune, with least-privilege delegated access and evidence-backed troubleshooting.
Scope
Reusable read-only Microsoft 365 administration tooling
The lab uses a Microsoft 365 E5 Developer tenant to build reusable administration and reporting scripts without creating meaningless tenant activity. Current work is intentionally read-only and focuses on tenant inventory, identity, licensing, directory roles, sign-in activity, and Intune managed-device reporting.
Authentication
Delegated Device Code with required-scope validation
Scripts use the Microsoft Graph PowerShell authentication module with delegated permissions. The stale-account and Intune scripts validate the active Graph context before reuse and reconnect when required scopes are missing.
Microsoft Graph
REST endpoints, pagination, and PowerShell 5.1 compatibility
The lab exercises organization, users, groups, group membership, subscribed SKUs, directory roles, directory-role members, devices, and Intune managed-device endpoints. Collection helpers follow @odata.nextLink, and hashtable-style response handling is used for Windows PowerShell 5.1 compatibility.
Validated results
Point-in-time tenant and device reporting
Validated results currently include 3 domains, 22 users, 12 groups, 1 subscribed SKU, 4 activated directory roles, 2 Entra devices, 19 licensed users, 3 unlicensed users, and 1 compliant encrypted Intune managed device. These values are documented as a point-in-time lab snapshot rather than permanent tenant state.
Troubleshooting
Permission and query failures preserved as evidence
A directory-role membership query returned 400 Bad Request until an unsupported $top parameter was removed. A stale-account query returned 403 Forbidden until AuditLog.Read.All was added and the Graph session was reauthenticated. These failures and corrections are documented instead of hidden.
Publication boundary
Reusable code public; live tenant exports withheld
The GitHub project publishes reusable scripts and sanitized validation notes. Live CSV exports are intentionally excluded because they can contain user principal names, object IDs, device identifiers, serial numbers, and other tenant-specific data.
Evidence
Sanitized validation
- GitHub labScripts, README, and troubleshooting documentation
- Tenant inventory scriptOrganization, users, groups, SKUs, roles, and Entra devices
- User inventory scriptUser state, sync state, and license mapping
- Group membership scriptGroup inventory and direct member reporting
- License reporting scriptSKU capacity and user assignment reporting
- Entra role scriptActivated directory roles and membership reporting
- Stale-account scriptSign-in activity and stale-account analysis
- Intune device scriptManaged-device compliance and encryption inventory
Microsoft 365 Graph Administration Lab / README.md
Microsoft 365 Graph Administration Lab
Status: COMPLETE — validated read-only Microsoft Graph administration and reporting automation.
Portfolio case study: https://jeremyfontenot.online/m365-graph-administration-lab.html
This lab demonstrates Microsoft 365, Microsoft Entra ID, Microsoft Intune, Microsoft Graph, and PowerShell administration using a Microsoft 365 E5 Developer tenant.
Objectives
- Build reusable Microsoft Graph administration tooling.
- Prefer read-only validation before configuration changes.
- Use delegated permissions with least-privilege scope selection.
- Exercise both Graph REST endpoints and PowerShell automation.
- Document authentication, permissions, API behavior, troubleshooting, and validation.
- Produce portfolio-quality evidence without creating meaningless tenant activity.
Validated scripts
| Script | Purpose | Status |
|---|---|---|
Get-M365TenantInventory.ps1 |
Tenant, domain, user, group, SKU, role, and Entra-device inventory | Validated |
Get-M365UserInventory.ps1 |
Detailed Entra/M365 user and licensing inventory | Validated |
Get-M365GroupMembership.ps1 |
Group and direct-membership reporting | Validated |
Get-M365LicenseAssignment.ps1 |
License capacity and assignment reporting | Validated |
Get-EntraRoleInventory.ps1 |
Activated Entra directory roles and membership | Validated |
Get-EntraStaleAccounts.ps1 |
Potentially stale account analysis using sign-in activity | Validated |
Get-IntuneManagedDeviceInventory.ps1 |
Intune managed-device inventory | Validated |
Validated environment snapshot
- Verified domains: 3
- Users: 22
- Groups: 12
- Subscribed SKUs: 1
- Activated directory roles: 4
- Entra devices: 2
- Licensed users: 19
- Unlicensed users: 3
- Intune managed devices: 1
These values are a point-in-time lab snapshot and will change as the environment evolves.
Graph permissions exercised
Organization.Read.AllUser.Read.AllGroup.Read.AllDirectory.Read.AllAuditLog.Read.AllDeviceManagementManagedDevices.Read.All
Graph endpoints exercised
GET /v1.0/organization
GET /v1.0/users
GET /v1.0/groups
GET /v1.0/groups/{id}/members
GET /v1.0/subscribedSkus
GET /v1.0/directoryRoles
GET /v1.0/directoryRoles/{id}/members
GET /v1.0/devices
GET /v1.0/deviceManagement/managedDevices
Validation highlights
- Device Code delegated authentication validated.
- Existing Graph sessions are checked for required scopes before reuse.
- Graph collection pagination is handled where supported.
- Windows PowerShell 5.1 hashtable behavior is handled explicitly.
- CSV reporting was validated for tenant, user, group, licensing, role, stale-account, and Intune inventory.
- A
400 Bad Requeston directory-role membership was traced to an unsupported$topquery parameter and corrected. - A
403 ForbiddenonsignInActivitywas traced to a missingAuditLog.Read.Allscope and corrected.
Repository layout
m365-graph-administration-lab/
├── README.md
├── scripts/
│ ├── Get-M365TenantInventory.ps1
│ ├── Get-M365UserInventory.ps1
│ ├── Get-M365GroupMembership.ps1
│ ├── Get-M365LicenseAssignment.ps1
│ ├── Get-EntraRoleInventory.ps1
│ ├── Get-EntraStaleAccounts.ps1
│ └── Get-IntuneManagedDeviceInventory.ps1
└── docs/
└── Troubleshooting.md
Completion and claim boundary
The intended portfolio scope is complete: seven read-only administration/reporting scripts were validated across Microsoft 365, Entra ID, and Intune. The project demonstrates delegated Microsoft Graph authentication, required-scope handling, read-only administration/reporting, CSV output, pagination, and documented troubleshooting. It does not claim Graph write automation or broader administrative coverage that was not validated.
Security and publication notes
Live CSV exports are intentionally not published because they can contain user principal names, object IDs, device identifiers, and other tenant-specific information. The public repository contains the reusable code and sanitized validation documentation only.
Troubleshooting Notes / Troubleshooting.md
Troubleshooting Notes
Microsoft.Graph.Authentication missing
Installed the authentication module:
Install-Module Microsoft.Graph.Authentication -Scope CurrentUser -Force
Validated module version during the lab: 2.41.0.
PowerShell execution policy blocked module loading
Corrected the current-user policy:
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
Windows PowerShell 5.1 Graph response behavior
Invoke-MgGraphRequest responses behaved as hashtables in this environment, so key indexing was used where appropriate:
$Org["displayName"]
Single-object count behavior
Graph collections were explicitly wrapped with @(...) so .Count remained reliable when only one object was returned.
Session-scoped project root
Reusable scripts determine the project root from their own location:
$ProjectRoot = Split-Path $PSScriptRoot -Parent
Directory-role members returned 400 Bad Request
The role list succeeded, but calls shaped like:
GET /v1.0/directoryRoles/{id}/members?$top=999
returned 400 Bad Request.
Removing $top corrected the query:
GET /v1.0/directoryRoles/{id}/members
All activated roles were then processed successfully.
signInActivity returned 403 Forbidden
An existing Graph session lacked AuditLog.Read.All. The stale-account script was changed to compare the required scopes with Get-MgContext.Scopes and reconnect when a required scope was missing.
After reauthentication with:
User.Read.All
AuditLog.Read.All
the user sign-in activity query completed successfully.
Disconnect-MgGraph MSAL cache warning
Disconnect-MgGraph displayed an authority-format warning while clearing the persisted MSAL token cache. The warning did not prevent a successful new Device Code authentication session.
Intune managed-device permission
The Intune inventory required:
DeviceManagementManagedDevices.Read.All
The script detected the missing scope, reauthenticated, and successfully retrieved the managed-device inventory.
Troubleshooting
A directory-role membership request returned 400 because it used an unsupported $top parameter. A signInActivity query returned 403 until the missing AuditLog.Read.All delegated scope was addressed.
Validation & results
Seven read-only scripts and CSV reporting are documented as validated. Live exports are deliberately excluded from the public repository.
Evidence
STRONG — seven validated read-only scripts, delegated authentication, scope handling, CSV/report output, Intune inventory, and documented Graph troubleshooting are preserved.
Skills demonstrated
View the complete capability map →Limitations & claim boundaries
Next milestones
Maintenance-only. Graph write operations or broader automation are optional future enhancements only when they serve a legitimate administrative requirement.