Force password change for all users in Microsoft 365 - Microsoft Graph Powershell Edition
Introduction
As the MSonline and AzureAD powershell modules have reached their end of life, it has become important to migrate old scripts using the retired module to the new Microsoft Graph Powershell.
In the interest of assisting with this migration I’ve been updating the Microsoft Documentation with the new commands, and while doing so I’ve come across some well-written blog posts linked within that had very good information but were using MSOnline/AzureAD cmdlets, so I’ve contacted the owners to receive authorization to re-write them for Microsoft Graph Powershell.
This is the first of one such blog posts. Original: https://www.michev.info/Blog/Post/1419/force-password-change-for-all-users-in-office-365
Preparation
Before starting the execution of the various commands let’s prepare the field by running or adding the following commands to the top of your script:
Connect-MgGraph -Scopes Directory.AccessAsUser.All, Directory.ReadWrite.All, User.ReadWrite.All
Select-MgProfile Beta
Single User
Preparation
We need to get the user’s UserID before we’re able to run commands against it, so let’s start with that:
$MGUserUPN = "john@contoso.com"
$MGUserId = Get-MgUser -UserId $MGUserUPN -ErrorAction Stop | Select-Object -ExpandProperty 'Id'
Now that we have the required UserID we can continue with the process for a single user.
Forcing user to change their password on next login
To force users to change their passwords on next login we need to prepare the PasswordProfile hashtable. There are a couple of ways to do so, but I’ll be going with the one provided in the excellent book “Office 365 for IT Pros”, linked at the end of this post.
Hashtable for changing password
If you want to change the password and then enforce password change afterwards by the user:
$passwordprofile = @{}
$passwordprofile["Password"] = "verycomplicatedpasswordthatyoushouldchangeorgenerateautomaticallyideally"
$passwordprofile["forceChangePasswordNextSignIn"] = $True
$passwordprofile["forceChangePasswordNextSignInWithMfa"] = $False
Note: We’re keeping forceChangePasswordNextSignInWithMfa disabled here because during testing it turned out that if the Password item is added to the hashtable the forceChangePasswordNextSignInWithMfa won’t be applied.
Hashtable for only forcing the user to change password at next login
If you just want to enforce the password change without changing the user’s password:
$passwordprofile = @{}
$passwordprofile["forceChangePasswordNextSignIn"] = $True
$passwordprofile["forceChangePasswordNextSignInWithMfa"] = $True
Note: Enabling forceChangePasswordNextSignInWithMfa if the user doesn’t have MFA enabled will force them to configure it.
Execution
Once we have the hashtable ready it’s time to execute:
Update-MgUser -UserId $MGUserId -PasswordProfile $passwordprofile -Verbose
To check whether the forceChangePasswordNextSignIn property has been correctly applied you can run:
Get-MgUser -UserId $MGUserId -Property * | Select-Object -ExpandProperty 'PasswordProfile'
And within a script this could be the result:
Getting the current password profile...
ForceChangePasswordNextSignIn :
ForceChangePasswordNextSignInWithMfa :
Password :
AdditionalProperties : {}
Updating the password profile...
Getting the updated password profile...
ForceChangePasswordNextSignIn : True
ForceChangePasswordNextSignInWithMfa : True
Password :
AdditionalProperties : {}
If you want to ensure that the user changes password sooner you can user Revoke-MgUserSignInSession to force the user to sign out from all devices:
Revoke-MgUserSignInSession -UserId $MGUserId
Multiple Users
Forcing multiple users to change passwords at next login
Assuming you’ll be using a CSV file as source of the users for whom the password needs to be changed:
$UsersToChangePassword = Import-Csv -Path "C:\Temp\passwordtoreset.csv"
#Defining the Password Profile
$passwordprofile = @{}
$passwordprofile["forceChangePasswordNextSignIn"] = $True
$passwordprofile["forceChangePasswordNextSignInWithMfa"] = $True
foreach ($UserToChangePassword in $UsersToChangePassword) {
#Preparing variables
$UserUPN = $UserToChangePassword.UserPrincipalName
$UserID = (Get-MgUser -UserId $UserUPN -ErrorAction Stop) | Select-Object -ExpandProperty 'Id'
Write-Output "Getting the current password profile for user $UserUPN..."
$CurrentPasswordProfile = Get-MgUser -UserId $UserID -Property * | Select-Object -ExpandProperty 'PasswordProfile' | Format-List
$CurrentPasswordProfile
Write-Output "Updating the password profile for user $UserUPN..."
Update-MgUser -UserId $UserID -PasswordProfile $passwordprofile
Write-Output "Getting the updated password profile for user $UserUPN..."
$CurrentPasswordProfile = Get-MgUser -UserId $UserID -Property * | Select-Object -ExpandProperty 'PasswordProfile' | Format-List
$CurrentPasswordProfile
}
This will force users to change password at the next sign-in.
Changing the password of multiple users and forcing them to change it again at next login
If you want to not just force a password change at sign-in but also directly change the user’s password you need to define it within the hashtable. As it’s not secure to set all users with the same password we’ll be using a function that will automatically generate a secure password for each user and then output a CSV file with the user’s new passwords:
$UsersToChangePassword = Import-Csv -Path "C:\Temp\passwordtoreset.csv"
#Preparing output object
$CSVOutput = @()
#Looping through the users in the CSV file and changing passwords
foreach ($UserToChangePassword in $UsersToChangePassword) {
#Preparing variables
$UserUPN = $UserToChangePassword.UserPrincipalName
$UserID = (Get-MgUser -UserId $UserUPN -ErrorAction Stop) | Select-Object -ExpandProperty 'Id'
#Creating a random strong password
Add-Type -AssemblyName 'System.Web'
$NewPassword = [System.Web.Security.Membership]::GeneratePassword(16, 3)
#Defining the Password Profile
$passwordprofile = @{}
$passwordprofile["Password"] = $NewPassword
$passwordprofile["forceChangePasswordNextSignIn"] = $True
$passwordprofile["forceChangePasswordNextSignInWithMfa"] = $False
Write-Output "Getting the current password profile for user $UserUPN..."
$CurrentPasswordProfile = Get-MgUser -UserId $UserID -Property * | Select-Object -ExpandProperty 'PasswordProfile' | Format-List
$CurrentPasswordProfile
Write-Output "Updating the password profile for user $UserUPN..."
try {
Update-MgUser -UserId $UserID -PasswordProfile $passwordprofile -ErrorAction Stop
Write-Output "Password updated successfully for user $UserUPN..."
$CSVOutput += [PSCustomObject]@{
UserPrincipalName = $UserUPN
NewPassword = $NewPassword
Status = "Success"
}
}
catch {
Write-Output "Password update failed for user $UserUPN..."
$CSVOutput += [PSCustomObject]@{
UserPrincipalName = $UserUPN
NewPassword = $NewPassword
Status = "Failed"
}
Write-Warning $_.Exception.Message
}
Write-Output "Getting the updated password profile for user $UserUPN..."
$CurrentPasswordProfile = Get-MgUser -UserId $UserID -Property * | Select-Object -ExpandProperty 'PasswordProfile' | Format-List
$CurrentPasswordProfile
}
#Outputting results and new passwords to a CSV file
$CSVOutput | Export-Csv -Path "C:\Temp\passwordtoresetoutput.csv" -NoTypeInformation
All Users
Forcing ALL users to change their password at next login
While I don’t recommend doing so, you can do this to all users as well by using this code for just enforcing password change on next sign-in without actually changing the password:
$UsersToChangePassword = Get-MgUser -All -Property UserPrincipalName, Id
$CSVOutput = @()
foreach ($UserToChangePassword in $UsersToChangePassword) {
#Preparing variables
$UserUPN = $UserToChangePassword.UserPrincipalName
$UserID = $UserToChangePassword.Id
#Defining the Password Profile
$passwordprofile = @{}
$passwordprofile["forceChangePasswordNextSignIn"] = $True
$passwordprofile["forceChangePasswordNextSignInWithMfa"] = $True
Write-Output "Getting the current password profile for user $UserUPN..."
$CurrentPasswordProfile = Get-MgUser -UserId $UserID -Property * | Select-Object -ExpandProperty 'PasswordProfile' | Format-List
$CurrentPasswordProfile
Write-Output "Updating the password profile for user $UserUPN..."
try {
Update-MgUser -UserId $UserID -PasswordProfile $passwordprofile -ErrorAction Stop
Write-Output "Password profile updated successfully for user $UserUPN..."
}
catch {
Write-Output "Password profile update failed for user $UserUPN..."
Write-Warning $_.Exception.Message
}
Write-Output "Getting the updated password profile for user $UserUPN..."
$CurrentPasswordProfile = Get-MgUser -UserId $UserID -Property * | Select-Object -ExpandProperty 'PasswordProfile' | Format-List
$CurrentPasswordProfile
}
Changing the password of ALL users and forcing them to change it again at next login
Or this one to also change the password:
$UsersToChangePassword = Get-MgUser -All -Property UserPrincipalName, Id
$CSVOutput = @()
foreach ($UserToChangePassword in $UsersToChangePassword) {
#Preparing variables
$UserUPN = $UserToChangePassword.UserPrincipalName
$UserID = $UserToChangePassword.Id
#Creating a random strong password
Add-Type -AssemblyName 'System.Web'
$NewPassword = [System.Web.Security.Membership]::GeneratePassword(16, 3)
#Defining the Password Profile
$passwordprofile = @{}
$passwordprofile["Password"] = $NewPassword
$passwordprofile["forceChangePasswordNextSignIn"] = $True
$passwordprofile["forceChangePasswordNextSignInWithMfa"] = $False
Write-Output "Getting the current password profile for user $UserUPN..."
$CurrentPasswordProfile = Get-MgUser -UserId $UserID -Property * | Select-Object -ExpandProperty 'PasswordProfile' | Format-List
$CurrentPasswordProfile
Write-Output "Updating the password profile for user $UserUPN..."
try {
Update-MgUser -UserId $UserID -PasswordProfile $passwordprofile -ErrorAction Stop
Write-Output "Password updated successfully for user $UserUPN..."
$CSVOutput += [PSCustomObject]@{
UserPrincipalName = $UserUPN
NewPassword = $NewPassword
Status = "Success"
}
}
catch {
Write-Output "Password update failed for user $UserUPN..."
$CSVOutput += [PSCustomObject]@{
UserPrincipalName = $UserUPN
NewPassword = $NewPassword
Status = "Failed"
}
Write-Warning $_.Exception.Message
}
Write-Output "Getting the updated password profile for user $UserUPN..."
$CurrentPasswordProfile = Get-MgUser -UserId $UserID -Property * | Select-Object -ExpandProperty 'PasswordProfile' | Format-List
$CurrentPasswordProfile
}
$CSVOutput | Export-Csv -Path "C:\Temp\passwordtoresetoutput.csv" -NoTypeInformation
Note: Be aware that this will change EVERYONE’s passwords, including the one of the account used to run this script and those of other admins. It is strongly recommended to filter out admins from this script or there is a big risk of being locked out of the tenant and solving that requires a very lenghty recovery process with Microsoft.
Filtering
You can also filter by a lot of different characteristics by replacing
$UsersToChangePassword = Get-MgUser -All -Property UserPrincipalName, Id
with
$UsersToChangePassword = Get-MgUser -ConsistencyLevel eventual -Filter "startsWith(UserPrincipalName, 'graph')" -Property UserPrincipalName, Id
or
$UsersToChangePassword = Get-MgUser -ConsistencyLevel eventual -Search '"UserPrincipalName:graph"' -Property UserPrincipalName, Id
A full explanation on filtering is available in the links at the end of this blog post.
References
- https://office365itpros.com/
- https://github.com/12Knocksinna/Office365itpros
- https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.users/update-mguser?view=graph-powershell-1.0
- https://learn.microsoft.com/en-us/powershell/microsoftgraph/azuread-msoline-cmdlet-map?view=graph-powershell-1.0
- https://learn.microsoft.com/en-us/graph/api/resources/passwordprofile?view=graph-rest-1.0
- https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-hashtable?view=powershell-7.3
- https://learn.microsoft.com/en-us/dotnet/api/system.web.security.membership.generatepassword?view=netframework-4.8
- https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.users/get-mguser?view=graph-powershell-1.0
- https://learn.microsoft.com/en-us/graph/aad-advanced-queries?tabs=powershell