Reputation: 1098
I have a Powershell script that queries for the pwdLastSet attribute for every user in
the Active Directory domain. Essentially, the script determines when each user in the domain last changed their password. However, when I try and output the result using scriptname.ps1 | Export-Csv "filename.csv"
it creates the file, however, I'm not getting the results I see in the console. I'm getting the following:
When I run the script without Export-Csv
the results I desire display correctly.
This is the Powershell script:
Trap {"Error: $_"; Break;}
$D = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$Domain = [ADSI]"LDAP://$D"
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.PageSize = 200
$Searcher.SearchScope = "subtree"
$Searcher.Filter = "(&(objectCategory=person)(objectClass=user))"
$Searcher.PropertiesToLoad.Add("distinguishedName") > $Null
$Searcher.PropertiesToLoad.Add("pwdLastSet") > $Null
$Searcher.PropertiesToLoad.Add("userAccountControl") > $Null
$Searcher.SearchRoot = "LDAP://" + $Domain.distinguishedName
$Results = $Searcher.FindAll()
ForEach ($Result In $Results)
{
$DN = $Result.Properties.Item("distinguishedName")
$PLS = $Result.Properties.Item("pwdLastSet")
$UAC = $Result.Properties.Item("userAccountControl")
# Retrieve user password settings to check if password can expire.
$blnPwdExpires = -not (($UAC.Item(0) -band 64) -or ($UAC.Item(0) -band 65536))
If ($PLS.Count -eq 0)
{
$Date = [DateTime]0
}
Else
{
# Interpret 64-bit integer as a date.
$Date = [DateTime]$PLS.Item(0)
}
If ($Date -eq 0)
{
# 0 really means never.
$PwdLastSet = "<Never>"
}
Else
{
# Convert from .NET ticks to Active Directory Integer8 ticks.
# Also, convert from UTC to local time.
$PwdLastSet = $Date.AddYears(1600).ToLocalTime()
}
"$DN;$blnPwdExpires;$PwdLastSet"
}
Upvotes: 1
Views: 1299
Reputation: 60518
There are two possible issues on your code, the first one, Export-Csv
is expecting an object
or object[]
as input and will convert it to CSV
format, you're already passing a formatted semi-colon delimited string[]
.
In this case you should use | Out-File path\to\csv.csv
instead of Export-Csv
.
Do not format objects before sending them to the
Export-CSV
cmdlet. IfExport-CSV
receives formatted objects the CSV file contains the format properties rather than the object properties.
An example of what you're passing to the cmdlet and what it actually expects:
PS \> 0..5 | ForEach-Object{ 'asd;asd;asd' } | ConvertTo-Csv
#TYPE System.String
"Length"
"12"
"12"
"12"
"12"
"12"
"12"
PS \> 0..5 | ForEach-Object{ [pscustomobject]@{col1='asd';col2='asd';col3='asd'} } | ConvertTo-Csv -Delimiter ';'
#TYPE System.Management.Automation.PSCustomObject
"col1";"col2";"col3"
"asd";"asd";"asd"
"asd";"asd";"asd"
"asd";"asd";"asd"
"asd";"asd";"asd"
"asd";"asd";"asd"
"asd";"asd";"asd"
The alternative to this, and cleaner approach in my opinion, would be to cast a [pscustomobject]
on each iteration of your loop and then pass the resulting array to Export-Csv
(code below).
The other possible issue, assuming you're choosing the path of using [pscustomobject]
, could be that $Result.Properties.Item(...)
will yield an object of the type System.DirectoryServices.ResultPropertyValueCollection
and you would need to convert it to [string]
before passing the results to Export-Csv
(also code below).
# Save the resulting pscustomobject array to the $output variable
$output = ForEach ($Result In $Results)
{
...
...
...
# All code should be as is up until:
# "$DN;$blnPwdExpires;$PwdLastSet" => Remove this line
[pscustomobject]@{
DistinguishedName = [string]$DN
blnPwdExpires = [string]$blnPwdExpires
pwdLastSet = [string]$PwdLastSet
}
}
# Then pipe the result to Export-Csv
$output | Export-Csv path\to\csv.csv -NoTypeInformation -Delimiter ';'
Upvotes: 1