Project 13 / Automation

Microsoft 365 Graph Administration Lab

Seven validated read-only scripts for tenant, user, group, license, role, stale-account, and Intune reporting.

COMPLETEDeveloper environment

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.

Microsoft GraphPowerShellREST APICSV

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.

7 scriptsValidated
Microsoft GraphREST + PowerShell
Read-onlyCurrent scope
IntuneIntegrated

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

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.All
  • User.Read.All
  • Group.Read.All
  • Directory.Read.All
  • AuditLog.Read.All
  • DeviceManagementManagedDevices.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 Request on directory-role membership was traced to an unsupported $top query parameter and corrected.
  • A 403 Forbidden on signInActivity was traced to a missing AuditLog.Read.All scope 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

Microsoft GraphPowerShellREST APICSV
View the complete capability map →

Limitations & claim boundaries

Read-only delegated administration/reporting is currently proven. Live CSV exports are intentionally not public because they may contain UPNs, object IDs, device identifiers, and tenant-specific data. Do not imply write automation or broader Graph coverage that has not been validated.

Next milestones

Maintenance-only. Graph write operations or broader automation are optional future enhancements only when they serve a legitimate administrative requirement.

Continue exploring

Related projects