Automated domain password expiration notifications through email

In an environment where every computer that you have is a PC and every computer is attached to your domain, password expiration is handled for you. If a users password is about to expire they are notified every time they log on and are forced to change it after it has expired.

Today, a network full of PC’s isn’t always feasible and, in some cases, adding the machine to the domain isn’t acceptable. I have worked in environments where every user had a laptop which could not be added to the domain. I have also worked in an environment where there were just as many Macs as PCs. I had to answer a question that I feel many admins will have to face in the next few years:

How do you manage password expiration for users that cannot be added to the domain?

There are a few restrictions that I placed on myself for this:

1) The solution should have a minimal impact on security.

2) The solution should preserve the use of SSL IMAP and SSL STMP for the users that require it.

3) The solution should require minimal maintenance.

4) The solution should be automated.

My answer to this was an automated password expiration email reminder and enabling password changing through OWA.

Scouring the web, I found a few pay for solutions, but I truly felt like this should be a feature that was included within windows.

Then I came across this:

http://bassplayerdoc.blogspot.com/2007/11/identify-password-expiration-in-active.html

It seemed perfect, if it worked.

After a bit of research I got the script to work and added a few things to it. The download link is below and the configuration options are as follows:


'====================================
'Script Configuration Options
Const EMAIL_SERVER = "exchange.domain.local"
Const EMAIL_FROM = "administrator@domain.com"
Const OWA_STRING = "at https://exchange.domain.com"

Const FIRST_REMINDER_DAY = 10
Const START_REMINDER_DAYS = 7

'Where log files will be stored
'Remember to end with 
Const LOG_PATH = "C:EMAIL_REMINDER"
'This setting allows you to append the date to the log file so that you get an Archive
Const APPEND_DATE = 0

'SET DEBUG MODE to 1 to send all emails to debug_email
DEBUG_MODE = 1
DEBUG_EMAIL = "jayt@domain.com"
'====================================

EMAIL_SERVER sets the SMTP server that the email should be sent through
EMAIL_FROM sets the from address
OWA_STRING sets the owa address
FIRST_REMINDER_DAY sets the first day that the reminder should go out
START_REMINDER_DAYS sets the first day that the user should receive continuous reminders until the password has expired
In this case, the user would receive a reminder 10 days before the password expired and then on the 7th, 6th, 5th… until the password did expire.
LOG_PATH sets where the logs will be stored
APPEND_DATE allows you to append the run date to the end of the log so that you can have an archive
DEBUG_MODE sends all of the emails to the DEBUG_EMAIL if it is not set to 0
DEBUG_EMAIL is where you would get the password expiration emails if DEBUG_MODE is set

Set this vb script to run as a scheduled task under a domain admin account every day and your users will now get password expiration emails.

DOWNLOAD: Email_Reminder

46 thoughts on “Automated domain password expiration notifications through email”

    1. Its been a while so let me know if this doesn’t work:

      Replace:
      strDNSDomain = objRootDSE.Get(“DefaultNamingContext”)

      With

      strDNSDomain = ldap path to OU

      where ldap path to OU is something like “OU=Test,OU=Users & Groups,OU=DEV2,OU=CA-Test,DC=ca,DC=test,DC=com”

      Replace:
      strDomain = Replace(strDNSDomain, “,DC=”,”.”)
      strDomain = Replace(strDomain, “DC=”,””)

      With

      strDomain = “domain”

      where domain is your domain like test.com

      and you may have to replace

      Set oDomain = GetObject(“LDAP://” & strDNSDomain)
      Set maxPwdAge = oDomain.Get(“maxPwdAge”)
      numDays = ((maxPwdAge.HighPart * 2 ^ 32) + maxPwdAge.LowPart) / -864000000000

      with

      numDays = number

      where number is the number of days until the password expires.

      You can use ADExplorer from Sysinternals ( http://technet.microsoft.com/en-us/sysinternals/bb963907.aspx ) to find the path of the OU you would like to scan.

  1. Hello, I tried your modified code so it would pick out a specific OU to look at and I seem to have run into an issue.
    So I followed your instructions and came up with this:
    strDNSDomain = “OU=office,DC=BLANK,DC=org”
    strDomain = “BLANK.org”
    numDays = 3
    I did end up needing to put just “numDays = 3” to replace the other 3 lines because otherwise the script would return the error: Script compilation error: Invalid character (121, 25)
    Now, I do get emails but I seem to be getting an email for every single user in the OU! For example, one email said the the password had expired last Saturday, July 25, 2009. But the password really isn’t expired.
    Can you help me out? If you need anymore information let me know. Thanks!
    (Also, the original unmodified script works great! But I need to target an OU.)

    1. You need to set numDays to the number of days before the password is set to expire for that OU. (ie. if your gpo has set the password to expire after 30 days numDays needs to be set to 30 not 3)

  2. Thanks for updating the script. I just realized that code snippets posted on my blog got truncated by the blog engine, thinking that it is a malicious code. Now I need to figure out how to post the codes as they are 😉

    1. I had the same issue when I first tried to post it. I ended up using syntax highlighter to preserve the code, but it was just too long to post directly on the site. I added a couple of things:

      1) it automatically figures out the domain you are on
      2) it automatically figures out the expiration time for the emails.
      3) optional logging
      4) …

      Your script taught me a lot about vb and AD. I really should go back and clean up the code and make it more readable. If you cant tell it was my first real attempt at VB and AD 🙂

  3. Thanks so much for posting this. The majority of our users use OWA and don’t pay attention to the pw expiration bar. Hopefully this script will reduce the amount of calls asking for pw resets.

    The script works perfectly. Though for some reason accounts with expired passwords receive the email. I looked through the script and thought I saw code that should prevent that.

    Any ideas?

    Thanks again!
    Ray

    1. The following section sends out emails to the accounts that have already expired:

      ElseIf DateDiff("d", Now, whenPasswordExpires) <0 Then
      strEmailMessage="0" 'password has already expired
      objFile.WriteLine adoRecordset.Fields("displayName").Value & "," & blnPwdExpire & " , " & blnAccountDisabled & " , " & dtmPwdLastSet & " , " & adoRecordset.Fields("mail").Value & "," & whenPasswordExpires & "," & DateDiff("d", Now, whenPasswordExpires)
      Call sendEmail(adoRecordset.Fields("mail").Value,FormatDateTime(whenPasswordExpires,2),strEmailMessage, adoRecordset.Fields("displayName").Value, strDomain)
      

      Commenting out the whole section should stop emails going to those whose passwords have already expired.

      Thanks
      Jay

  4. The script seems to work, currently I have DEBUG_EMAIL enabled, however, I only receive emails from the person whose account expires today. Should I be receving the ones for the ones that expire in less than 10 days?

  5. This looks perfect for what I need but I have a question and mostly it’s based out of ignorance. I am not a programmer so bear with me, some the text seems to imply needing CDO. Is that correct and do I need it on the system running the script only? Are there other back end requirements?

    I like the script, to me a non-programmer, it’s a logical lay out.
    Thanks!

    1. I believe if you change

      strFilter = "(&(objectCategory=person)(objectClass=user))"
      

      to

      strFilter = "(&(objectCategory=Person)(objectClass=User)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
      

      the script should then ignore disabled accounts completely.

  6. Thanks for the good and useful script, I just wanted to mention that I had to change:
    “If APPEND_DATE=true Then” to “If APPEND_DATE=1 Then” to get log file to append the date correctly.

  7. You may want to edit this script with the following so you can use NTLM authentication to send mail instead of requiring to enable anonymous on your Exchange Receive Connector:

    Const cdoSendUsingPickup = 1 'Send message using the local SMTP service pickup directory. 
    Const cdoSendUsingPort = 2 'Send the message using the network (SMTP over the network). 
    
    Const cdoAnonymous = 0 'Do not authenticate
    Const cdoBasic = 1 'basic (clear-text) authentication
    Const cdoNTLM = 2 'NTLM
    
    Set objMessage = CreateObject("CDO.Message") 
    objMessage.Subject = "Example CDO Message" 
    objMessage.From = """Test"" <test@mydomain.com>" 
    objMessage.To = "test@mydomain.com" 
    objMessage.TextBody = "This is some sample message text.." & vbCRLF & "It was sent using SMTP authentication."
    
    '==This section provides the configuration information for the remote SMTP server.
    
    objMessage.Configuration.Fields.Item _
    ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2 
    
    'Name or IP of Remote SMTP Server
    objMessage.Configuration.Fields.Item _
    ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "mail.your.com"
    
    'Type of authentication, NONE, Basic (Base64 encoded), NTLM
    objMessage.Configuration.Fields.Item _
    ("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = cdoNTLM
    
    'Your UserID on the SMTP server
    objMessage.Configuration.Fields.Item _
    ("http://schemas.microsoft.com/cdo/configuration/sendusername") = "youruserid"
    
    'Your password on the SMTP server
    objMessage.Configuration.Fields.Item _
    ("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "yourpassword"
    
    'Server port (typically 25)
    objMessage.Configuration.Fields.Item _
    ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25 
    
    'Use SSL for the connection (False or True)
    objMessage.Configuration.Fields.Item _
    ("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = False
    
    'Connection Timeout in seconds (the maximum time CDO will try to establish a connection to the SMTP server)
    objMessage.Configuration.Fields.Item _
    ("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = 60
    
    objMessage.Configuration.Fields.Update
    
    '==End remote SMTP server configuration section==
    
    objMessage.Send
    
    

    Obtained from: http://www.paulsadowski.com/wsh/cdo.htm

    Using NTLM you can ignore the userid and password parts btw.

  8. Much appreciated. The ‘enhanced’ version you came up with works great (BassPlayerdoc’s wouldn’t even pass the runtime/compilation stage), and is much more readable and easier to maintain. I tried several other scripts, and freeware (commercial) version from NetWrix, but this worked best.

  9. I want to run this against a security group instead of an OU. The script work perfectly for me now just wondering if I can do Security group vs OU. I have tried to modify myspelf but continuoulsy get compilation errors.

  10. I got an error while trying to run it on a Windows 2008.

    Line: 132
    Char : 1
    Error : Object required :’Fields(…). Value’
    Code : 800A01A8
    Source : Microsft VBScript runtim error

    Line 132 : Set objDate = adoRecordset.Fields(“pwdLastSet”).Value

    Kindly advise me, i tried running on my Windows 7(My laptop) there’s not issue.

    Regards
    Kyoichi

  11. I know this is an older post, but I really like this script and want to use in for my Domain. My problem is probably simple to fix but, not being a programmer, I’m not quite sure how to go about it. I am testing the script and it seems to work fine, but I think I am receiving all the email notifications for the users. I’m thinking it has something to do with our email convention using FiLName@[domain] maybe? Not really sure but checking the emails I am receiving against the log file created that is what appears to be happening. How might I correct this please?

  12. Oh good heavens! Please disregard my previous post….I’m a nitwit! haha. Apparently I need to go back to school and learn to read!! Turn off the debug….doh!

  13. My brother suggested I may like this blog. He was once entirely right. This post truly made my day. You can not imagine simply how so much time I had spent for this info! Thank you!

  14. I get file :itpasswordusers_DOMAIN.txt cannot be opend

    Is there something that is missing or am i missing something?

    Please help.

    I also am not receiving any debug emails to myself to notify me as a test. Any ideas I can check?

    1. Hi Henry,

      it looks like your path is wrong. It looks like you are missing the drive letter in your path, and you need to ensure that the folders exist.

      Thanks
      Jay

  15. Hi I have tried your script and I am getting the error of:

    Active Directory: the directory property cannot be found in the cache

    Any help would be greatly appreciated

  16. Hi

    i have got the script running but the users didnt get an email alerting them to the password expiry, but as i get the debug emails i got it. is there something that i need to tweek to get the users to get the reminder.

    thanks

    ally

  17. Hi I checked, the path does exist and is correctly entered into the script. Here is the script part.

    Script Configuration Options
    Const EMAIL_SERVER = “x.x.x.x”
    Const EMAIL_FROM = “password.notify@x.x.x”
    Const OWA_STRING = “at https://password.x.com/iisadmpwd/aexp2b.asp

    Const FIRST_REMINDER_DAY = 10
    Const START_REMINDER_DAYS = 7

    ‘Where log files will be stored
    ‘Remember to end with
    Const LOG_PATH = “C:EMAIL_REMINDER”
    ‘This setting allows you to append the date to the log file so that you get an Archive
    Const APPEND_DATE = 0

    ‘SET DEBUG MODE to 1 to send all emails to debug_email
    DEBUG_MODE = 1
    DEBUG_EMAIL = “x@x.x.x”

    Plus I don’t get an email when in debug mode?

  18. Hi,
    Firstly, I would say to thank you for this script.it work nice but Im having some trouble.it run on server with regional settings EN but our users using Turkish names.so ş,ç,ğ,ü words can not showing on reminder email.I did some trick but dont.can you help me for this?

    thanks

  19. Dear All,

    Please help me, when i will run the script it will show me the error as follow:

    Script: C:notifications.vbs
    Line: 265
    Char: 1
    Error: The transport failed to connect to the server
    Code: 80040213
    Source: CDO.Message.1

    Any idea please…

  20. Love this script, it is one of the most useful scripts I’ve found for this need. I have worked through a couple of situations, but I’m not a scripter per se, though I’m kind of learning on the fly. I am needing to exclude some types of accounts, is there a way of doing that please? I’m thinking probably the easiest way would be to use a text file to hold the accounts I want to exclude so they can be managed and updated as needed, but I’ve really no idea how to include these types of exclusions in the script itself. Would you be so kind as to help me with that? Thanks!

  21. Hi,

    This is a very useful script that I began to use in my company.
    I just have a problem with already expired Passwords.
    For some users, the date of parword expiration returns me 01/04/1601 instead of a date in 2011 or 2012.
    Have you got any Idea ?
    Thanks a lot.

  22. facing problem with ntlm authentication. my exchange uses ntlm and this script is not permitted to send mail. pls help

  23. Hi,

    Script run perfectly when debug mode is 1. But when I set debug mode to 0, then it doesn’t work. It sends email to 2 users adn script stops with error
    cdo.message.1 at least one recipient is required but none were found

    Please help

    Regards,
    Amit

  24. Hi Christian; Eduardo Menegalli Nazato and all,

    Could you pls show me how:
    1. My system use Windows Server 2008 and Exchange Server 2007, I can use Security policy on Active Directory for seting alert when password expire, but this rule can not apply to user who use MS outlook mail client or OWA for checking email.
    My question is: How to automatically sending email alert to users who are work out side domain?

    2. Could you pls show me step by step how to setup above script for automatically running on ?

    Thank you.

  25. When I test the script by: cscript email-exp-notification.vbs, I am getting the following error on Microsoft Small Business Server 2011 (SBS):

    C:Email_Reminderemail-exp-notification.vbs(136, 1) Microsoft VBScript runtime error: Invalid procedure call or argument
    : ‘DateAdd’

    Do have a suggestion how to fix and thank you for your help,
    Matt

    P.S. On Windows 2008 R2 standard domain controller, it works great.

  26. Your work on this is awesome!

    Are any changes needed to accommodate secondary password policies in AD 2008 R2? I ask because the secondary password policy isn’t a GPO like the primary and I’ve been getting some password expiration dates that don’t seem correct per the secondary policy.

    Thanks for any help.

Leave a Reply

Your email address will not be published. Required fields are marked *