Query AD for user account passwords expiring within a given date range

I need my script to find Active Directory (AD) user accounts with passwords due to expire within 14 days. I have the below script so far, but my problem is that logic for getting only the accounts within the specified date range.

$maxPwdAge=(Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Days
$14days=(get-date).AddDays(14-$maxPwdAge).ToShortDateString()

$userOU1='OU=EmployeesHere,DC=contoso,DC=com'
$userOU2='OU=EmployeesThere,DC=contoso,DC=com'

$userOU1,$userOU2 | ForEach-Object {

$flag=Get-ADUser -SearchBase $_ -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False -and PasswordLastSet -gt 0} –Properties passwordLastSet | 
            Where {($_.PasswordLastSet).ToShortDateString() -le $14days} -and ($_.PasswordLastSet).ToShortDateString() -ge (get-date).AddDays(-$maxPwdAge).ToShortDateString() | 
            Select Name,Enabled,PasswordLastSet,samAccountName,emailAddress

$flag 
}

Open in new window


Unless there is a completely different way of doing this, the main problem with this script is the 'Where' comparison. I am getting accounts in the results that have 'PasswordLastSet' times that are out of the range that I specified and I can't figure out why they are being flagged.

To troubleshoot I plugged in actual dates instead of variables, i.e. I used this line,

Where {($_.PasswordLastSet).ToShortDateString() -le "7/8/2015"} -and ($_.PasswordLastSet).ToShortDateString() -ge "6/24/2015"}

Open in new window


With the date range specified to find accounts before 7/8/2015 and after 6/24/2015, I am still seeing accounts with PasswordLastSet flagged with dates such as 8/18/2015 or 9/8/2015. How can I fix this and only see the accounts within the range specified?

Thank you
ryanmavesAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Y RizviyCommented:
You can use this tool http://www.systemtools.com/somarsoft/?somarsoft.com

DumpSec 2.8.7
Jeremy WeisingerSenior Network Consultant / EngineerCommented:
The only thing I can see is that your where statement has an error in it.

The issue is that the curly brace needed to be moved to the end of operator statements. Here's the corrected line:
 Where {($_.PasswordLastSet).ToShortDateString() -le $14days -and ($_.PasswordLastSet).ToShortDateString() -ge (get-date).AddDays(-$maxPwdAge).ToShortDateString() }|

Open in new window

ryanmavesAuthor Commented:
@Y Rizviy

Thanks, but no thanks. I want this solution accomplished with PowerShell and not a third party software.
Has Powershell sent you back into the Stone Age?

If managing Active Directory using Windows Powershell® is making you feel like you stepped back in time, you are not alone.  For nearly 20 years, AD admins around the world have used one tool for day-to-day AD management: Hyena. Discover why.

ryanmavesAuthor Commented:
@Jeremy

Hi Jeremy, thanks but I must have entered that typo when writing it for this thread. There are no typo's in my actual script and the results kick out but I am still seeing accounts outside of my date range specified.

Here is another thing to point out, I am using -le and -ge to compare $_.PasswordLastSet date with my date range. If I tell PowerShell to do some comparisons on dates just by themselves, I don't get the results I would expect, look at these comparisons below:

PS C:\scripts> "7/27/2015" -ge "6/23/2015" 
True

PS C:\scripts> "7/27/2015" -ge "6/23/2015" -and "7/27/2015" -le "7/7/2015"
True

PS C:\scripts> "7/27/2015" -le "7/7/2015"
True

PS C:\scripts> "7/27/2015" -ge "7/7/2015"
False

Open in new window


How is 7/27/2015 less than 7/7/2015 but greater than 6/23/2015?

Something is not right about how PowerShell is computing this...
David Johnson, CD, MVPOwnerCommented:
cls
$14days = (get-date).adddays(434)

$userOU1='OU=EmployeesHere,DC=contoso,DC=com'
$userOU2='OU=EmployeesThere,DC=contoso,DC=com'
$usersOU1,$usersOU2 | % {
$users = Get-ADUser -searchbase $_ -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False} –Properties "DisplayName", "msDS-UserPasswordExpiryTimeComputed"|
 Select-Object -Property "Displayname",@{Name="ExpiryDate";Expression={[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}}
foreach ($user in $users) 
    {
    if ($user.ExpiryDate -le $14days){
        write-output ($user.DisplayName + "     " +$user.ExpiryDate)
        }
    }
 }
  

Open in new window

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
ryanmavesAuthor Commented:
Hey David, your script got me on the right track and I was able to work it out from there.

I like how you used the property, "msDS-UserPasswordExpiryTimeComputed", but I have no idea how you went about finding that property to use in the first place.

It is not listed under -Properties * |Select *
I'd love to know for next time.

Thanks again!
Jeremy WeisingerSenior Network Consultant / EngineerCommented:
ryanmaves, I know you already solved this with David's script but I just wanted to explain the issues you were getting when comparing the dates.

When you run this:
"7/27/2015" -le "7/7/2015"

Open in new window

It is comparing two strings and it will essentially put them in alphabetic order. It sees it like this:
7/27/2015
7/7/2015

Open in new window

Since the 2 and the 7 line up in character count, the two is less.

To compare the dates you can do something like this:
(Get-Date "7/27/2015") -ge (Get-date "6/23/2015") 
(Get-Date "7/27/2015") -ge (Get-Date "6/23/2015") -and (Get-Date "7/27/2015") -le (Get-Date "7/7/2015" )
(Get-Date "7/27/2015") -le (Get-Date "7/7/2015" )
(Get-Date "7/27/2015") -ge (Get-Date "7/7/2015" )

Open in new window

This should return your expected results.

When I initially looked at your script I was looking for any ".ToString()" parameters on your dates but they looked fine. So I'm not sure what was going on there.

Anyways, I'm glad David was able to solve it for you.

Jeremy
ryanmavesAuthor Commented:
Thank you for taking the time to share Jeremy. I see now why it was giving back false positives. That's definitely a "Gotcha" in PowerShell that was hanging me up for a while. I really appreciate the explanation so I can take away the knowledge.

Cheers~
Kevin StanushApplication DeveloperCommented:
msDS-UserPasswordExpiryTimeComputed is a computed attribute, which is why it won't appear in an attribute dump for any users.  It was added in Windows 2008 Server and is a great thing to know about.  

The only other one like this might be 'CanonicalName' and 'msds-user-account-control-computed'.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Powershell

From novice to tech pro — start learning today.