locked
Stagger/Schedule password expirations? RRS feed

  • Question

  • Hello all - We recently had to change every users active directory password in our company, over 5000 people all on the same day.  We have a 90 day password policy expiration and in the past, not everyone had to change at the same time.  It was staggered throughout the year. Unfortunately, what will happen in 90 days, the nightmare will repeat itself with all of the support calls because of course not every single one of them will of read the multiple emails warning them about this. 

    Is it possible to schedule a block of users (lets say 500 people) password to expire 45 days out.  Then another block of 500 60 days out and so on?  Trying to avoid the moment 90 days from now where we have a line of users out the door and down the block for our support team.  

    Wednesday, April 11, 2018 9:20 PM

Answers

  • If the passwords are already changed, the only option is a script to reset the pwdLastSet for blocks of users periodically. This is done by assigning the value 0 to the pwdLastSet attribute, saving the change, then assigning the value -1, and saving again. The value 0 means the password is immediately expired. The value -1 means the password expires very far in the future, but the next time the user logs on the system will assign a value corresponding to the current datetime to the pwdLastSet attribute. The only values admins can assign are 0 and -1, and you cannot assign -1 unless the current value is 0.

    Edit: The following PowerShell script will assign 0, then -1, for all users in a CSV file. The file must have a header line defining the field "ID, which will be either the sAMAccountName or the distinguishedName of the users. The script performs all operations on the same DC, to avoid synchronization problems. The specified DC should be nearby, and best to run the script during off hours, as a lot of synchronization traffic will be generated.

    # Specify the DNS name of a nearby Domain Controller, so all updates are performed on the same DC.
    $DC = "MyDC.MyDomain.com"
    
    # Read user sAMAccountNames or distinguishedNames from CSV file.
    # The header line defines this field as "ID".
    $Users = Import-Csv .\Users1.csv
    
    # Assign 0 to pwdLastSet attribute for all users in the CSV.
    # This expires the password.
    ForEach ($User In $Users)
    {
        Set-ADUser -server $DC -Identity $User.ID -Replace @{pwdLastSet=0}
    }
    
    # Assign -1 to pwdLastSet attribute for all users in the CSV.
    # The system will assign a value corresponding to the current datetime the next time the user logs on.
    ForEach ($User In $Users)
    {
        Set-ADUser -server $DC -Identity $User.ID -Replace @{pwdLastSet=-1}
    }
    
    The idea is to have several CSV files, and run the script periodically to essentially reset the pwdLastSet attribute for the users in one of the files. It should be easy to retrieve the ID for all enabled users in one file, remove any users that should not be processed, then split the file into several and add the header line to each.


    Richard Mueller - MVP Enterprise Mobility (Identity and Access)


    Wednesday, April 11, 2018 9:34 PM
  • This PowerShell script will create a CSV file with the sAMAccountName of all enabled users in the domain:

    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Export-Csv -Path .\Users.csv -NoTypeInformation

    Then break the file up into 10 separate files (about 500 names each) and add a header line to each. The header line will identify the field name as "ID" (without the quotes).

    You might want to manually remove user names that should not be affected by this, like perhaps the default Administrator user.

    Edit: Here is another idea, using the -First and -Skip parameters allowed in Select clauses. This script creates 10 csv files with 500 names each. The last file will include all users after the first 4500:

    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -First 500 | Export-Csv -Path .\Users1.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 500 | Select -First 500 | Export-Csv -Path .\Users2.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 1000 | Select -First 500 | Export-Csv -Path .\Users3.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 1500 | Select -First 500 | Export-Csv -Path .\Users4.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 2000 | Select -First 500 | Export-Csv -Path .\Users5.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 2500 | Select -First 500 | Export-Csv -Path .\Users6.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 3000 | Select -First 500 | Export-Csv -Path .\Users7.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 3500 | Select -First 500 | Export-Csv -Path .\Users8.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 4000 | Select -First 500 | Export-Csv -Path .\Users9.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 4500 | Export-Csv -Path .\Users10.csv -NoTypeInformation
    

    You would still need to add the initial header line to each CSV file, defining the ID field referred to by the first script I posted.


    Richard Mueller - MVP Enterprise Mobility (Identity and Access)


    Tuesday, April 17, 2018 11:59 PM

All replies

  • If the passwords are already changed, the only option is a script to reset the pwdLastSet for blocks of users periodically. This is done by assigning the value 0 to the pwdLastSet attribute, saving the change, then assigning the value -1, and saving again. The value 0 means the password is immediately expired. The value -1 means the password expires very far in the future, but the next time the user logs on the system will assign a value corresponding to the current datetime to the pwdLastSet attribute. The only values admins can assign are 0 and -1, and you cannot assign -1 unless the current value is 0.

    Edit: The following PowerShell script will assign 0, then -1, for all users in a CSV file. The file must have a header line defining the field "ID, which will be either the sAMAccountName or the distinguishedName of the users. The script performs all operations on the same DC, to avoid synchronization problems. The specified DC should be nearby, and best to run the script during off hours, as a lot of synchronization traffic will be generated.

    # Specify the DNS name of a nearby Domain Controller, so all updates are performed on the same DC.
    $DC = "MyDC.MyDomain.com"
    
    # Read user sAMAccountNames or distinguishedNames from CSV file.
    # The header line defines this field as "ID".
    $Users = Import-Csv .\Users1.csv
    
    # Assign 0 to pwdLastSet attribute for all users in the CSV.
    # This expires the password.
    ForEach ($User In $Users)
    {
        Set-ADUser -server $DC -Identity $User.ID -Replace @{pwdLastSet=0}
    }
    
    # Assign -1 to pwdLastSet attribute for all users in the CSV.
    # The system will assign a value corresponding to the current datetime the next time the user logs on.
    ForEach ($User In $Users)
    {
        Set-ADUser -server $DC -Identity $User.ID -Replace @{pwdLastSet=-1}
    }
    
    The idea is to have several CSV files, and run the script periodically to essentially reset the pwdLastSet attribute for the users in one of the files. It should be easy to retrieve the ID for all enabled users in one file, remove any users that should not be processed, then split the file into several and add the header line to each.


    Richard Mueller - MVP Enterprise Mobility (Identity and Access)


    Wednesday, April 11, 2018 9:34 PM
  • The suggested option with CSV seems to be a valid idea in your case. Also, maybe it's a good time to implement password self-service to reduce the inevitable load on the help desk team?

    Here's an example of a good one https://www.adaxes.com/active-directory_self-service-password-reset.htm It allows resetting own passwords and unlocking accounts either from the Windows logon screen or the Web Interface. It also works for remote users (e.g. working from home) with no VPN required.

    Thursday, April 12, 2018 11:52 AM
  • Hi,

     

    Just checking in to see if the information provided was helpful. Please let us know if you would like further assistance.

     

    Best Regards,

    William


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    Tuesday, April 17, 2018 9:53 AM
  • Hi,

     

    Just checking in to see if the information provided was helpful. Please let us know if you would like further assistance.

     

    Best Regards,

    William


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    Yes, very helpful.  Much appreciated.  I thought my original goal wasn't possible at all.  It is, with a bit of extra work.  But it's better than a Zero Day support nightmare.
    Tuesday, April 17, 2018 9:47 PM
  • If the passwords are already changed, the only option is a script to reset the pwdLastSet for blocks of users periodically. This is done by assigning the value 0 to the pwdLastSet attribute, saving the change, then assigning the value -1, and saving again. The value 0 means the password is immediately expired. The value -1 means the password expires very far in the future, but the next time the user logs on the system will assign a value corresponding to the current datetime to the pwdLastSet attribute. The only values admins can assign are 0 and -1, and you cannot assign -1 unless the current value is 0.

    Edit: The following PowerShell script will assign 0, then -1, for all users in a CSV file. The file must have a header line defining the field "ID, which will be either the sAMAccountName or the distinguishedName of the users. The script performs all operations on the same DC, to avoid synchronization problems. The specified DC should be nearby, and best to run the script during off hours, as a lot of synchronization traffic will be generated.

    # Specify the DNS name of a nearby Domain Controller, so all updates are performed on the same DC.
    $DC = "MyDC.MyDomain.com"
    
    # Read user sAMAccountNames or distinguishedNames from CSV file.
    # The header line defines this field as "ID".
    $Users = Import-Csv .\Users1.csv
    
    # Assign 0 to pwdLastSet attribute for all users in the CSV.
    # This expires the password.
    ForEach ($User In $Users)
    {
        Set-ADUser -server $DC -Identity $User.ID -Replace @{pwdLastSet=0}
    }
    
    # Assign -1 to pwdLastSet attribute for all users in the CSV.
    # The system will assign a value corresponding to the current datetime the next time the user logs on.
    ForEach ($User In $Users)
    {
        Set-ADUser -server $DC -Identity $User.ID -Replace @{pwdLastSet=-1}
    }
    The idea is to have several CSV files, and run the script periodically to essentially reset the pwdLastSet attribute for the users in one of the files. It should be easy to retrieve the ID for all enabled users in one file, remove any users that should not be processed, then split the file into several and add the header line to each.


    Richard Mueller - MVP Enterprise Mobility (Identity and Access)


    Would it be possible to scan a  random batch of users and run through, say 500 one day?  Then another 500 seven days later and so on.  Would be a PITA to have to create manual CSV files for over 5000 people.
    Tuesday, April 17, 2018 9:50 PM
  • This PowerShell script will create a CSV file with the sAMAccountName of all enabled users in the domain:

    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Export-Csv -Path .\Users.csv -NoTypeInformation

    Then break the file up into 10 separate files (about 500 names each) and add a header line to each. The header line will identify the field name as "ID" (without the quotes).

    You might want to manually remove user names that should not be affected by this, like perhaps the default Administrator user.

    Edit: Here is another idea, using the -First and -Skip parameters allowed in Select clauses. This script creates 10 csv files with 500 names each. The last file will include all users after the first 4500:

    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -First 500 | Export-Csv -Path .\Users1.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 500 | Select -First 500 | Export-Csv -Path .\Users2.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 1000 | Select -First 500 | Export-Csv -Path .\Users3.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 1500 | Select -First 500 | Export-Csv -Path .\Users4.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 2000 | Select -First 500 | Export-Csv -Path .\Users5.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 2500 | Select -First 500 | Export-Csv -Path .\Users6.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 3000 | Select -First 500 | Export-Csv -Path .\Users7.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 3500 | Select -First 500 | Export-Csv -Path .\Users8.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 4000 | Select -First 500 | Export-Csv -Path .\Users9.csv -NoTypeInformation
    Get-ADUser -Filter {Enabled -eq $True} | Select sAMAccountName | Select -Skip 4500 | Export-Csv -Path .\Users10.csv -NoTypeInformation
    

    You would still need to add the initial header line to each CSV file, defining the ID field referred to by the first script I posted.


    Richard Mueller - MVP Enterprise Mobility (Identity and Access)


    Tuesday, April 17, 2018 11:59 PM