Skip to content

Commit a048157

Browse files
authored
Merge pull request #884 from divya-akula/GetGraphUsers
Add PowerShell Script to Export Microsoft 365 Active Users via Microsoft Graph (Cross-Platform)
2 parents 28621f2 + 4008357 commit a048157

File tree

6 files changed

+234
-0
lines changed

6 files changed

+234
-0
lines changed
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
2+
# Export Microsoft 365 Active Users to CSV Using Microsoft Graph (Cross-Platform)
3+
4+
## Summary
5+
6+
This PowerShell script exports Microsoft 365 users — including properties such as Display Name, UPN, Department, Job Title, Account Status, and Manager Email — into a CSV file using the Microsoft Graph API.
7+
8+
It automatically ensures that the Microsoft.Graph module is installed, connects securely using interactive or device-code authentication, and retrieves enabled users by default (with optional switches for guests, disabled accounts, and manager lookups).
9+
10+
The script is fully optimized for macOS and Linux, performing a safe write-access check on the Graph cache directory (~/.mg) to prevent permission issues caused by previous sudo runs.
11+
If you are running on Windows, that permission check can be safely skipped or removed since Windows manages Graph credentials differently.
12+
13+
14+
15+
# [Microsoft Graph PowerShell](#tab/graphps)
16+
17+
```powershell
18+
19+
<#
20+
.SYNOPSIS
21+
Export active Microsoft 365 users to CSV using Microsoft Graph.
22+
23+
.DESCRIPTION
24+
Wraps the script in an advanced function that ensures the Microsoft.Graph module is available,
25+
connects using device code (with a macOS-friendly context scope), queries enabled users by default,
26+
optionally looks up manager email, and writes a CSV. Includes a fallback to interactive login if
27+
device code fails on macOS environments.
28+
29+
.EXAMPLE
30+
Export-M365ActiveUsers
31+
Exports enabled Member users to Desktop/M365_ActiveUsers.csv using device-code authentication.
32+
33+
.EXAMPLE
34+
Export-M365ActiveUsers -IncludeGuests -Path "/tmp/users.csv"
35+
Includes Guests in addition to Members and writes to /tmp/users.csv
36+
#>
37+
function Export-M365ActiveUsers {
38+
[CmdletBinding(SupportsShouldProcess)]
39+
param(
40+
[Parameter()]
41+
[string]$Path = 'M365_ActiveUsers.csv',
42+
43+
[Parameter()]
44+
[string]$TenantId = $(Read-Host "Enter your Azure AD Tenant ID"),
45+
46+
[Parameter()]
47+
[string[]]$Scopes = @('User.Read.All','Directory.Read.All'),
48+
49+
[Parameter()]
50+
[switch]$IncludeGuests,
51+
52+
[Parameter()]
53+
[switch]$IncludeDisabled,
54+
55+
[Parameter()]
56+
[switch]$IncludeManager = $true,
57+
58+
[Parameter()]
59+
[switch]$DeviceCode = $true,
60+
61+
[Parameter()]
62+
[switch]$Force
63+
)
64+
65+
# 1) Ensure Graph module exists
66+
$moduleName = 'Microsoft.Graph'
67+
if (-not (Get-Module -ListAvailable -Name $moduleName)) {
68+
Write-Host "Microsoft Graph module not found. Installing to CurrentUser scope..." -ForegroundColor Yellow
69+
try {
70+
Install-Module -Name $moduleName -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop | Out-Null
71+
} catch {
72+
throw "Failed to install module '$moduleName': $($_.Exception.Message)"
73+
}
74+
}
75+
Import-Module -Name $moduleName -ErrorAction Stop
76+
77+
# Preflight check: warn if ~/.mg is owned by root (from a previous sudo run)
78+
$platform = [System.Environment]::OSVersion.Platform
79+
if ($platform -eq 'Unix' -or $platform -eq 'MacOSX') {
80+
$mgDir = Join-Path $HOME '.mg'
81+
if (Test-Path $mgDir) {
82+
try {
83+
$testFile = Join-Path $mgDir "test_write_$(Get-Random).tmp"
84+
[void][System.IO.File]::WriteAllText($testFile, 'test')
85+
Remove-Item $testFile -Force -ErrorAction SilentlyContinue
86+
} catch {
87+
Write-Warning "Cannot write to $mgDir — likely owned by root from a previous sudo run."
88+
Write-Host "Fix with: sudo chown -R `$USER:staff ~/.mg && sudo chmod 700 ~/.mg" -ForegroundColor Yellow
89+
Write-Host "Or remove it: sudo rm -rf ~/.mg`n" -ForegroundColor Yellow
90+
throw "Access denied to Microsoft Graph cache directory. See instructions above."
91+
}
92+
}
93+
} else {
94+
Write-Verbose "Windows detected — skipping ~/.mg permission check."
95+
}
96+
97+
# 2) Connect to Graph
98+
Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
99+
100+
Write-Host "`n🔐 Connecting to Microsoft Graph..." -ForegroundColor Cyan
101+
Write-Host "A browser window will open for authentication.`n" -ForegroundColor Yellow
102+
103+
try {
104+
# Use interactive browser login - more reliable on macOS than device code
105+
# ContextScope Process keeps tokens in memory only (not persisted to disk)
106+
Connect-MgGraph -Scopes $Scopes -TenantId $TenantId -ContextScope Process -ErrorAction Stop
107+
} catch {
108+
$msg = $_.Exception.Message
109+
Write-Error "Failed to connect to Microsoft Graph: $msg"
110+
Write-Host "`n💡 Troubleshooting:" -ForegroundColor Yellow
111+
Write-Host " 1. Make sure you complete the browser sign-in" -ForegroundColor Yellow
112+
Write-Host " 2. If the browser doesn't open, check for popup blockers" -ForegroundColor Yellow
113+
Write-Host " 3. Close all browser windows and try again`n" -ForegroundColor Yellow
114+
throw
115+
}
116+
117+
$context = Get-MgContext
118+
if (-not $context) { throw "Authentication context not available after login." }
119+
Write-Host "✅ Connected as: $($context.Account)" -ForegroundColor Green
120+
121+
# 3) Query users
122+
$filters = @()
123+
if (-not $IncludeDisabled) { $filters += "accountEnabled eq true" }
124+
if (-not $IncludeGuests) { $filters += "userType eq 'Member'" }
125+
$filterStr = $filters -join ' and '
126+
127+
$props = @('Id','DisplayName','UserPrincipalName','Department','JobTitle','AccountEnabled')
128+
if ([string]::IsNullOrWhiteSpace($filterStr)) {
129+
$users = Get-MgUser -All -Property $props
130+
} else {
131+
$users = Get-MgUser -All -Filter $filterStr -Property $props
132+
}
133+
134+
# 4) Project output
135+
$export = @()
136+
foreach ($u in $users) {
137+
$mgr = $null
138+
if ($IncludeManager) {
139+
try {
140+
$mgrObj = Get-MgUserManager -UserId $u.Id -ErrorAction SilentlyContinue
141+
if ($mgrObj) { $mgr = $mgrObj.AdditionalProperties.mail }
142+
} catch {}
143+
}
144+
$export += [PSCustomObject]@{
145+
EmployeeId = $u.Id
146+
EmployeeName = $u.DisplayName
147+
Email = $u.UserPrincipalName
148+
Department = $u.Department
149+
JobTitle = $u.JobTitle
150+
ManagerEmail = $mgr
151+
AccountEnabled = $u.AccountEnabled
152+
}
153+
}
154+
155+
# 5) Export
156+
if ($PSCmdlet.ShouldProcess($Path, 'Export users to CSV')) {
157+
$export | Export-Csv -Path $Path -NoTypeInformation -Encoding UTF8 -Force:$Force
158+
Write-Host "📄 Export complete. File saved to $Path" -ForegroundColor Cyan
159+
}
160+
161+
return $export
162+
}
163+
164+
# Run with defaults if executed directly
165+
Export-M365ActiveUsers | Out-Null
166+
```
167+
168+
[!INCLUDE [More about Microsoft Graph PowerShell SDK](../../docfx/includes/MORE-GRAPHSDK.md)]
169+
***
170+
171+
## Contributors
172+
173+
| Author(s) |
174+
|-----------|
175+
| [Divya Akula](https://github.com/divya-akula)|
176+
177+
[!INCLUDE [DISCLAIMER](../../docfx/includes/DISCLAIMER.md)]
178+
179+
<img src="https://m365-visitor-stats.azurewebsites.net/script-samples/scripts/graph-get-teams-tabs-export-to-csv" aria-hidden="true" />
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"EmployeeId","EmployeeName","Email","Department","JobTitle","ManagerEmail","AccountEnabled"
2+
"0213a8be-3d6c-4275-ad1b-3b4f5ca6a02b","Divya Akula","DivyaAkula@DevDefender.onmicrosoft.com",,,,"True"
20 KB
Loading
48.3 KB
Loading
114 KB
Loading
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
[
2+
{
3+
"name": "graph-get-users-export-to-csv",
4+
"source": "pnp",
5+
"title": "Export Microsoft 365 Active Users to CSV Using Microsoft Graph (Cross-Platform)",
6+
"shortDescription": "This PowerShell script exports Microsoft 365 users — including properties into a CSV file using the Microsoft Graph API.",
7+
"url": "https://pnp.github.io/script-samples/graph-get-users-export-to-csv/README.html",
8+
"longDescription": [
9+
""
10+
],
11+
"creationDateTime": "2025-11-02",
12+
"updateDateTime": "2025-11-02",
13+
"products": [
14+
"Graph"
15+
],
16+
"metadata": [
17+
{
18+
"key": "GRAPH-POWERSHELL",
19+
"value": "1.0.0"
20+
}
21+
],
22+
"categories": [
23+
"Data",
24+
"Report"
25+
],
26+
"tags": [
27+
"<Cmdlets-Used>"
28+
],
29+
"thumbnails": [
30+
{
31+
"type": "image",
32+
"order": 100,
33+
"url": "https://raw.githubusercontent.com/pnp/script-samples/main/scripts/graph-get-users-export-to-csv/assets/preview.jpg",
34+
"alt": "Preview of the sample Export Microsoft 365 Active Users to CSV Using Microsoft Graph (Cross-Platform)"
35+
}
36+
],
37+
"authors": [
38+
{
39+
"gitHubAccount": "divya-akula",
40+
"company": "",
41+
"pictureUrl": "https://github.com/divya-akula.png",
42+
"name": "Divya Akula"
43+
}
44+
],
45+
"references": [
46+
{
47+
"name": "Want to learn more about Microsoft Graph PowerShell SDK and the cmdlets",
48+
"description": "Check out the Microsoft Graph PowerShell SDK documentation site to get started and for the reference to the cmdlets.",
49+
"url": "https://learn.microsoft.com/graph/powershell/get-started"
50+
}
51+
]
52+
}
53+
]

0 commit comments

Comments
 (0)