Contradictory values from Active Directory regarding password expiry date - powershell

I am using Powershell to determine the password expiry date for domain accounts. I have used the following command to get this information:
Get-ADUser -Filter {SamAccountName -eq "<username>"} -Properties "DisplayName" , "msDS-UserPasswordExpiryTimeComputed"
I then covert this value to a meaningful date by using:
[datetime]::FromFileTime(<computed filetime from above command>)
This works fine for all the domains I use, except one. In that domain I get a value of 9223372036854775807 as the msDS-UserPasswordExpiryTimeComputed. I am not able to use the FromFileTime function to convert this number into a date. It throws as error. Upon researching I have found that this number means that the password is set not to expire. However, I know that passwords do expire in this domain. Further, the PasswordNeverExpires property from the Get-ADUser cmdlet shows as False.
How can I get 9223372036854775807 from msDS-UserPasswordExpiryTimeComputed attribute and get a False from the PasswordNeverExpires property? This seems like a contradiction. What am I missing? Are there other situations when msDS-UserPasswordExpiryTimeComputed could be 9223372036854775807 too? Thanks.

The documentation lists several conditions in which msDS-UserPasswordExpiryTimeComputed returns 9223372036854775807 aka 0x7fffffffffffffff aka [int64]::MaxValue (TO refers to a given target object):
If any of the ADS_UF_SMARTCARD_REQUIRED, ADS_UF_DONT_EXPIRE_PASSWD, ADS_UF_WORKSTATION_TRUST_ACCOUNT, ADS_UF_SERVER_TRUST_ACCOUNT, ADS_UF_INTERDOMAIN_TRUST_ACCOUNT bits is set in TO!userAccountControl, then TO!msDS-UserPasswordExpiryTimeComputed = 0x7FFFFFFFFFFFFFFF.
[...]
Else, if Effective-MaximumPasswordAge = 0x8000000000000000, then TO!msDS-UserPasswordExpiryTimeComputed = 0x7FFFFFFFFFFFFFFF (where Effective-MaximumPasswordAge is defined in [MS-SAMR] section 3.1.1.5).
Without knowing all the details, it seems that the msDS-UserPasswordExpiryTimeComputed property returning 0x7FFFFFFFFFFFFFFF indicates that there is effectively no password expiration, for a variety of reasons, only one of which is PasswordNeverExpires being set to $True.
Therefore, you could filter out such values:
Get-ADUser -Filter "SamAccountName -eq '<username>'" `
-Properties DisplayName, msDS-UserPasswordExpiryTimeComputed | #`
Where { $_.msDS-UserPasswordExpiryTimeComputed -ne 0x7FFFFFFFFFFFFFFF } | ForEach {
# ...
$dt = [datetime]::FromFileTime($_.msDS-UserPasswordExpiryTimeComputed)
}
It may even be possible to incorporate the test for 0x7FFFFFFFFFFFFFFF into the -Filter argument.
As an aside: Note that I've used a string rather than a script block ({ ... }) as the -Filter argument, because using script blocks is best avoided.

I don't have a DC at the moment to test but theoretically this can work.
Get the Password Policy of the domain with Get-ADDefaultDomainPasswordPolicy -Identity Domain.com
Get the passwordLastSet property of a user like this: (Get-ADUser -Identity <UserName> -Properties *).PasswordlastSet and convert to appropriate [datetime] object based on the domain's Culture: Ref Get-Culture. DateTime format may change based on regional settings like US and UK.
Now that you have the passwordpolicy of say 60 days and the passwordlastset date, it is simple math to determine if the password is expiring or not.
Alternately, on 2016 servers, you can also use something like:
Search-ADAccount -AccountExpiring -TimeSpan 10.00:00:00

Related

AD GUI shows properties that PowerShell returns empty

I want to get a list of all AD Users and their creation time and last logon time. First I used the Active Diretory Users and Computers app and activated the Advanced Features. In the Attribute Editor I can see the properties are called LastLogon and WhenCreated.
So I did this:
$allUsers = Get-ADUser -Filter * -Properties SamAccountName,LastLogon,WhenCreated
$allUsers | select SamAccountName,LastLogon,WhenCreated
However LastLogonand WhenCreated are only filled for 13 of 500 Users. In the Attribute Editor these values are filled for a lot more...
When I query one user only that has these values in the Attribute Editor with Get-ADUser -Identity $User -Properties * I see that the attributes are called LastLogonDateand Created (values are shown empty).
So I searched for those attributes:
$allUsers2= Get-ADUser -Filter * -Properties SamAccountName,LastLogonDate,Created
$allUsers2 | select SamAccountName,LastLogonDate,Created
Then again those 13 have the info the rest doesn't.
Has anyone an idea how I get those values? (I am going to export them with Export-CSV so another way to get those in Excel is ok, too )
As requested my comments as answer.
First attempt:
Add the -Server switch on Get-ADUser and have it query the same Domain Controller you are currently connected to with Active Directory Users and Computers. It may be that you are asking for properties that have not yet been synchronized (especially the lastLogon time stamp which I believe is synced only once every 14 days unless you have specified a different value for the ms-DS-Logon-Time-Sync-Interval attribute on the domain default naming context.)
--> didn't apply because you're running this on the DC itself
Second attempt:
Try ADSI as in $searcher = [adsisearcher]'(&(objectCategory=person)(objectClass=user))'; $searcher.FindAll()
--> same results as with Get-ADUser; still empty values
Third attempt:
Check PowerShell version(s)
--> apparently the DC had PS version 4. With version 5.1 it works
First, look at what properties your cmdlet has:
$a = Get-ADUser -server 'DomenNameTest.en' -Identity 'makarovayu' -Properties *
$a | Get-Member
I recommend copying the received data into a notepad in order to copy the available field names later.
2-Let's declare an array and use the cmdlet to try to collect information on the required fields
$userList = Get-ADUser -server 'DomenNameTest.en' -Properties SamAccountName,Name -Filter * |
#Do not forget that the comanlet has a limitation and can fall off on timeout.See how I work with each property in [select]
Select #{Name = "SamAccountName"; Expression={$_.SamAccountName}},#{Name = "Name"; Expression={$_.Name}} |
#Uploading data to [csv]
Export-Csv -Path "D:\Users\userTest\Desktop\userList.csv" -Append -NoTypeInformation -Encoding Default -Delimiter ';'
Remove-Variable a,userList #Clear the variables

Subtraction between two dates in Powershell is evaluating to null

I am using the following code to display some values , the subtraction between two dates as in the expression
[datetime]::Now.AddDays(-365) - $_.PasswordLastSet).Days
is evaluating to null in my data, what could be the reason, how to fix this code ?
Get-ADUser -Filter * -Properties PasswordLastSet, Mail -SearchBase $searchBase |
Where-Object{
([datetime]::Now.AddDays(-365) - $_.PasswordLastSet).Days -in #(" ",364, 350, 354, 340, 335,' ','',"")
}|
Select-Object name, PasswordLastSet,#{n='DaysUntilExpired';e={(([datetime]::Now.AddDays(365) - [datetime]::$_.PasswordLastSet).Day).days}} |
Add-Content -Path C:\Temp\PasswordExpList.txt
There are two obvious bugs in your code, both here:
#{n='DaysUntilExpired';e={(([datetime]::Now.AddDays(365) - [datetime]::$_.PasswordLastSet).Day).days}}
However, I can't tell if you should specify this:
#{n='DaysUntilExpired';e={([datetime]::Now.AddDays(365) - $_.PasswordLastSet).Days}}
Or, if you're looking for the last day of the month prior to the expiration date, then this:
#{n='DaysUntilExpired';e={(([datetime]::Now.AddDays(365) - $_.PasswordLastSet.Day).Days}}
As the comments mention, [datetime]::$_.PasswordLastSet is nonsense. That's the first bug. The second one is that the property of a timespan is Days, not Day. Day is a property of a datetime.
However, assuming you're trying to calculate the password expiration date defined by AD password policy, you should look at the msDS-UserPasswordExpiryTimeComputed property in AD because that's what that property contains. You have to convert it from a large integer date, but that's what [datetime]::fromfiletime() does.
Get-ADUser -Filter * -Properties PasswordLastSet, Mail, 'msDS-UserPasswordExpiryTimeComputed' -SearchBase $searchBase |
Select-Object -Properties Name, PasswordLastSet, #{n='PasswordExpirationDate';e={[DateTime]::FromFileTime($_.'msDS-UserPasswordExpiryTimeComputed')}}
This doesn't do everything your script does, but then I don't understand why you're looking for accounts whose password was set an exact number of days in the past. That doesn't seem useful.

Use Search-ADAccount to find all accounts with Account Expiration Date Not Set

Is there a way in Powershell to list all AD Accounts in the domain which don't have an account expiration date set? ie the accounts expiration date is set to 'Never'.
I can utilise Get-ADuser to get the expiry of a single account.
get-aduser username | Select-Object -Property name,Enabled,AccountExpirationDate,DistinguishedName | Where-Object {$_.AccountExpirationDate -eq '`n'}
However is there a way to use "Search-ADAccount -AccountExpiring....." to get a list of all accounts in the domain which don't have an account expiration date set or a better way to do this?
When I use the below command I don't get any return information however I setup a test account with no Expiration Date (ie Never) set to test the command however the account is not displayed in the results.
Search-ADAccount -AccountExpiring | Select-Object -Property name,AccountExpirationDate | Where-Object {$_.AccountExpirationDate -eq '`n'}
Solution with Get-ADUser
You can still use Get-ADUser to find the accounts, if you use the correct Filter:
Get-ADUser -Filter {-not(AccountExpirationDate -like "*") } -Properties AccountExpirationDate | Select-Object -Property name,Enabled,AccountExpirationDate,DistinguishedName
This will give you every Account where no value is set for the Attribute AccountExpirationDate.
Solution with Search-ADAccount
If you still want to use Search-ADAccount you have to offer a timestamp until the Accounts should expire (take a look at the ref). There is no option to search for accounts that doesn't have any value set. You allways have to offer some kind of DateTime.
For example, if you want to find all Accounts that expire in 2 Weeks you have to options:
First:
A solution with a date on which the account expires:
Search-ADAccount -AccountExpiring -DateTime ((Get-Date).AddDays(14))
Second:
A solution on the remaining days until the account expires:
Search-ADAccount -AccountExpiring -Timespan "14"
I found a solution combining Paxz reply above and this Technet thread. The 'AccountExpirationDate' is too hard to filter out of AD effectively without exporting every users Account Expiration Date and formatting it to readable date-time string.
The Technet article linked above explains that "accountExpires AD attribute is a large integer (64-bit) representing dates (in UTC) as the number of 100-nanosecond intervals since 12:00 am January 1, 1601. The AccountExpirationDate parameter is a property method that converts the value of accountExpires into the corresponding date in the local time zone". From additional research when an account is set to Never expire it will have an 'accountexpires' integer of 9223372036854775807.
'Get-ADUser' can be used to filter for all accounts where the 'accountexpires' integer equals '9223372036854775807'. This avoids the need to convert the date for every account in the Domain.
Get-ADUser -filter {Enabled -eq 'True' -and accountexpires -eq '9223372036854775807'} | Select-Object name,enabled,accountexpires,distinguishedName

Get-ADUser -Properties not returning PasswordNeverExpires for all users

I am trying to list all users that have the PasswordNeverExpires flag set.
If I use
Get-ADUser
I get a list of all users in my domain, along with a load of default properties.
If I use
Get-ADUser -Filter * -Properties Name | Format-Table -Property Name -AutoSize
I also get a list of all usernames in my domain, as a table.
When I use
Get-ADUser -Filter * -Properties Name,PasswordNeverExpires | Format-Table -Property Name,PasswordNeverExpire
I get a table that contains a full list of usernames, but ONLY the following accounts have either True or False in the PasswordNeverExpires column
Guest
krbtgt
Administrator
SBSMonAcct
Network Administrator
<MyDomainAdminAccount>
SPSearch
<AnAdministratorAccountForOneOfOurSoftwareVendors>
<AnAccountThatWasCopiedFromTheDomainAdministratorAccount>
<AnotherAccountCopiedFromTheDomainAdministratorAccount>
All the other items/usernames in the table have empty/blank/non-existent values.
I have also tried
Get-ADUser -LDAPFilter "(&(!userAccountControl:1.2.840.113556.1.4.803:=2)(userAccountControl:1.2.840.113556.1.4.803:=65536))"
but that only returns
<MyDomainAdminAccount>
SPSearch
Why is the PasswordNeverExpires flag not being picked up for all users? Thanks.
PasswordNeverExpires is calculated from the userAccountControl attribute.
Probably the fastest way to search for users that have that flag set is as follows:
Get-ADUser -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=65536)" -Properties PasswordNeverExpires
See the documentation for more information on searching using a bitwise filter. 65536 (0x10000) corresponds to the ADS_UF_DONT_EXPIRE_PASSWD bit position, so this LDAP search filter searches only for accounts that have that flag set.
Hmm, your third line pulls the property "PasswordNeverExpires" but Selects "PasswordNeverExpire". If this was just a typo in your question this disregard. If not then there is your answer. :-)

How to find AD users where no password is set?

How can I find AD-users where the password has not been set?
Some backgrund information:
We have a script (written in C#) that creates AD users and sets a default password. The user is prompted to change this password at the first attempt to login.
The script runs fine but recently we discoverd that the password was not set in a few cases. And it keeps happening now and then. We are investigating this issue at the moment.
Now I wan't to find the users where the default password hasn't been set by the script in order to set the default password manually where required.
When I look at the AD attributes of a user I can't see an obvious way to find these users without (default) password set.
The pwdLastSet is 0x0 when the user is created form the script and the password has been set with succes (tested). So how to know when the password hasn't been set?
What you are after is Get-ADReplicationAttributeMetadata.
Give this one a go, you should be able to set it to what you need. It is fairly fast so you can add results to a hashtable and pump that out when done.
I have this pumped into a nice little function with $Username and $ServerName as params.
The main attributes you are after are pwdLastSet, ntPwdHistory and lastLogonTimestamp.
Get-ADuser $username |
Get-ADReplicationAttributeMetadata -Server $ServerName |
Where-Object Version -GT 1 | Select AttributeName, LastOriginatingChangeTime, Version | OGV
The version will tell you how many times it has been changed while the LastOriginatingChangeTime will tell you when. If you do a Select * and remove the Where-Object Version -GT 1 you can see the other data it pulls.
EDIT:
Looks like Get-ADReplicationAttributeMetadata is not available on version 2.
PSVersion Get-ADReplicationAttributeMetadata
--------- ----------------------------------
2 False
3 True
4 True
5 True
The PasswordLastSet attribut could do it. It saves the date when the password was set the last time.
Get-ADUser -Filter * -Properties PasswordLastSet | Where-Object { $_.PasswordLastSet -eq $null }
It IS possible to have users in AD that have a blank password, despite activated password policy. This is due to the PASSWD_NOTREQD flag in the userAccountControl property of a user. The value for this PASSWD_NOTREQD flag is 32.
To check for enabled users that have this flag (and therefore don't need a password) you can do
$noPwdRequired = Get-ADUser -LDAPFilter "(&(objectClass=user)(objectCategory=person)(userAccountControl:1.2.840.113556.1.4.803:=544))"
foreach($user in $noPwdRequired ){
Write-Host $user.sAMAccountName # or displayName or whatever you want to identify the user
}
Normally, a user object has the default value of 512 (NORMAL_ACCOUNT). With the PASSWD_NOTREQD they will have a value of 544
Use value 546 to find disabled accounts that also has this flag set.
You can update this for any of those found users doing something like
$noPwdRequired = Get-ADUser -LDAPFilter "(&(objectClass=user)(objectCategory=person)(userAccountControl:1.2.840.113556.1.4.803:=544))"
foreach($user in $noPwdRequired ){
Set-ADAccountControl $user -PasswordNotRequired $false
}