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
}
Related
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
We have users with minor but annoying differences in naming standards that are loosely followed making scripting a pain. The company has been around a while and depending on who was working in IT when the employee was hired the account could follow any naming convention if followed at all.
In one forest we have accounts that start with a-, the manager attribute of that account is the DN of the account owners other/main account without many other common attributes populated. I then need to look up the managers account and grab their SamAccountName. Then I need to add s- to the SamAccountName and do search for that to see if it exists.
Then I need to write the original SamAccountName, the second SamAccountName and the s-SamAccountName and something like a check box if its a valid account name all to a CSV.
without rewriting the script and passing everything to/from a var and then processing that I dont see a way to do it. This script looks up roughly 800 users and processes that three time, so it already takes a while to run without slowing it down with a bunch of var transfers.
$test = get-aduser -ldapFilter "(SamAccountName=a-*)" -Server XXX.int:3268 -Properties manager |
Select -ExpandProperty manager | Get-ADUser -Server XXX.int:3268 |
Select -ExpandProperty samaccountname
$test
You can do something like this to get you started.
$test = get-aduser -ldapFilter "(SamAccountName=a-*)" -Server XXX.int:3268 -Properties manager |
Select-Object SamAccountName,
#{n='Manager';e={Get-ADUser $_.Manager -Server XXX.int:3268 | Select -Expand SamAccountName}},
#{n='s-Manager';e={Get-ADUser "s-$($_.Manager)" -Server XXX.int:3268 | Select -Expand SamAccountName}}
$test
I am not clear if you need to add s- to the manager's or the original account's SamAccountName. The script above adds s- to the manager's SamAccountName.
In scripts where multiple AD lookups are required, sometimes it is best to do one larger lookup and then filter that result for what you need.
Explanation:
The main technique of the script makes use of delayed-script binding with the Select-Object command. Each user object found by the original Get-ADUser command is piped into Select-Object and is accessible by the pipeline variable $_. The hash table select can be updated fairly easily to meet your needs.
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
I'm trying to use below script to get all active users from AD, however, I don't get any results although I know there are data with value 512.
Do you know what I have wrong here?
Get-ADUser -filter {$userAccountControl -eq "512"} -properties Name,userAccountControl -Server myserver.local | Export-CSV "E:\Folder\ADusers.csv" -NoTypeInformation -Encoding UTF8
As pointed out in the comments, the $ does not belong. That tells PowerShell that you want to use the value in a variable called $userAccountControl and compare that to 512. Considering that you probably never set a variable called $userAccountControl, that means it's comparing nothing to 512 and seeing that it's not true for every account.
Remove the $ and it will compare the property called userAccountControl to 512.
Get-ADUser -filter {userAccountControl -eq "512"} -properties Name,userAccountControl -Server myserver.local | Export-CSV "E:\Folder\ADusers.csv" -NoTypeInformation -Encoding UTF8
you could try the LDAPfilter syntax
Get-ADUser -property userAccountControl -LDAPfilter "(userAccountControl=512)"
However, this might not be a good method : userAccountControl is a binary field, each bit representing a binary value (see https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-pro )
for example :
512 is "normal account", wich you want
66048 is "normal account" + "password dont expire", which you might also want
514 is "normal account" + "account disabled" , which you might not want
all value would match
-band 512
you need to clarify what's an active user.
All the new user accounts created in Active Directory are kept as disabled and the option "user must change password on next login" is ticked. This accounts will remain as disabled for 7 days and in the 8th day it needs to be enabled.. Creating the account is already done by another script and I am stuck with account enable part.
How can I archive enabling these account using PowerShell script? How to use all account properties like lastlogon date, account creation date, account status(disabled), and the option "user must change password on next login" to validate and find the user is a new user which needs to enable?
get-aduser -f {-not ( lastlogontimestamp -like "*") -and (enabled -eq $false) -and (pwdLastSet -eq 0)} |
Select-Object name,SamAccountName |
export-csv -path data.csv
Use filters to filter according to your criteria. This snippet gets user objects for accounts that are disabled then filters out those less than a week old. It stores them in a collection of user objects. You can further filter $userlist by any user object property using the $variable | $variable where { [filter] } format. The filters can be as complex as you want. I like putting one on each line so I can easily comment out given filters. Use the filtered list for whatever you want.
$WeekAgoDate = $(Get-Date).addDays(-7)
$userlist = get-aduser -filter { enabled -eq $false } -Properties *
$userlist = $userlist | Where { $_.created -lt $WeekAgoDate }
"$userlist.count accounts found"
$userlist | select name, samaccountname | format-table
To enable $userlist:
$userlist | Enable-ADAccount
Here is how you can do it but you will need to write the script:
Check the whenCreated attribute It has a date time stamp and you can use that to see how old the account is.
If the account is older then 7 days and the account is disabled set the userAccountControl attribute to enabled IE 512 and then set your user must change password property true