ANAVEM
Reference
Languagefr
How to Force Password Changes for All Users in Microsoft 365 Using PowerShell

How to Force Password Changes for All Users in Microsoft 365 Using PowerShell

Learn to force password changes for single users, groups, or all users in Microsoft 365 using PowerShell and Microsoft Graph API with automated scripts and verification commands.

Emanuel DE ALMEIDAEmanuel DE ALMEIDA
March 17, 2026 15 min 11
mediummicrosoft365 9 steps 15 min

Why Force Password Changes in Microsoft 365?

Password security remains one of the most critical aspects of organizational cybersecurity. Whether responding to a security breach, implementing new password policies, or conducting routine security maintenance, the ability to force password changes across your Microsoft 365 environment is essential. This tutorial focuses on the modern Microsoft Graph PowerShell approach, which has replaced the deprecated MSOnline module as the standard for Microsoft 365 administration.

What Makes Microsoft Graph PowerShell the Right Choice?

Microsoft Graph PowerShell SDK represents the current standard for Microsoft 365 automation and administration. Unlike the legacy MSOnline module that's being phased out, Graph PowerShell provides comprehensive access to Microsoft 365 services through a unified API. The Update-MgUser cmdlet with PasswordProfile parameters offers granular control over user password policies, including the critical ForceChangePasswordNextSignIn setting.

What Will You Accomplish in This Tutorial?

You'll master the complete workflow for forcing password changes in Microsoft 365, from single-user operations to enterprise-scale bulk processing. This includes proper authentication setup, administrative account protection, error handling, progress tracking, and comprehensive verification procedures. You'll also learn advanced techniques like temporary password assignment and CSV-based targeted operations, ensuring you can handle any password management scenario your organization requires.

Related: How to Export BitLocker Recovery Keys from Active Directory

Related: How to Enable Remote Desktop on Windows Server Using

Implementation Guide

Full Procedure

01

Install Microsoft Graph PowerShell SDK

First, you need to install the Microsoft Graph PowerShell SDK module, which replaces the deprecated MSOnline module. Open PowerShell as Administrator and run the installation command.

Install-Module Microsoft.Graph -Repository PSGallery -AllowClobber -Force

The installation might take several minutes as it downloads multiple sub-modules. If prompted about installing from an untrusted repository, type 'Y' to continue.

Pro tip: Use the -AllowClobber parameter to overwrite any existing cmdlets with the same names, ensuring you get the latest versions.

Verification: Check the installed module version:

Get-Module Microsoft.Graph -ListAvailable | Select-Object Name, Version

You should see the Microsoft.Graph module listed with the current version number.

02

Connect to Microsoft Graph with Required Permissions

Establish a connection to Microsoft Graph with the necessary permissions to modify user password policies. The User.ReadWrite.All scope provides the required access to update user password settings.

Connect-MgGraph -Scopes "User.ReadWrite.All"

This command will open a browser window for authentication. Sign in with your Microsoft 365 administrator account and consent to the requested permissions.

Warning: Always use an account with appropriate administrative privileges. Global Administrator or User Administrator roles are required for bulk password operations.

Verification: Confirm your connection and permissions:

Get-MgContext | Select-Object Account, Scopes

The output should show your authenticated account and the User.ReadWrite.All scope in the permissions list.

03

Force Password Change for a Single User

Before running bulk operations, test the process on a single user account. This helps verify your permissions and understand the process flow.

$UserPrincipalName = "testuser@yourdomain.com"
Update-MgUser -UserId $UserPrincipalName -PasswordProfile @{
    ForceChangePasswordNextSignIn = $true
}

Replace 'testuser@yourdomain.com' with the actual user's email address. This command sets the ForceChangePasswordNextSignIn flag to true, requiring the user to change their password at next login.

Verification: Check if the password change requirement was applied:

Get-MgUser -UserId $UserPrincipalName -Property "UserPrincipalName,PasswordProfile" |
Select-Object UserPrincipalName, @{Name="MustChangePassword";Expression={$_.PasswordProfile.ForceChangePasswordNextSignIn}}

The output should show 'True' in the MustChangePassword column, confirming the setting was applied successfully.

04

Retrieve All Users and Prepare for Bulk Operations

Before forcing password changes for all users, retrieve the complete user list and prepare your environment for bulk processing. This step helps you understand the scope of the operation.

$AllUsers = Get-MgUser -All -Property "UserPrincipalName,DisplayName,AccountEnabled"
$TotalUsers = $AllUsers.Count
Write-Host "Total users found: $TotalUsers" -ForegroundColor Cyan

This command retrieves all users with essential properties. The -All parameter ensures you get every user, not just the first 100 (default limit).

Pro tip: Filter out disabled accounts and service accounts before proceeding to avoid unnecessary operations on inactive users.

Filter active users only:

$ActiveUsers = $AllUsers | Where-Object {$_.AccountEnabled -eq $true}
Write-Host "Active users: $($ActiveUsers.Count)" -ForegroundColor Green

Verification: Display a sample of users to review:

$ActiveUsers | Select-Object UserPrincipalName, DisplayName | Select-Object -First 5
05

Create Administrative Account Exclusion List

Critical step: Create an exclusion list for administrative accounts to prevent locking yourself out of the system. This is essential for maintaining access during and after the password change operation.

# Define administrative accounts to exclude
$AdminAccounts = @(
    "admin@yourdomain.com",
    "globaladmin@yourdomain.com",
    "serviceaccount@yourdomain.com"
)

# Filter out administrative accounts
$UsersToUpdate = $ActiveUsers | Where-Object {
    $_.UserPrincipalName -notin $AdminAccounts
}

Write-Host "Users to update after exclusions: $($UsersToUpdate.Count)" -ForegroundColor Yellow
Write-Host "Excluded admin accounts: $($AdminAccounts.Count)" -ForegroundColor Red

Customize the $AdminAccounts array with your organization's administrative account email addresses. This prevents accidentally forcing password changes on critical service accounts.

Warning: Never skip this step. Forcing password changes on administrative accounts can result in service disruptions and loss of access to critical systems.

Verification: Review the exclusion list:

Write-Host "Excluded accounts:" -ForegroundColor Red
$AdminAccounts | ForEach-Object { Write-Host "  - $_" -ForegroundColor Red }
06

Execute Bulk Password Change with Progress Tracking

Now execute the bulk password change operation with comprehensive error handling and progress tracking. This script processes each user individually and provides detailed feedback.

# Initialize counters
$SuccessCount = 0
$ErrorCount = 0
$ProcessedCount = 0
$TotalToProcess = $UsersToUpdate.Count

# Process each user with error handling
ForEach ($User in $UsersToUpdate) {
    try {
        Update-MgUser -UserId $User.UserPrincipalName -PasswordProfile @{
            ForceChangePasswordNextSignIn = $true
        }
        $SuccessCount++
        Write-Host "✓ Successfully updated: $($User.UserPrincipalName)" -ForegroundColor Green
    }
    catch {
        $ErrorCount++
        Write-Host "✗ Failed to update $($User.UserPrincipalName): $($_.Exception.Message)" -ForegroundColor Red
    }
    
    $ProcessedCount++
    $PercentComplete = [math]::Round(($ProcessedCount / $TotalToProcess) * 100, 2)
    Write-Progress -Activity "Forcing Password Changes" -Status "Processing $($User.UserPrincipalName)" -PercentComplete $PercentComplete
    
    # Add small delay to avoid throttling
    Start-Sleep -Milliseconds 500
}

Write-Host "`nOperation completed:" -ForegroundColor Cyan
Write-Host "  Success: $SuccessCount" -ForegroundColor Green
Write-Host "  Errors: $ErrorCount" -ForegroundColor Red
Write-Host "  Total Processed: $ProcessedCount" -ForegroundColor Cyan

Verification: Check a random sample of updated users:

$SampleUsers = $UsersToUpdate | Get-Random -Count 3
$SampleUsers | ForEach-Object {
    $User = Get-MgUser -UserId $_.UserPrincipalName -Property "UserPrincipalName,PasswordProfile"
    [PSCustomObject]@{
        UserPrincipalName = $User.UserPrincipalName
        MustChangePassword = $User.PasswordProfile.ForceChangePasswordNextSignIn
    }
}
07

Force Password Changes with Temporary Password Assignment

For enhanced security, you can force password changes while simultaneously assigning temporary passwords. This ensures users get new credentials immediately and must change them at next login.

# Function to generate secure temporary passwords
function New-TempPassword {
    param([int]$Length = 12)
    $Characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*'
    $Password = -join ((1..$Length) | ForEach {$Characters[(Get-Random -Maximum $Characters.Length)]})
    return $Password
}

# Array to store results
$PasswordResults = @()

# Process users with temporary password assignment
ForEach ($User in $UsersToUpdate) {
    try {
        $TempPassword = New-TempPassword
        Update-MgUser -UserId $User.UserPrincipalName -PasswordProfile @{
            Password = $TempPassword
            ForceChangePasswordNextSignIn = $true
        }
        
        $PasswordResults += [PSCustomObject]@{
            UserPrincipalName = $User.UserPrincipalName
            DisplayName = $User.DisplayName
            TempPassword = $TempPassword
            Status = "Success"
            Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        }
        
        Write-Host "✓ Password updated for: $($User.UserPrincipalName)" -ForegroundColor Green
    }
    catch {
        $PasswordResults += [PSCustomObject]@{
            UserPrincipalName = $User.UserPrincipalName
            DisplayName = $User.DisplayName
            TempPassword = "N/A"
            Status = "Failed: $($_.Exception.Message)"
            Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        }
        Write-Host "✗ Failed to update password for $($User.UserPrincipalName)" -ForegroundColor Red
    }
    
    Start-Sleep -Milliseconds 500
}

Export results to CSV:

$ExportPath = "C:\Temp\PasswordReset-$(Get-Date -Format 'yyyyMMdd-HHmmss').csv"
$PasswordResults | Export-Csv -Path $ExportPath -NoTypeInformation
Write-Host "Results exported to: $ExportPath" -ForegroundColor Cyan
Warning: Store the CSV file containing temporary passwords in a secure location and delete it after distributing passwords to users. Never email passwords in plain text.
08

Process Users from CSV File for Targeted Operations

For targeted password changes, you can process specific users from a CSV file. This approach is useful when you need to force password changes for specific departments, groups, or security incidents.

First, create a CSV file with the following structure:

UserPrincipalName,DisplayName,Department
user1@yourdomain.com,John Doe,IT
user2@yourdomain.com,Jane Smith,HR
user3@yourdomain.com,Bob Johnson,Finance

Save this as 'users.csv' in C:\Temp\ directory. Then process the users:

# Import users from CSV
$CSVPath = "C:\Temp\users.csv"
if (Test-Path $CSVPath) {
    $UsersFromCSV = Import-Csv $CSVPath
    Write-Host "Imported $($UsersFromCSV.Count) users from CSV" -ForegroundColor Cyan
    
    # Process each user from CSV
    $CSVResults = @()
    $UsersFromCSV | ForEach-Object {
        try {
            Update-MgUser -UserId $_.UserPrincipalName -PasswordProfile @{
                ForceChangePasswordNextSignIn = $true
            }
            
            $CSVResults += [PSCustomObject]@{
                UserPrincipalName = $_.UserPrincipalName
                DisplayName = $_.DisplayName
                Department = $_.Department
                Status = "Success"
                ProcessedTime = Get-Date
            }
            
            Write-Host "✓ Updated: $($_.UserPrincipalName)" -ForegroundColor Green
        }
        catch {
            $CSVResults += [PSCustomObject]@{
                UserPrincipalName = $_.UserPrincipalName
                DisplayName = $_.DisplayName
                Department = $_.Department
                Status = "Failed: $($_.Exception.Message)"
                ProcessedTime = Get-Date
            }
            Write-Host "✗ Failed: $($_.UserPrincipalName)" -ForegroundColor Red
        }
        
        Start-Sleep -Milliseconds 300
    }
    
    # Display summary
    $SuccessfulCSV = ($CSVResults | Where-Object {$_.Status -eq "Success"}).Count
    $FailedCSV = ($CSVResults | Where-Object {$_.Status -ne "Success"}).Count
    
    Write-Host "`nCSV Processing Summary:" -ForegroundColor Cyan
    Write-Host "  Successful: $SuccessfulCSV" -ForegroundColor Green
    Write-Host "  Failed: $FailedCSV" -ForegroundColor Red
}
else {
    Write-Host "CSV file not found at $CSVPath" -ForegroundColor Red
}
Pro tip: Use CSV processing for incident response scenarios where you need to quickly force password changes for compromised accounts or specific user groups.

Verification: Export CSV results for documentation:

$CSVResults | Export-Csv -Path "C:\Temp\CSV-PasswordReset-Results-$(Get-Date -Format 'yyyyMMdd-HHmmss').csv" -NoTypeInformation
09

Verify Password Change Requirements and Generate Reports

After completing the password change operations, verify the results and generate comprehensive reports for documentation and compliance purposes.

# Verify password change requirements for all processed users
$VerificationResults = @()

Write-Host "Verifying password change requirements..." -ForegroundColor Cyan

$UsersToUpdate | ForEach-Object {
    try {
        $UserDetails = Get-MgUser -UserId $_.UserPrincipalName -Property "UserPrincipalName,DisplayName,PasswordProfile,LastPasswordChangeDateTime"
        
        $VerificationResults += [PSCustomObject]@{
            UserPrincipalName = $UserDetails.UserPrincipalName
            DisplayName = $UserDetails.DisplayName
            MustChangePassword = $UserDetails.PasswordProfile.ForceChangePasswordNextSignIn
            LastPasswordChange = $UserDetails.LastPasswordChangeDateTime
            VerificationTime = Get-Date
            Status = if ($UserDetails.PasswordProfile.ForceChangePasswordNextSignIn) { "Verified" } else { "Not Set" }
        }
    }
    catch {
        $VerificationResults += [PSCustomObject]@{
            UserPrincipalName = $_.UserPrincipalName
            DisplayName = "Unknown"
            MustChangePassword = "Error"
            LastPasswordChange = "Unknown"
            VerificationTime = Get-Date
            Status = "Verification Failed: $($_.Exception.Message)"
        }
    }
}

# Generate summary statistics
$TotalVerified = $VerificationResults.Count
$RequireChange = ($VerificationResults | Where-Object {$_.MustChangePassword -eq $true}).Count
$NotSet = ($VerificationResults | Where-Object {$_.MustChangePassword -eq $false}).Count
$Errors = ($VerificationResults | Where-Object {$_.Status -like "*Failed*"}).Count

Write-Host "`nVerification Summary:" -ForegroundColor Cyan
Write-Host "  Total Users Checked: $TotalVerified" -ForegroundColor White
Write-Host "  Require Password Change: $RequireChange" -ForegroundColor Green
Write-Host "  Not Set: $NotSet" -ForegroundColor Yellow
Write-Host "  Verification Errors: $Errors" -ForegroundColor Red

Generate comprehensive report:

# Export detailed verification report
$ReportPath = "C:\Temp\PasswordChange-VerificationReport-$(Get-Date -Format 'yyyyMMdd-HHmmss').csv"
$VerificationResults | Export-Csv -Path $ReportPath -NoTypeInformation

Write-Host "`nDetailed report exported to: $ReportPath" -ForegroundColor Cyan

# Display users who still don't require password change (potential issues)
$ProblematicUsers = $VerificationResults | Where-Object {$_.MustChangePassword -eq $false}
if ($ProblematicUsers.Count -gt 0) {
    Write-Host "`nUsers not requiring password change (review needed):" -ForegroundColor Yellow
    $ProblematicUsers | Select-Object UserPrincipalName, Status | Format-Table -AutoSize
}

Final verification command:

# Quick verification of random sample
Get-MgUser -All -Property "UserPrincipalName,PasswordProfile" | 
Where-Object {$_.PasswordProfile.ForceChangePasswordNextSignIn -eq $true} | 
Select-Object UserPrincipalName, @{Name="RequiresPasswordChange";Expression={$_.PasswordProfile.ForceChangePasswordNextSignIn}} | 
Select-Object -First 10

Frequently Asked Questions

Can I force password changes for all Microsoft 365 users without affecting administrative accounts?+
Yes, you can exclude administrative accounts by creating an exclusion list before running bulk operations. Define an array of admin account email addresses and filter them out using PowerShell's Where-Object cmdlet. This prevents accidentally locking out critical service accounts and maintains system access during password change operations.
What's the difference between Microsoft Graph PowerShell and the MSOnline module for password management?+
Microsoft Graph PowerShell SDK is the modern, recommended approach that uses Update-MgUser cmdlets, while MSOnline module uses deprecated Set-MsolUser cmdlets. Graph PowerShell provides better error handling, more granular permissions, and is actively maintained by Microsoft. The MSOnline module is being phased out and should not be used for new implementations.
How can I verify that password change requirements were successfully applied to users?+
Use the Get-MgUser cmdlet with PasswordProfile property to verify the ForceChangePasswordNextSignIn setting. You can check individual users or create verification scripts that process all updated users and generate reports showing which accounts require password changes and which operations failed.
Is it possible to assign temporary passwords while forcing password changes in Microsoft 365?+
Yes, you can use the Update-MgUser cmdlet with both Password and ForceChangePasswordNextSignIn parameters in the PasswordProfile object. Create a secure password generation function and assign temporary passwords while setting the force change flag. Always export results to CSV for secure distribution and delete the file after use.
What permissions are required to force password changes for Microsoft 365 users using PowerShell?+
You need the User.ReadWrite.All scope in Microsoft Graph PowerShell, which requires Global Administrator or User Administrator roles in Microsoft 365. Connect using Connect-MgGraph with the appropriate scopes, and ensure your account has sufficient privileges to modify user password policies across the organization.
Emanuel DE ALMEIDA
Written by

Emanuel DE ALMEIDA

Microsoft MCSA-certified Cloud Architect | Fortinet-focused. I modernize cloud, hybrid & on-prem infrastructure for reliability, security, performance and cost control - sharing field-tested ops & troubleshooting.

Discussion

Share your thoughts and insights

You must be logged in to comment.

Loading comments...