Extract all users with powershell and what to do about it part two
In a previous post I showed how to extract all users from a Microsoft 365 tenant, and what you should do about that. If you followed along that leak got restricted. The Azure AD module isn’t the only way to extract user information from a tenant. This post will show you how to do the same (extract all users to csv file) with the Graph PowerShell modules and what you should do about that.
Install Graph PowerShell modules
First you need to install some PowerShell modules:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Install-Module Microsoft.Graph.Authentication -Scope CurrentUser
Install-Module Microsoft.Graph.Users -Scope CurrentUser
# or just importing if previously installed
# Import-Module Microsoft.Graph.Authentication
# Import-Module Microsoft.Graph.Users
All available users to CSV
Let’s say you want all the available users in your tenant “safely” stored in a CSV file on your local machine.
# This will open a Microsoft login screen and save the resulting session
$graphSession = Connect-MgGraph -Scopes "User.Read.All"
# Create temp file
$filename = $(New-TemporaryFile).FullName + ".csv" # or "C:\temp\user.csv"
# This command will load the top 5 users, but you can modify this accordingly. It's just a demonstration....
# see https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.users/get-mguser?view=graph-powershell-1.0&wt.mc_id=SEC-MVP-5004985
#
# it selects some properties (adjust accordingly)
# and exports to the filename
Get-MgUser -Top 5 `
| Select-Object -Property Id,AccountEnabled,UserPrincipalName,Mail,GivenName,Surname,DisplayName,JobTitle `
| Export-Csv -Path $filename
Write-Host "Users exported to $filename"
That was easy, 6 lines of “code” and you have a CSV file with all the users in your organization.
Is this a problem?
Not exactly, but it might be a problem if you have a lot of users. Such a csv file with hundreds/thousands users might be considered a data leak. Still no problem, because powershell is only for administrative accounts, right? RIGHT?? Wrong… By default EVERY user in your Microsoft 365 tenant is able to do this read action with powershell.
Restrict access to Graph powershell
Let’s see if we can find a way to prevent access for most users. As said this command Connect-MgGraph
triggers a login screen, since I know which user logged-in to this PowerShell module at what exact time, I could directly find an entry in the Sign-in logs for this user, pointing me in the right direction.
I found a login for application Microsoft Graph PowerShell with ID: 14d82eec-204b-4c2f-b7e8-296a70dab67e
.
This application did request access to Microsoft Graph with ID: 00000003-0000-0000-c000-000000000000
so the sign-in logs prove the Microsoft Graph PowerShell module is actually using the Graph API.
At first I opened Enterprise Applications, switched to Microsoft Applications and looked for Microsoft Graph
. The idea was, if I find the Microsoft Graph PowerShell application there, I’ll just turn on User assignment required and only allow the few users I want.
No luck there. It shows the resource application though, but I don’t want to mess with the Azure AD internals. The resource application Microsoft Graph
SHOULD NOT BE TOUCHED. You will mess up a lot!
If you switch to All Applications the Microsoft Graph PowerShell application shows up. Just flip the switch Assignment required to Yes
and press save. Go to Users and Groups and assign the application to the users who require it.
Block Microsoft Graph module with PowerShell
By design not all options are shown on the Azure AD portal, and messing with options that are hidden by design might lead to unrecoverable results. Check the script below, careful, before executing (as with any script from a remote source).
This script uses powershell to change the service principal of the AzureAD application that is used to get tokens.
Connect-MgGraph -Scopes 'Application.ReadWrite.All'
$appId = "14d82eec-204b-4c2f-b7e8-296a70dab67e" # Microsoft Graph Powershell
# Get or create service principal
$sp = Get-MgServicePrincipal -Filter "appId eq '$appId'"
if (-not $sp) {
$sp = New-MgServicePrincipal -AppId $appId
}
$ServicePrincipalUpdate =@{
"appRoleAssignmentRequired" = "true"
}
Update-MgServicePrincipal -ServicePrincipalId $sp.Id -BodyParameter $ServicePrincipalUpdate
No more user access to this module
If some other user tries to use the Graph PowerShell modules, they are greeted with an AADSTS50105
error meaning you successfully blocked the application for other users. The error is descriptive enough for users who actually need this module to know what to do (ask the admin to grant access).
Unblock access to Graph PowerShell module
Connect-MgGraph -Scopes 'Application.ReadWrite.All'
$appId = "14d82eec-204b-4c2f-b7e8-296a70dab67e" # Microsoft Graph Powershell
# Get or create service principal
$sp = Get-MgServicePrincipal -Filter "appId eq '$appId'"
if (-not $sp) {
$sp = New-MgServicePrincipal -AppId $appId
}
$ServicePrincipalUpdate =@{
"appRoleAssignmentRequired" = "false"
}
Update-MgServicePrincipal -ServicePrincipalId $sp.Id -BodyParameter $ServicePrincipalUpdate
Conclusion
In my opinion this module and the one from the previous post should be blocked by default (maybe by the security defaults?) Be sure to share this post with all other system administrators you know. It will take them less then 5 minutes to block these modules and that makes their organization just a little more secure.