Can't pull Max Password Age with vbscrip

Ok, here's the setup.  My company has been building a new 2012 domain environment.  So far, all the servers in the domain are 2012.

With the new domain, I have begun testing Fine-Grained Password Policies because it was one of the functions we could not do on our old domain.  And so far, everything on the domain side appear to be working just fine.

So now comes the problem.  Because our users will be using Remote-apps to connect to their servers, they aren't going to be notified that their passwords will be expiring soon.  Thus enters the VBscript.

Using Microsofts instructions, http://msdn.microsoft.com/en-us/library/ms974598.aspx, I have been trying to get a script going that will pop up a message telling the user that they need to change their password, but I've hit a brick wall with this part of the script.

Set objDomain = GetObject("LDAP://" & objADSystemInfo.DomainDNSName)
Set objMaxPwdAge = objDomain.Get("maxPwdAge")

    If objMaxPwdAge.LowPart = 0 Then
        WScript.Echo "The Maximum Password Age is set to 0 in the " & _
                     "domain. Therefore, the password does not expire."
        WScript.Quit

For some reason, it will not pull the max password age.  Now I'm using Fine-Grained passwords for the test account, but I have also tried setting the max password age in the default domain policy, and I still get the message that the "age is set to 0 and the password will not expire" even though I know the policy is functioning on the account.

I was thinking I might need to try and get the information from this attribute http://msdn.microsoft.com/en-us/library/cc220303.aspx, but I am unsure of how to call this information.

If I can just get the script to pull the max password age, I believe I can get the rest of the script working.


  • Edited by Hotpocketdeath Wednesday, July 03, 2013 2:16 PM code correction
July 3rd, 2013 1:27pm

You may want to post this question in the Scripting forum:

http://social.technet.microsoft.com/Forums/scriptcenter/en-US/home?forum=ITCG

Free Windows Admin Tool Kit Click here and download it now
July 3rd, 2013 2:09pm

Hi,

Take a look at this URL: http://www.activexperts.com/admin/scripts/vbscript/0176/

Best regards,

/Jimmy

July 3rd, 2013 3:31pm

Hi,

Take a look at this URL: http://www.activexperts.com/admin/scripts/vbscript/0176/

Best regards,

/Jimmy

July 3rd, 2013 4:51pm

If you open up adsiedit, connect to the Default Naming Context and open up the properties on the domain what do you see for maxPwdAge? I don't recall where I got the code from in line 3 but I've always used the below to read this out:

Set oDomain = GetObject("LDAP://" & GetObject("LDAP://rootDSE").Get("defaultNamingContext"))
Set maxPwdAge = oDomain.Get("maxPwdAge")
numDays = CCur((maxPwdAge.HighPart * 2 ^ 32) + maxPwdAge.LowPart) / CCur(-864000000000)
Wscript.echo numDays

I hope this helps,
Mark.

 

Free Windows Admin Tool Kit Click here and download it now
July 3rd, 2013 5:31pm

Ok, I went back to the drawing board and got the script to work.  Problem is that it still wants to pull the maxPwdAge from the Domain Policy instead of the Fine-Grained Policy.

Full Script

On Error Resume Next
Const ADS_UF_DONT_EXPIRE_PASSWD = &h10000
Const E_ADS_PROPERTY_NOT_FOUND  = &h8000500D
Const ONE_HUNDRED_NANOSECOND    = .000000100
Const SECONDS_IN_DAY            = 86400
Const NO_EXPIRE                 = 99999

'**************************************************************
'* Function to get number of days left before password change *
'**************************************************************

Function PasswordDaysLeft()

'Default to zero days left...an error can occur if the user
'is forced to change their password. This will cover that
PasswordDaysLeft = 0

Set objADSystemInfo = CreateObject("ADSystemInfo")              
Set objUser = GetObject("LDAP://" & objADSystemInfo.UserName)   


intUserAccountControl = objUser.Get("userAccountControl")
If intUserAccountControl And ADS_UF_DONT_EXPIRE_PASSWD Then
    PasswordDaysLeft = NO_EXPIRE
    Exit Function
Else
    dtmValue = objUser.PasswordLastChanged
    If Err.Number = E_ADS_PROPERTY_NOT_FOUND Then
        PasswordDaysLeft = NO_EXPIRE
	Exit Function
    Else
        intTimeInterval = Int(Now - dtmValue)
    End If

    Set objDomain = GetObject("LDAP://" & objADSystemInfo.DomainDNSName)
    Set objMaxPwdAge = objDomain.Get("maxPwdAge")

    If objMaxPwdAge.LowPart = 0 Then
	PasswordDaysLeft = NO_EXPIRE
        Exit Function
    Else
        dblMaxPwdNano = _
            Abs(objMaxPwdAge.HighPart * 2^32 + objMaxPwdAge.LowPart)
        dblMaxPwdSecs = dblMaxPwdNano * ONE_HUNDRED_NANOSECOND
        dblMaxPwdDays = Int(dblMaxPwdSecs / SECONDS_IN_DAY)

        If intTimeInterval >= dblMaxPwdDays Then
         PasswordDaysLeft = 0
        Else
         PasswordDaysLeft = Int((dtmValue + dblMaxPwdDays) - Now)
        End If
    End If
End If

End Function

'**************************************************************
'* Main Application Loop                                      *
'**************************************************************

'Sleep for 1 minutes
WScript.Sleep(1 * 1000 * 60)

bRunning = TRUE
'1 minutes is up, check every 1 min to see if password is too old
while (bRunning)
 if (PasswordDaysLeft() <= 14) then
  MsgBox("Your password is about to expire.")
  bRunning = FALSE
 end if
 WScript.Sleep(1 * 1000 * 60)
wend 

Domain max password age is 365

Fine-Grained password age is 150

Test account password was set 146 days ago.  For testing, I changed the line "if (PasswordDaysLeft() <= 14) then" to "if (PasswordDaysLeft() <= 219) then", the script works and I get a popup saying it's about to expire.  But that's a dead giveaway that it's using the domain max age.



July 3rd, 2013 6:39pm

When you bind to the default naming context and read maxPwdAge you are reading the default domain account policy that's written there by the PDCe DC. This series of articles does a good job of explaining this relationship: http://jorgequestforknowledge.wordpress.com/2010/09/27/password-policies-and-account-lockout-policies-within-an-ad-domain-part-1/

I haven't used fine-grained password policies in the past so I unfortunately can't test this but by the looks of the following article you need to bind to the user account in question and attempt to retrieve the msDS-ResultantPSO constructed attribute: http://technet.microsoft.com/en-us/library/cc770848(v=ws.10).aspx If that's null then you need to do the maths with PasswordLastChanged against maxPwdAge. If it's not null then you need your script to do a bit more processing to find the relevant PSO in the Password Settings Container and read the equivalent of maxPwdAge out of that PSO. By the looks of this article, that is the msDS-MaximumPasswordAge  attribute:
http://technet.microsoft.com/en-us/library/cc754461(v=ws.10).aspx
I hope this helps,
Mark

 
Free Windows Admin Tool Kit Click here and download it now
July 3rd, 2013 8:13pm

As Mark mentioned, your script is only looking at the Default Domain Policy. I too am trying to do what you are doing, however am getting stuck on the coding of it. What i have so far is:

On Error Resume Next
Dim objSysInfo, objUser, ResultantPSO
Set objSysInfo = CreateObject("ADSystemInfo")
Set objUser = GetObject("LDAP://" & objSysInfo.UserName)
objUser.GetInfoEx Array("msDS-ResultantPSO"),0
ResultantPSO = objUser.get("msDS-ResultantPSO")
WScript.Echo "PSO: " & ResultantPSO
Set objSysInfo = nothing
Set ibjUser = nothing
Set ResultantPSO = nothing

The above script will retrieve the currently active PSO that is set for the user (and is generated on the fly by using GetInfoEx).

From there i tried to add the following code *BUT IT DOESNT WORK* and I cant seem to work out what im doing wrong.

Set objPSO = GetObject("LDAP://" & ResultantPSO)
Set MaxPWAge = objPSO.get("msDS-MaximumPasswordAge")
WScript.Echo "MaxPWAge: " & maxPWAge


In theory, a combination of your code and mine would get the script to work, with the following workflow:

1. Get current logged on user details
2. Retrieve ResultantPSO setting.
3. If exist, retrieve MaximumPasswordAge from that particular PSO object, otherwise retrieve domain policy object.
4. Set either value to the same variable, of objMaxPwdAge
5. calculate the time to days
6. Prompt user if date is within the threshold.

If anyone is able to combine the two scripts, i would greatly appreciate it as I am not able to figure out how to do it. (i was trying to not search search, and use the same method as for retrieving user info but i dont think its possible).

Kind Regards,
Ivan
http://ivan.dretvic.com


December 31st, 2013 12:52am

From there i tried to add the following code *BUT IT DOESNT WORK* and I cant seem to work out what im doing wrong.

Set objPSO = GetObject("LDAP://" & ResultantPSO)
Set MaxPWAge = objPSO.get("msDS-MaximumPasswordAge")
WScript.Echo "MaxPWAge: " & maxPWAge

I was working on this as well and finally figured out a solution.  

Set objPSO = GetObject("LDAP://" & ResultantPSO)
Set objMaxPwdAge = objPSO.Get("msDS-MaximumPasswordAge")

intPwdAge = (((objPwdAge.HighPart * (2^32) + objPwdAge.LowPart) / (60 * 10000000)) / 1440) * -1

Wscript.Echo "The " & objPSO.CN & " maximum password age is " & intPwdAge & "."

From what I have learned, 64-bit integers are not supported in VBScript, which is why we have to do some Integer magic.  

Hotpocketdeath's last post used the same formula, so the answer was not that far away.  However, I ended up using the following link as a resource.  I combined to one line and multiplied by -1 to make it a positive integer.    

http://blogs.technet.com/b/heyscriptingguy/archive/2010/01/27/dandelions-vcr-clocks-and-last-logon-times-these-are-a-few-of-our-least-favorite-things.aspx

Free Windows Admin Tool Kit Click here and download it now
September 2nd, 2014 7:57pm

Hi there Jasen

What did your final script look like please.

Thanks

August 7th, 2015 12:09am

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics