How to put a header to csv file generated by Powershell script - powershell

I got this Powershell script that queries users that have not changed their password for 24 hours. The query redirects the output to csv file. Below are the Powershell script and batch script:
Powershell script:
$root = [ADSI]''
$searcher = new-object System.DirectoryServices.DirectorySearcher($root)
$searcher.filter = "(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
$searcher.sizelimit = 5000
[Void]$searcher.PropertiesToLoad.Add("cn")
[Void]$searcher.PropertiesToLoad.Add("samAccountName")
[Void]$searcher.PropertiesToLoad.Add("pwdLastSet")
$users = $searcher.findall()
$UserOU = "OU=Mountain,DC=Atlanta,DC=ga"
$PWDays = (Get-Date).AddDays(-1)
$UserCount = 0
$UserPW = 0
foreach($user in $users)
{
if ($user.path -like "*$UserOU")
{
$usercount = $UserCount
if ([datetime]::FromFileTime(($user.properties.pwdlastset)[0]) -le $PWDays)
{
$UserPW = $UserPW + 1
Write-Host $user.Properties.cn
}
}
}
Batch script:
powershell.exe d:\temp\query.ps1 > D:\temp\query.csv
My question is: How do I put change the script to put header for username in the the csv output file?
The header may simple be 'Username' not necessarily Firstname and Lastname.

Any reason why you aren't using Export-Csv? You can just pipe your objects into it and it will include headers. Something along the lines of
$users |
? { $_.Path -like "*$UserOU" } |
? { [datetime]::FromFileTime(($user.properties.pwdlastset)[0]) -le $PWDays } |
% { $_ | Add-Member -PassThru NoteProperty Username $_.Properties.cn } |
select Username |
Export-Csv D:\temp\query.csv
might work. (Hint: The pipeline is more fun than the loop :))

Not sure (never have user PS) but I guess that sticking
Write-Host "Username"
before the foreach, might do the trick

Related

Pass Powershell script to batch file

I'm trying to create a working PowerShell script. However, it doesn't quite work correctly in the function, and we think it's because of the type of format.
Could you help me pass this function to a cmd/batch command?
$datos = quser
foreach($id in $datos)
{
$nom = ""
$userid = $id -split '\s+'
$ad = $userid[0].Replace(">","")
if ($ad -eq $usuario)
{
$nom = $ad
$userid[2]
}
}
It is a function to get only the ID from the quser command utility.
This PowerShell will output the active console session ID from quser.exe. Save to file Get-Qusers.ps1
$pattern = '(?<username>.*?) {2,}(?<sessionname>.*?) {2,}(?<id>.*?) {2,}(?<state>.*?) {2,}(?<idletime>.*?) {2,}(?<logontime>.*?)$'
$quser = quser.exe
($quser | Select-String -Pattern $pattern ).Matches | Select-Object -Skip 1 | ForEach-Object {
[PSCustomObject]#{
Username = $_.Groups['username'].Value.Trim() -replace '>'
Sessionname = $_.Groups['sessionname'].Value
Id = $_.Groups['id'].Value
State = $_.Groups['state'].Value
IdleTime = $_.Groups['idletime'].Value
LogonTime = $_.Groups['logontime'].Value
}
} |
Where-Object { $_.Sessionname -eq 'console' } |
Select-Object -ExpandProperty Id
To run the ps1 script in a batch file and save the Id to a variable
echo off
FOR /F "tokens=*" %%g IN ('powershell.exe -f get-qusers.ps1') do (SET QuserId=%%g)
echo %QuserId%

Trying to extract specific text and merge output with existing output

I want to extract text from a .txt file. The way the file is layed out is in this format (below first block). Optimally, I would like for the powershell script to take the content of username and votecount and output them side by side. With an integer of 25>= add the letter D beside it. With the output adding itself to a pre-existing output file. Say this week is week 1. And testuser voted 25 times. They should have the output "testuser" 25D. But say in week 2 they voted 24 times. Then it should be "testuser" 49D. However say they had 25 again. Output should then be "testuser" 50DD or 50D2?.. I have what I think should work as an initial baseline for the script which in itself doesn't work.. But combining an output with a pre existing output is beyond my capability. This needs to parse an entire txt file of some 100+ people. So imagine there's like an extra 100 users..
{
"username": "testuser",
"votecount": "42",
"votesclaimed": "0",
"lastvotetime": "2022-11-04 09:08:29",
"steamid": "00000000000000000000"
}
Below is what I am working with.
Get-Content -Raw C:\Users\--------\Desktop\votes.txt |
ConvertFrom-txt |
ForEach-Object {
[pscustomobject] #{
UserName = $_.username
VoteCount = '{0}{1}' -f $_.votecount, ('', 'D')[[int] $_.votecount -gt 25]
}
} |
Export-Csv -NoTypeInformation -Encoding utf8 C:\Users\---------\Desktop\outvotes.csv
Try following :
$match = Select-String -Path "c:\temp\test.txt" -Pattern '^\s*"(?<key>[^"]+)"\s*:\s*"(?<value>[^"]+)'
$table = [System.Collections.ArrayList]::new()
foreach( $row in $match.Matches )
{
$key = $row.Groups["key"].Value
$value = $row.Groups["value"].Value
if($key -eq "username") {
$newRow = New-Object -TypeName psobject
$table.Add($newRow) | Out-Null
}
$newRow | Add-Member -NotePropertyName $key -NotePropertyValue $value
}
$table | Format-Table
$groups = $table | Group-Object {$_.username}

Exporting PowerShell Results In To CSV for Each User In The Domain That Last Changed Their Password

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"
}
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. If Export-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 ';'

PowerShell variable definition from a pscustomobject

i've got this piece of code from a script i found on the web (just showing the part that interests me)
ForEach ($Computer in $Computername) {
$adsi = [ADSI]"WinNT://$Computername"
$adsi.Children | where {$_.SchemaClassName -eq 'user'} | ForEach {
[pscustomobject]#{
UserName = $_.Name[0]
SID = ConvertTo-SID -BinarySID $_.ObjectSID[0]
PasswordAge = [math]::Round($_.PasswordAge[0]/86400)
LastLogin = If ($_.LastLogin[0] -is [datetime]){$_.LastLogin[0]}Else{'Never logged on'}
UserFlags = Convert-UserFlag -UserFlag $_.UserFlags[0]
MinPasswordLength = $_.MinPasswordLength[0]
MinPasswordAge = [math]::Round($_.MinPasswordAge[0]/86400)
MaxPasswordAge = [math]::Round($_.MaxPasswordAge[0]/86400)
BadPasswordAttempts = $_.BadPasswordAttempts[0]
MaxBadPasswords = $_.MaxBadPasswordsAllowed[0]
}
}
}
the code displays things on the console, but i would like to define/use these values as variables instead (as i want to use them in a hash table afterwards to send them in a http/POST request afterwards)
is there a way to get all these attributes as variables such as $LastLogin, $MinPasswordAge etc ?
as i don't want to display them, but send them in a POST like this :
$postParams = #{LastLogin=$LastLogin;MinPasswordAge=$MinPasswordAge}
Invoke-WebRequest -Uri http://example.com/foobar -Method POST -Body $postParams
to be honest i'm a complete newbie in PowerShell (i'm a Perl guru) and i don't know what pscustomobject does in there, i just want to define the variables in that loop, and use them at the end.
i've tried a couple of things with no success (can post them if required)
thanks !
Your own solution works, but only if you perform all processing inside the ForEach-Object script block (unless there's only ever 1 iteration, which doesn't appear to be the case here).
If you want to process the results later, you can simply collect them in an array by assigning the entire foreach loop to a variable (code shortened):
$allUsers = foreach ($Computer in $Computername) {
$adsi = [ADSI]"WinNT://$Computername"
$adsi.Children | where {$_.SchemaClassName -eq 'user'} | ForEach {
# Output a custom object for each user.
[pscustomobject]#{
ComputerName = $Computer # also record the computer name
UserName = $_.Name[0]
SID = ConvertTo-SID -BinarySID $_.ObjectSID[0]
# ...
}
}
}
You can then simply enumerate the collected [pscustomobject]s and access their properties rather than using variables:
foreach ($user in $allUsers) {
# Use the properties to define a hashtable for later use in a http/POST request.
$ht = #{
User = $user.UserName
# ...
}
}
nm,
i found the solution a minute ago.
just got rid of that pscustomobject hash completely, and assigning the variables directory
$adsi.Children | where {$_.SchemaClassName -eq 'user'} | ForEach {
$UserName = $_.Name[0]
$SID = ConvertTo-SID -BinarySID $_.ObjectSID[0]
$PasswordAge = [math]::Round($_.PasswordAge[0]/86400)
$LastLogin = If ($_.LastLogin[0] -is [datetime]){$_.LastLogin[0]}Else{'Never logged on'}
$UserFlags = Convert-UserFlag -UserFlag $_.UserFlags[0]
$MinPasswordLength = $_.MinPasswordLength[0]
$MinPasswordAge = [math]::Round($_.MinPasswordAge[0]/86400)
$MaxPasswordAge = [math]::Round($_.MaxPasswordAge[0]/86400)
$BadPasswordAttempts = $_.BadPasswordAttempts[0]
$MaxBadPasswords = $_.MaxBadPasswordsAllowed[0]
Write-Host $UserName
}
}

PowerShell AD script not working

For some reason everything works except the homeDirectory. For that everything is blank..The AD actually has values for these fields but this script isn't displaying anything for that attribute. Any ideas?
$objSearch = New-Object System.DirectoryServices.DirectorySearcher
$objSearch.PageSize = 15000
$objSearch.Filter = $ObjFilter
$objSearch.SearchRoot = "LDAP://$dn"
$AllObj = $objSearch.FindAll()
foreach ($Obj in $AllObj)
{ $objItemS = $Obj.Properties
$Ssamaccountname = $objItemS.samaccountname
$SsamaccountnameGN = $objItemS.givenname
$SsamaccountnameSN = $objItemS.sn
$SsamaccountnameEN = $objItemS.mail
$SsamaccountnameLS = $objItemS.homeDirectory
"$Ssamaccountname`t$SsamaccountnameGN`t$SsamaccountnameSN`t$SsamaccountnameEN`t$SsamaccountnameLS" | Out-File $UserInfoFile -encoding ASCII -append
} # End of foreach
} # End of ForEach-Object
If you are not attached to using the .NET I really like Quest Active Directory cmdlets
Your command would then be:
get-qadUser <UserName or another unique attribute> | Format-List <Attributes> | Out-File MyTextFile.txt
To get a list of possible attributes, you can:
get-qaduser UserName -includeAllAttribute | fl * | Out-File C:\AllAttributes.txt
Can you retry adding this line before the FindAll:
$objSearch.PropertiesToLoad.Add("homeDirectory");
Generaly speaking it's better to add to the search each attribute you want to retreive.