I am using Powershell to pull simple data from AD. The script pulls usernames from a text file and is similar to:
Import-Module ActiveDirectory
Get-Content c:\temp\userlist.txt | Foreach-Object {
$user = $_
$fullname = (Get-ADuser -identity $user | foreach {$_.Name})
$email = (Get-ADUser -identity $user -properties * | foreach {$_.mail})
$lastlog = (Get-ADUser -identity $user -properties * | foreach {$_.lastLogon})
$lastlogstr = (Get-Date -format "yyyy MMM d" $lastlog)
"$user,$fullname,$email,$lastlog,$lastlogstr"
}
When I run this, all the correct data is pulled, and I'll see (for example)
XXXXX,John Smith,jsmith#company.com,130252373811789928,0413 Oct 2
When I check Active directory, and use the Attribute editor to view "lastLogon", I see
10/2/2013 8:29:41PM Central Daylight Time
and if I click to View that field, I see 130252373811789928 as shown above.
But for absolutely every user I query, and no matter how I format the date (i.e. as above, using D, etc.) it -always- puts in 0413 instead of 2013. Similarly, it shows 0411 for 2011, etc.
I cannot imagine why this is happening. Powershell is being run from a Windows 7 system, and I have tried using both the ISE and the powershell command.
Can you try :
$date = [datetime]::fromfiletime($lastlog)
$date.ToString("yyyy MMM d")
or
$date = [datetime]::FromFileTimeUtc($lastlog)
$date.ToString("yyyy MMM d")
In ActiveDirectory Microsoft specifics attibuts that deal with time are stored as a Windows file time. It's a 64-bit value that represents the number of 100-nanosecond intervals that have elapsed since 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC).
Related
HelloThere,
we get AD-Users from an import script that runs daily and if a user was found in a csv we write the current date in the extensionattribute1 in format "dd-MM-yyyy".
Now we want to calculate if a AD user has a different date in the extensionattribute1 (minus 90 days) then we want to inform their managers what should happen to those accounts.
Interesting is, with my query we get correct Users and some Users between 30 and 90 Days. I don't know what is wrong in my code or with the syntax - can someone pls help? Thanks
sorry, i am not a PowerShell Pro by now but working on it :D
Greetings
# get date -90 Days
$date = (Get-Date).AddDays(-90).ToString("dd-MM-yyyy")
$date
# it gets "27-08-2022" -> so the script should only show users less than this date
# query for extensionattribute1 less than 90 days
get-aduser -properties ExtensionAttribute1 -filter {(enabled -eq "true") -and (extensionAttribute1 -gt $date)}
# here the output is partial correct, we get users with an extensionattribute1 from
# 31-05-2022
# 30-06-2022
# 29-07-2022
# but also some Users with a date from between 30 and 90 days what is incorrect =/
# 30-09-2022
# 31-10-2022
You should not compare date strings against eachother, especially not when you use date format "dd-MM-yyyy", since that is a non-sortable date format. (you might get away with it if you would have used format 'yyyy-MM-dd'..)
Instead, convert the value found in the ExtensionAttribute1 to a DateTime object first and compare that to a real reference DateTime object.
Try
# get date -90 Days
$date = (Get-Date).AddDays(-90).Date # set to midnight with time part '00:00:00'
# query for extensionattribute1 less than 90 days
Get-ADUser -Properties ExtensionAttribute1 -Filter 'Enabled -eq $true -and ExtensionAttribute1 -like "*"' |
Where-Object {[datetime]::ParseExact($_.ExtensionAttribute1, 'dd-MM-yyyy', $null) -gt $date}
I'm working on scripts which send emails to users 14, 10, 7, 3 days before password expiring.
Password expires in 60 days.
If I set like below it works for accounts with 3 and less days to expiring. I don't want accounts with 2 days, 1 day, etc.
$ExpiringUsers = $AdUsers | where PasswordLastSet -lt (get-date).AddDays(-57)
If I set like below it doesn't work at all
$ExpiringUsers = $AdUsers | where PasswordLastSet -eq (get-date).AddDays(-57)
How to set equal 3 days not more not less.
Thanks!
You need to define and filter by a time range, for example, set equal 3 days not more not less would be like this:
$start = [datetime].AddDays(-3) # => April 2 at 12:00 AM
$finish = [datetime].AddDays(-2) # => April 3 at 12:00 AM
# Note `-lt $finish` instead of `-le $finish`
$AdUsers | Where-Object { $_.PasswordLastSet -ge $start -and $_.PasswordLastSet -lt $finish }
A better approach is to use msDS-UserPasswordExpiryTimeComputed for checking how long until the account credential expires:
$serviceAccounts =
'acct1#domain.tld',
'acct2#domain.tld
$daysLeft = 20
$expiringAccounts = ( $serviceAccounts | Foreach-Object {
Get-AdUser -Filter "(UserPrincipalName -eq '$_' -Or sAMAccountName -eq '$_' ) -And ( PasswordNeverExpires -eq 'false' )" -Properties msDS-UserPasswordExpiryTimeComputed |
Where-Object { ( New-TimeSpan -Start $currentDate -End ( [DateTime]::FromFileTime( $_.'msDS-UserPasswordExpiryTimeComputed' ) ) ).Days -le $daysLeft }
} )
As written, this code will gather the accounts expiring within 20 days. Adjust $daysLeft to control the remaining threshold until expiry.
Note that [DateTime]::FromFileTime is required to transpose the value ofmsDS-UserPasswordExpiryTimeComputed from the file-time format it is stored in AD as to a workable DateTime object.
You can define the account as a sAMAccountName or in Universal Principal Name (UPN) format.
This also exemplifies using AD filters in order to have AD return only the objects you need for this query, minimizing the local processing performed with Where-Object.
The notable exception is msDS-UserPasswordExpiryTimeComputed; because it is a Constructed Attribute within Active Directory, neither AD filters or LDAP filters can evaluate it and so this must be done via additional local processing.
Here is an answer I have also written which goes into deeper detail on using the AD filter syntax.
What is wrong with PasswordLastSet for checking credential expiration if you know the account's lifecycle?
The problem with checking PasswordLastSet is that, while it works, if the threshold until expiry changes on the Active Directory side, your script will need to be updated or it will incorrectly identify accounts whose credential is set to expire soon. It also becomes more difficult to track which accounts are beholden to different lifecycles in environments where not all accounts are beholden to the same security policies.
I need to get the last logon time for each user profile on a remote computer (not the local accounts). I've tried something similar to the following using Win32_UserProfile & LastLogonTime, however this is not giving accurate results. For example, one this computer, only 1 account has been used in the past year, however LastUpdateTime is showing very recent dates. Some accounts have not even been logged into and should say "N/A", but it doesn't.
$RemoteSB_UserADID = Get-WmiObject win32_userprofile -Property * | Where-Object {$_.LocalPath -like "*users*"} | Sort-Object $_.LastUseTime | ForEach-Object{
$Parts = $_.LocalPath.Split("\")
$ADID = $Parts[$Parts.Length - 1]
if ($ADID -ne "SPECIALPURPOSEACCOUNT1" -and $ADID -ne "SPECIALPURPOSEACCOUNT2"){
$Time = $null
try{
$Time = $_.ConvertToDateTime($_.LastUseTime)
}catch{
$Time = "N/A"
}
"[$ADID | $Time]"
}
}
Example Output
[Acct1 | 03/13/2022 07:18:19]
[Acct2 | 03/15/202214:59:16]
[Acct3 | 03/13/2022 07:18:19]
[Acct4 | 03/16/2022 11:53:17] <--- only "active" account
How can I go about retrieving accurate (or decently accurate) login times for each user profile? Thanks!
It would help to know for what reason you need that, so that I know how to find a (better) solution for you.
If you need to cleanup your profiles not used for a long time at the target system, then take the last changed date of "ntuser.dat". That is the last logon if you define logon like logging on to a new session. If the user was logged on and simply locked the computer or used standby and then relogs then this date won't change.
Use this to get this date from all users you have access to but possibly not getting real user names
Get-ChildItem \\REMOTECOMPUTERNAMEHERE\Users\*\ntuser.dat -Attributes Hidden,Archive | Select #{Name="NameByFolder";Expression={($_.DirectoryName -split "\\")[-1]}},LastWriteTime
Or this a bit more complex version
Invoke-Command -ComputerName REMOTECOMPUTERNAMEHERE -ScriptBlock {$UsersWithProfilePath = #{}
dir "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" |
where {$_.name -like "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\S-1-5-21-*"} |
foreach {$UsersWithProfilePath[([System.Security.Principal.SecurityIdentifier]$_.name.split("\")[-1]).Translate( [System.Security.Principal.NTAccount]).Value] = $_.GetValue("ProfileImagePath")}
foreach ($Name in $UsersWithProfilePath.Keys) {#{$Name =(dir (join-path $UsersWithProfilePath.$Name ntuser.dat) -Attributes Hidden,Archive,System).LastWriteTime}}}
Depending on what you need you need to change it a bit.
Sorry for the long codelines... it is late here.
(Sorry if my English is bad, I'm a French guy).
I'm trying to automate some tasks on my AD and i was looking for a powershell commands which could list the users who didn't connect the last three months.
I tried using the lastlogontimestamp attribute but i can't make it work.
Here is what my command actually look like :
$date = new-object System.DateTime -ArgumentList #(2015,8,1,0,0,0)
Get-ADUser -Filter { -not (LastLogonTimeStamp -le "$date") }
Any ideas ?
The lastLogonTimestamp attribute is stored as a 64-bit integer representing a FILETIME timestamp.
You can convert your DateTime object to this format with the ToFileTime() method.
I would suggest using the Get-Date cmdlet rather than New-Object System.DateTime
# Current time -3 months:
$Threshold = (Get-Date).AddMonths(-3).ToFileTime()
# Retrieve the users
Get-ADUser -LDAPFilter "(&(lastlogontimestamp < $Threshold))"
I'm creating a script that tells me the creation / modification date and other pieces of info of AD objects to determine upgrade status of the machines in large domains. I have no problem accomplishing this in a well formatted and easy to read manner in Server 2008 because it has Active Directory modules, but this isn't the case with Server 2003.
With server 2003 I had to use a different approach to the script to gather the information I want, but I am unsure how to format this.
This is my current script:
$filePath = “$ENV:UserProfile\Desktop\output.txt”
## Enter the name of the Domain controller below
$DCName = “Exchange”
$computers = dsquery computer domainroot -name * -limit 0
Foreach ($computer in $computers) {
repadmin /showattr $DCName $computer /atts:"WhenChanged,WhenCreated,OperatingSystem" /long | out-file –append $filepath
}
This is the sample output:
DN: CN=Sample-Object,CN=Computers,DC=Contoso,DC=com
1> whenCreated: 07/04/2011 14:00:02 Pacific Standard Time Pacific Daylight Time
1> whenChanged: 08/09/2012 11:24:22 Pacific Standard Time Pacific Daylight Time
1> operatingSystem: Windows 7 Professional
In server 2008 I'm able to use string formatting ('"{0}","{1}"' -F $computer.name, $computer.whatever) amd output it to a .csv to make it presentable but I don't think the same methods will apply to the results of repadmin.
My end goal would to simply have a CSV with Computer Name, along with the three or however many attributes I have extracted from repadmin.
Any help appreciated, thank you.
Give this a try, you can export it to CSV and import it back as objects:
$result = dsquery computer domainroot -name * -limit 0 | foreach {
repadmin /showattr $DCName $_ /atts:"WhenChanged,WhenCreated,OperatingSystem" /long
} | Out-String
$result.split([string[]]'DN:',[StringSplitOptions]::RemoveEmptyEntries) | Foreach-Object{
$attr = $_.Split("`r`n",[StringSplitOptions]::RemoveEmptyEntries)
New-Object -TypeName PSObject -Property #{
DN = $attr[0].Trim()
whenCreated = $attr[1].Trim() -replace '^1> whenCreated: '
whenChanged = $attr[2].Trim() -replace '^1> whenChanged: '
operatingSystem = $attr[3].Trim() -replace '^1> operatingSystem: '
}
}