I'm trying to generate a report that lists the password expiration dates for all our users, but I've hit a snag with the PowerShell script my coworker started. We have both Windows 10 and Windows 11 users, and they follow different password policies. I need to pull this data using Active Directory. Here's the code I'm working with:
```powershell
# Define the domain you want to query
$Domain = "mycompany.com" # Replace with your domain name or domain controller FQDN
# Define LDAP filter
$Filter = "(&(objectCategory=person)(objectClass=user)(employeeID=*)(!(userAccountControl:1.2.840.113556.1.4.803:=65536)))"
# Array to hold employees
$Employees = @()
Write-Host "Getting all employees from $Domain"
try {
# Pull users from the specified domain
$Employees += Get-ADUser -LDAPFilter $Filter -Properties pwdLastSet, mail -Server $Domain | Select-Object -Property *,
@{N = 'Domain'; E = { $Domain } },
@{N = 'PasswordLastSet'; E = { [DateTime]::FromFileTimeUtc($_.pwdLastSet) } },
@{N = 'DaysTilExpiry'; E = {
$Policy = Get-ADUserResultantPasswordPolicy -Identity $_.UserPrincipalName
if ( $null -eq $Policy ) {
89 - ((Get-date) - (Get-Date -Date ([DateTime]::FromFileTimeUtc($_.pwdLastSet)))).Days
} else {
($Policy.MaxPasswordAge.TotalDays - 1) - ((Get-date) - (Get-Date -Date ([DateTime]::FromFileTimeUtc($_.pwdLastSet)))).Days
}
}}
# THIS IS WHERE WE ARE STUCK - HOW DO WE GET THE PROPERTIES LISTED BELOW?
# Create custom object
$EmployeeObj = [PSCustomObject]@{
UserPrincipalName = $Employee.UserPrincipalName
Mail = $Employee.mail
Domain = $Domain
PasswordLastSet = $PwdLastSetDate
DaysTilExpiry = $DaysTilExpiry
}
# Add to array
$Employees += $EmployeeObj
}
catch {
Write-Warning "Failed to get users from $Domain"
}
# Export to CSV
$Employees | Export-Csv -Path "some path.csv" -NoTypeInformation
Write-Host "Report exported to some pathPasswordExpiryReport.csv"
```
Any advice on how to resolve this would be greatly appreciated!
4 Answers
You're already using Select-Object, which generates a custom object. The extra object creation is unnecessary since you already have your data! Check out this revised version of your script that should work:
```powershell
#Requires -Modules ActiveDirectory
$Domain = "mycompany.com"
$Filter = "(&(objectCategory=person)(objectClass=user)(employeeID=*)(!(userAccountControl:1.2.840.113556.1.4.803:=65536)))"
Write-Host "Getting all employees from $Domain"
if (-not (Test-Path "C:Temp")) {
New-Item -Path "C:Temp" -ItemType Directory -Force | Out-Null
}
try {
$DefaultPolicy = Get-ADDefaultDomainPasswordPolicy -Server $Domain
$Employees = Get-ADUser -LDAPFilter $Filter -Properties pwdLastSet, mail -Server $Domain | ForEach-Object {
$Policy = Get-ADUserResultantPasswordPolicy -Identity $_.DistinguishedName -Server $Domain -ErrorAction SilentlyContinue
$PwdLastSetDate = [DateTime]::FromFileTimeUtc($_.pwdLastSet)
# Further processing for $MaxAge, $DaysTilExpiry, etc.
[PSCustomObject]@{
UserPrincipalName = $_.UserPrincipalName
Mail = $_.mail
Domain = $Domain
PasswordLastSet = $PwdLastSetDate
DaysTilExpiry = $DaysTilExpiry
}
}
$Employees | Export-Csv -Path "C:TempPasswordExpiryReport.csv" -NoTypeInformation
Write-Host "Report exported successfully to C:TempPasswordExpiryReport.csv"
}
catch {
Write-Warning "Failed to get users from $Domain : $_"
}
```
Try this out tomorrow and let me know how it goes!
You're definitely making this more complicated than it needs to be! You can simplify things significantly with a direct command:
```powershell
Get-ADUser -Filter {Enabled -eq $True -and PasswordNeverExpires -eq $False} -Properties DisplayName, msDS-UserPasswordExpiryTimeComputed | Select-Object -Property DisplayName, samaccountname, @{Name="PasswordExpirationDate";Expression={[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}}
```
This will get you the expiration dates directly without the extra fuss!
I agree! I haven't tested this exact version, but I have something similar that works well and gives accurate results. Simple is usually better!
This approach is spot on!
I’d say maybe consider using a tool like ChatGPT for help, but I've found it has its limitations with PowerShell scripts.
Using the PasswordExpirationDate property directly could streamline your code further!

Thanks for the helpful tips! I'm going to put this to the test.