4

When a user changes his password, it's typically sometime during the day. This means that a password expiration date set to last change + n days will result in the password expiring during the day. How can I force the passwords to expire at midnight of that day instead?

Terry Gardner
  • 632
  • 5
  • 9
anonymous
  • 41
  • 1
  • 1
  • 2
  • 9
    If they've ignored the **Your password will expire in n days** message for 2 weeks already so their password actually does expire in the middle of the day, what makes you think expiring the password at midnight will help :-) Seriously though, I don't think what you want will help, and personally I have no sympathy - if someone ignores a message *every time they log on* that warns them of impending doom for a whole 2 weeks, then it's their own fault really. – Ben Pilbrow Aug 12 '11 at 17:40
  • 2
    @Ben Agreed, but with Windows 7, the notification is a balloon pop-up near the system tray, instead of a modal dialog during logon. It is even *easier* to ignore now. *sigh*. – jscott Aug 12 '11 at 17:44

3 Answers3

5

I don't believe that is possible without manually changing the PwdLastSetattribute within ADSI Edit, which I wouldn't recommend doing.

The value is stored in 100-nanosecond intervals since 12:00 am January 1, 1601. However, your only options to edit the attribute are to set it to 0 (password is now expired and user must reset), or -1 (value for PwdLastSet is changed to the current date/time).

As mentioned in comments, you would need to set the value to 0 first, then set it to -1.

You could potentially write a script to update the attribute to -1 at midnight on a given day for all users. However, this would set all your user's passwords to expire @ midnight in N days (N being your domain password policy max age setting). This could potentially extend the max age of a password.

What is your goal in setting the password to expire at midnight?

HostBits
  • 11,776
  • 1
  • 24
  • 39
  • In my testing, with VBS and PowerShell, I need to first do a `setInfo()` to change `pwdLastSet` to `0` and then a second `setInfo()` to change it to `-1`. – jscott Aug 12 '11 at 17:46
  • It is not possible to manually edit the timestamp in pwdLastSet. The only thing you can mainpulate with it is a forced expiration of the password. – Brian Desmond Aug 12 '11 at 19:38
  • You can change it to 0 or -1 with ADSI Edit or by scripting as my answer states. I'll update my answer to be a little more clear. – HostBits Aug 12 '11 at 19:59
  • The main goal is to be consistent with other systems that get the expiry info from LDAP, but don't have sub-day granularity. The secondary goal is for users to change their passwords at the start of the day instead of potentially getting interrupted in the middle of something. – anonymous Aug 12 '11 at 20:30
2

Windows simply doesn't support the concept of a "password expiry time" that applies globally. You also cannot set the time, except to say it is expired now, or that it was just changed. However, what you could do is write a script using command-line AD tools or powershell that runs nightly: it can query AD for users with passwords due to expire in less than 24h (pwdLastSet is older than one day less than your password max age days), and set it to -1 (the password is expired). This would avoid extending password life unintentionally, and also avoid midday password expiration.

There are also third-party tools that can do this kind of thing for you. For instance, one feature of Hitachi ID Password Manager allows you to pop up a web browser in which the user must change their password or else be logged out, and you can set this to happen an arbitrary number of days in advance of the actual expiration.

Falcon Momot
  • 24,975
  • 13
  • 61
  • 92
0

You can run a script each day at midnight and if the password is set to expire during the next day, you force the expiration.

A bit illustrated there

Never tested it in VBS;

Const SEC_IN_DAY = 86400 
Const ADS_UF_DONT_EXPIRE_PASSWD = &h10000 
Const ADS_SCOPE_SUBTREE = 1000

dim strname
dim strdist
dim dtmvalue

on error resume next


        Set objConnection = CreateObject("ADODB.Connection")
        Set objCommand =   CreateObject("ADODB.Command")
        objConnection.Provider = "ADsDSOObject"
        objConnection.Open "Active Directory Provider"
        Set objCommand.ActiveConnection = objConnection
        objCommand.Properties("Page Size") = 1000
        objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE 
        objCommand.CommandText = "SELECT distinguishedName, profilepath, name from 'LDAP://dc=Example,dc=com' where objectCategory = 'User'"        


     Set objuserRecordSet = objCommand.Execute

objUSerRecordSet.MoveFirst

Do Until objuserRecordSet.EOF  

    strdist = objuserRecordSet.Fields("distinguishedName").Value
    strname = objuserRecordSet.Fields("name").Value

    Set objUserLDAP = GetObject _ 
    ("LDAP://" & strdist) 

    intCurrentValue = objUserLDAP.Get("userAccountControl") 

        dtmValue = objUserLDAP.PasswordLastChanged  

        If intCurrentValue and ADS_UF_DONT_EXPIRE_PASSWD Then 
            x  =  "The password does not expire." 
        Else 
                Set objDomainNT = GetObject("WinNT://escc.gov.uk") 
                intMaxPwdAge = objDomainNT.Get("MaxPasswordAge") 
                    If intMaxPwdAge < 0 Then 
                        x  = "Password does not expire" 
                    Else
                        intMaxPwdAge=intMaxPwdAge/86400
                        strold = ((dtmValue + intMaxPwdAge)-now)

                        if strold < 2 and strold > 0 then
                            objUserLDAP.pwdLastSet = 0
                            objUserLDAP.SetInfo
                        end if
                    end if

        End If 

    dtmValue= ""

    objuserrecordset.movenext   

Loop

Or there for a powershell's example:

# This PowerShell Script will query Active Directory and return the user accounts with passwords 
# set to expire before the end of the next day, export a list of the affected accounts, and require
# a password change at the next logon.  The script is configured to ingore accounts which have been
# configured with passwords that never expire, and to ignore accounts who do not have permission to
# change their own password.  Any other account would be affected, so be warned before running this
# script, as you could experience unintended consequences.  Either modify the script to reduce the
# scope of user accounts, or ensure that accounts that shouldn't be affected are either flaged with
# a non-expiring password or are flagged with "cannot change password.  When ready to run/schedule 
# in production, remove the -WhatIf from the last line.
#
# - MWT, 10/11/13

# The 89 is based upon your environment. If passwords expire every X (90) days, and you run the script
# in the early morning, you can set it to -1*(X-1) (-89), if you run the script late at night, set it to
# -1*(X-2) (-88).

Import-Module ActiveDirectory # Required for PowerShell 2.0 only

$a = (Get-Date).Date.AddDays(-89)

# The following line will build the variable based upon the noted criteria
$b = Get-ADUser -Property Name,SamAccountName,PasswordLastSet,CannotChangePassword,PasswordNeverExpires -Filter {(PasswordLastSet -lt $a) -and (PasswordNeverExpires -eq $false)} | Where-Object {$_.CannotChangePassword -eq $false}

# The following line will display/export the data logging the accounts to be changed; please note the
# Out-File path and change to suit your needs.
$b | Format-Table Name,PasswordLastSet,CannotChangePassword,PasswordNeverExpires -AutoSize | Out-File -FilePath C:\passwordchanges.txt

# The following line will actually flag the accounts to require a password change (after -WhatIf is removed)
$b.SamAccountName | ForEach-Object {Set-ADUser -Identity $_ -ChangePasswordAtLogon $true -WhatIf}
yagmoth555
  • 16,300
  • 4
  • 26
  • 48