Set Office 365 Password Expiration Policy for all delegated customer tenants

In Office 365, the default password expiration policy is 90 days. While this is a good security measure in theory, in practice it can cause downtime and user frustration, especially if an entire organisation’s users have their passwords expire on the same day.

While it’s still a good idea for users to change their passwords occasionally, you might decide to find another way to enforce it.

If you’re managing a single Office 365 organisation, you can set the password expiration policy via the Office 365 Admin portal, or via PowerShell.

How to set passwords to never expire for a single Office 365 tenant via the Office 365 Admin Portal

  1. Log into the Office 365 Admin Center at https://portal.office.com/AdminPortal
  2. Click Settings on the left menu, then Security and Privacy
  3. Click Edit next to Password Policy, and change Set user passwords to never expire to On

How to set passwords to never expire for a single Office 365 tenant via Powershell

  1. Ensure you can connect to the Microsoft Online Service/Azure Active Directory via PowerShell. Follow our quick guide here for instructions.
  2. Run the following command
    Connect-MsolService
  3. Copy and Paste the following script:
    $domains = Get-MsolDomain | Where-Object {$_.Status -eq "Verified"}
    
    foreach($domain in $domains){
    	$domainStatus = Get-MsolPasswordPolicy -DomainName $domain.Name
    	if($domainStatus.ValidityPeriod -ne 2147483647){
    	Write-Host "Setting the Password Expiration Policy on $($domain.Name)"
    	Set-MsolPasswordPolicy -DomainName $domain.Name -ValidityPeriod 2147483647 -NotificationDays 30
    }
    

If you’re a Microsoft Partner managing a lot of Office 365 organisations, it can be a time consuming process to enable this setting on all of your customers domains.

How to set passwords to never expire for all Office 365 customers via PowerShell and delegated administration

The script below will set the password policy to ‘Never expire’ for all of your customers domains. Setting Password Expiration Policy On Customer Domains

If the domain is already configured to never expire, the script will report this also.

Customer Domain Office 365 Password Expiration Policy Is Already Set

You may have noticed in the script above that the -ValidityPeriod parameter is set to 2147483647 days. This date seems to be the magic number of days that ensures that the tenant passwords are set to Never Expire.

When running this script you’ll need to replace ‘[email protected]’ with the username of a user that has Delegated Administration permissions for your Office 365 customer tenants. You’ll also need to ensure that the CSV output paths specified at the top of the script exist and are writable. Usually you can just create a folder called ‘temp’ under the C:\ directory and it works fine.

cls

$UserName = "[email protected]"

$MsolPasswordPolicyStatus = "C:\temp\MsolPasswordPolicyStatus.csv"

$ChangedMsolPasswordPolicyStatus = "C:\temp\ChangedMsolPasswordPolicyStatus.csv"

$Cred = get-credential -Credential $UserName

$MsolPasswordPolicyStatusExport = @()

$ChangedMsolPasswordPolicyStatusExport = @()

Connect-MsolService -Credential $cred

$Customers = Get-MsolPartnerContract -All
$PartnerInfo = Get-MsolCompanyInformation

Write-Host "Found $($Customers.Count) customers for $($PartnerInfo.DisplayName)"


foreach ($Customer in $Customers) { 

	Write-Host "-----------------------------------------------"
	Write-Host " "
	Write-Host "Checking the Password Expiration Policy on each domain for $($Customer.Name)"
	Write-Host " "

	$domains = Get-MsolDomain -TenantId $Customer.TenantId | Where-Object {$_.Status -eq "Verified"}

	foreach($domain in $domains){

		$domainStatus = Get-MsolPasswordPolicy -TenantId $Customer.TenantId -DomainName $domain.Name

		if($domainStatus.ValidityPeriod -eq 2147483647){

			Write-Host " "
			Write-Host "Password Expiration Policy is set for $($domain.name) already"
			Write-Host " "

			$PasswordsWillExpire = $false

			$MsolPasswordPolicyInfo = @{

				TenantId = $Customer.TenantId
				CompanyName = $Customer.Name
				DomainName = $domain.Name
				ValidityPeriod = $domainStatus.ValidityPeriod
				NotificationDays = $domainStatus.NotificationDays
				PasswordsWillExpire = $PasswordsWillExpire
			}

	$MsolPasswordPolicyStatusExport = @()

	$MsolPasswordPolicyStatusExport += New-Object psobject -Property $MsolPasswordPolicyInfo

	$MsolPasswordPolicyStatusExport | Select-Object TenantId,CompanyName,DomainName,ValidityPeriod,NotificationDays,PasswordsWillExpire | Export-Csv -notypeinformation -Path $MsolPasswordPolicyStatus -Append

	}



	if($domainStatus.ValidityPeriod -ne 2147483647){

		Write-Host "Setting the Password Expiration Policy on $($domain.Name) for $($Customer.Name):"
		Write-Host " "

		Set-MsolPasswordPolicy -TenantId $Customer.TenantId -DomainName $domain.Name -ValidityPeriod 2147483647 -NotificationDays 30

		$PasswordPolicyResult = Get-MsolPasswordPolicy -TenantId $Customer.TenantId -DomainName $domain.Name

		if($PasswordPolicyResult.ValidityPeriod -eq 2147483647){
			
			$PasswordsWillExpire = $false
			Write-Host "Password policy change confirmed working"
		}

		if($PasswordPolicyResult.ValidityPeriod -ne 2147483647){
			
			$PasswordsWillExpire = $true
			Write-Host "Password policy change not confirmed yet, you may need to run this again."
		}

		$MsolPasswordPolicyInfo = @{

 			TenantId = $Customer.TenantId
			CompanyName = $Customer.Name
			DomainName = $domain.Name
			ValidityPeriod = $PasswordPolicyResult.ValidityPeriod
			NotificationDays = $PasswordPolicyResult.NotificationDays
			PasswordsWillExpire = $PasswordsWillExpire
		
		}

		$MsolPasswordPolicyChangedExport = @()

		$MsolPasswordPolicyChangedExport += New-Object psobject -Property $MsolPasswordPolicyInfo

		$MsolPasswordPolicyChangedExport | Select-Object TenantId,CompanyName,DomainName,ValidityPeriod,NotificationDays,PasswordsWillExpire | Export-Csv -notypeinformation -Path $ChangedMsolPasswordPolicyStatus -Append

		Write-Host " "
		}
	}
}

This script generates the following output:

The ChangedMsolPasswordPolicyStatus.csv file

This CSV lists the domains for the Office 365 customers whose password policy was modified.

List The Changed Office 365 Password Policy Status

The MsolPasswordPolicyStatus.csv file

This CSV lists the Password Policy status for all of your customers’ domains.

List The Current Office 365 Password Policy Status For All Customers

Set Office 365 Password Expiration policy on a per user basis for all customers

Before settling on this method, I wrote another script that gets the current password expiration status for each user in each of your customers’ Office 365 organisations. If the user’s password is not set to never expire, it will update the PasswordNeverExpires parameter and add it to a CSV. It also creates a another CSV report of the current PasswordNeverExpires status and the LastPasswordChangeTimestamp

I can’t think of many reasons why you’d use this script instead of the one above for password policy management. Unless you wanted to get a list of all users with their password expiration status, and the date their password was last changed.

cls

$UserName = "[email protected]"

$MsolUserPasswordStatus = "C:\temp\MsolUserPasswordStatus.csv"

$ChangedMsolUserPasswordStatus = "C:\temp\ChangedMsolUserPasswordStatus.csv"

$Cred = get-credential -Credential $UserName

$MsolUsersStatusExport = @()

$MsolUsersChangedExport = @()

Connect-MsolService -Credential $cred

$Customers = Get-MsolPartnerContract -All
$PartnerInfo = Get-MsolCompanyInformation

Write-Host "Found $($Customers.Count) customers for $($PartnerInfo.DisplayName)"

foreach ($Customer in $Customers) { 

	Write-Host "Setting the Password Expiration Policy on $($Customer.Name)"
	Write-Host " "
	$Users = Get-MsolUser -TenantId $Customer.tenantid

	foreach($User in $Users){

		$MsolUserInfo = @{

			TenantId = $Customer.TenantId
			CompanyName = $Customer.Name
			DefaultDomainName = $Customer.DefaultDomainName
			UserPrincipalName = $User.UserPrincipalName
			DisplayName = $User.DisplayName
			PasswordNeverExpires = $User.PasswordNeverExpires
			LastPasswordChangeTimestamp = $User.LastPasswordChangeTimestamp
		}



		$MsolUsersStatusExport += New-Object psobject -Property $MsolUserInfo

		if(!$User.PasswordNeverExpires){

			Set-MsolUser -TenantId $Customer.TenantID -UserPrincipalName $User.UserPrincipalName -PasswordNeverExpires $true

			$MsolUserInfo = @{

				TenantId = $Customer.TenantId
				CompanyName = $Customer.Name
				DefaultDomainName = $Customer.DefaultDomainName
				UserPrincipalName = $User.UserPrincipalName
				DisplayName = $User.DisplayName
				PasswordNeverExpires = "TRUE"
				LastPasswordChangeTimestamp = $User.LastPasswordChangeTimestamp
			}

			$MsolUsersChangedExport = @()

			$MsolUsersChangedExport += New-Object psobject -Property $MsolUserInfo

			$MsolUsersChangedExport | Select-Object TenantId,CompanyName,DefaultDomainName,UserPrincipalName,DisplayName,PasswordNeverExpires,LastPasswordChangeTimeStamp | Export-Csv -notypeinformation -Path $ChangedMsolUserPasswordStatus -Append

			Write-Host "Changing Password Policy on $($User.DisplayName) in $($Customer.Name)"

			Write-Host " "

		}

	}

}

$MsolUsersStatusExport | Select-Object TenantId,CompanyName,DefaultDomainName,UserPrincipalName,DisplayName,PasswordNeverExpires,LastPasswordChangeTimeStamp | Export-Csv -notypeinformation -Path $MsolUserPasswordStatus

This script generates the following output:

The ChangedMsolUserPasswordStatus.csv file

This CSV lists the Office 365 users whose PasswordNeverExpires value was modified, it also lists the timestamp of when they last changed their password.

List The Changed Office 365 User Password Status

The MsolUserPasswordStatus.csv file

This CSV lists all Office 365 users, the PasswordNeverExpires value at the time the report was run, and the timestamp of when they last changed their password.

List The Current Msol User Password Status

Was this article helpful?

Related Articles