Import-Csv add a column then Export-Csv - powershell

I am often given a CSV and asked to add information to it. For a simplified example I may be handed the following CSV and asked to get the lastlogontimestamp for each user
In order to achieve this I did the following
$csv = import-csv users.csv
$report = "User Audit_{0:dd-MM-yyyy_HHmm}.csv" -f (Get-Date)
$csv | ForEach-Object {
$user = ""
$user = get-aduser $_.user -Properties lastlogontimestamp | Select-Object #{ N = 'LastLogonTimestamp'; E = { [DateTime]::FromFileTime($_.LastLogonTimestamp) } }
[pscustomobject]#{
User = $_.User
'First Name' = $_.'First Name'
'Last Name' = $_.'Last Name'
'LastLogonTimestamp (AD)' = $user.lastlogontimestamp
} | Export-Csv $report -NoTypeInformation -Append
}
This works very well but creating it can be time consuming for spreadsheets with lots of columns and is subject to human error during the pscustomobject construction. So my question is - rather than manually constructing the pscustomobject is there a way to code it (using the column headings to populate each key and value), after which I can append my new additions?

You can use Select-Object with * to copy all existing columns and use calculated properties to add new ones:
$report = "User Audit_{0:dd-MM-yyyy_HHmm}.csv" -f (Get-Date)
import-csv users.csv | ForEach-Object {
$user = get-aduser $_.user -Properties lastlogontimestamp
$_ | Select-Object *,
#{ N = 'LastLogonTimestamp (AD)'; E = { [DateTime]::FromFileTime($user.LastLogonTimestamp) } }
} | Export-Csv $report -NoTypeInformation
Also, as suggested by Mathias R. Jessen, move Export-Csv to the end of the pipeline and remove -Append to improve performance. Otherwise the file would be opened and closed for each row, which is very slow.
I also took the liberty to slightly refactor the code to remove the first Select-Object statement, which is no longer needed. Also you can pipe Import-Csv directly into ForEach-Object, without using a temporary variable.

Part of the reason why it's time-consuming is that you're appending your results for each user. You could reduce how long it takes by simply assigning all your results to a variable and then exporting those results to a CSV at the end like this:
$csv = import-csv users.csv
$report = "User Audit_{0:dd-MM-yyyy_HHmm}.csv" -f (Get-Date)
$results = $csv | ForEach-Object {
$user = ""
$user = get-aduser $_.user -Properties lastlogontimestamp | Select-Object #{ N = 'LastLogonTimestamp'; E = { [DateTime]::FromFileTime($_.LastLogonTimestamp) } }
[pscustomobject]#{
User = $_.User
'First Name' = $_.'First Name'
'Last Name' = $_.'Last Name'
'LastLogonTimestamp (AD)' = $user.lastlogontimestamp
}
}
$results | Export-Csv $report -NoTypeInformation
You could also ignore the variable all together and pipe your results directly to Export-Csv
$csv = import-csv users.csv
$report = "User Audit_{0:dd-MM-yyyy_HHmm}.csv" -f (Get-Date)
$csv | ForEach-Object {
$user = ""
$user = get-aduser $_.user -Properties lastlogontimestamp | Select-Object #{ N = 'LastLogonTimestamp'; E = { [DateTime]::FromFileTime($_.LastLogonTimestamp) } }
[pscustomobject]#{
User = $_.User
'First Name' = $_.'First Name'
'Last Name' = $_.'Last Name'
'LastLogonTimestamp (AD)' = $user.lastlogontimestamp
}
} | Export-Csv $report -NoTypeInformation
In terms of human error. I'm not sure what exactly is "prone to human error" in your code so I can't make a recommendation there.
UPDATE - This should get you what you're looking for
$csv | ForEach-Object {
Get-AdUser $_.user -Properties lastlogontimestamp | Select-Object UserPrincipalName, SamAccountName, GivenName, SurName, #{ N = 'LastLogonTimestamp'; E = { [DateTime]::FromFileTime($_.LastLogonTimestamp) } }
} | Export-Csv $report -NoTypeInformation

Related

Display UserName only once in csv powershell

I am having the below code which is giving me the list of ad groups a specific user is part of
Get-Content "C:\Automation Scripts\users.txt" | Get-ADUser | ForEach{
$user = $_
$groups = Get-ADPrincipalGroupMembership $user
$groups | %{ New-Object PSObject -Property #{ User = $user.SamAccountName; Group = $_.SamAccountName } }
} | Export-Csv "C:\Automation Scripts\User_Groups.csv" -NoTypeInformation
It is working fine and I am getting the result, but the result is coming like below
"User","Group"
"A92M7202822","Domain Users"
"A92M7202822","A92GS-505-Central Team Data"
"A92M7202822","A00GRP-AdminAccounts"
"A92A7803642","Protected Users"
"A92A7803642","A00GRP-AdminAccounts"
I need the result in csv like below
User,Group
A92M7202822,"Domain Users
A92GS-505-Central Team Data
A00GRP-AdminAccounts
A92GS-505-Ids-Analytics-Share-A92DFS
A92GS-505-Data-DSICF-DEV
A92GS-505-Data-DSICF-PRD
CFRGRP-FS-FR4000_SSgt_CFIDS_RW"
A92A7803642,"Domain Users
Protected Users
A00GRP-AdminAccounts"
One cell for user and next cell should have all the groups in it.
Please let me know what changes need to be done for this
Try not to use New-Object, instead you can use PSCustomobject. Also, in your script you have used $user.samaccountname as values for both User and Groups. I have replaced group name value with name you may modify it.
$output = #()
$output += Get-Content "C:\Automation Scripts\users.txt" | Get-ADUser | ForEach{
$user = $_
$groups = Get-ADPrincipalGroupMembership $user
[PSCustomObject]#{
'User'= $user.SamAccountName
'Group' = $groups.Name
}
}
$output | Export-Csv "C:\Automation Scripts\User_Groups.csv" -NoTypeInformation
And another efficient way of doing this is like:
$users = Get-Content "C:\Automation Scripts\users.txt"
$output = foreach ($user in $users)
{
$groups = Get-ADPrincipalGroupMembership $user
foreach($group in $groups)
{
[PSCustomObject]#{
User = $user
Group = $group.Name
}
}
}
$output | Export-Csv "C:\Automation Scripts\User_Groups.csv" -NoTypeInformation

Powershell to create list of duplicated proxyaddresses as .CSV file?

I need some way to report which users in our AD are having duplicated ProxyAddresses or aliases.
Get-ADUser -filter * -properties proxyaddresses |
Select-Object Name,
#{ L = "proxyAddresses"; E = { ($_.ProxyAddresses -like 'smtp:*') -join ";" } } |
export-csv -Path C:\proxyaddresses.csv -NoTypeInformation
I need only the duplicated AD user, not the whole lot, how can I get that report to . CSV file?
You need to wait before concatening your proxy addresses until you are done working with them.
You can get the duplicates by comparing the count of proxy addresses with the count of unique proxy addresses (Select-Object -Unique). If the count mismatch, then you have some dupe in there. If it is the same, then no duplicates.
Here is an example:
$Users = Get-ADUser -filter * -properties proxyaddresses |
Select-Object Name,
#{ L = "proxyAddresses"; E = { $_.ProxyAddresses -like 'smtp:*' } }
$Dupes = $Users | Where-Object -FilterScript { $_.proxyaddresses.Count -ne ($_.ProxyAddresses | Select-Object -Unique).Count }
$Dupes | Select Name, #{'Name' = 'ProxyAddresses' ; 'Expression' = { $_.proxyAddresses -join ';' } } | export-csv -Path C:\proxyaddresses.csv -NoTypeInformation
Reference dataset used
$Users = #(
[PSCustomObject]#{Name = 'Value'; proxyaddresses = #('SMTP:a#a.com', 'SMTP:a#a.com' ) }
[PSCustomObject]#{Name = 'Bob Value'; proxyaddresses = #('SMTP:a#a.com', 'b#a.com') }
)
Not sure if you want:
Users that have a duplicated address in their proxy list (see answer #SagePourpre), or
All users that have the same proxy addresses in their list as another user (this answer)
Create an index (hashtable) where each proxy address refers to a list of users that own that specific proxy address:
$ADUserByProxy = #{}
Get-ADUser -filter * -properties proxyaddresses |
ForEach-Object {
ForEach ($Proxy in $_.ProxyAddresses) {
if (!$ADUserByProxy.Contains($Proxy)) {
$ADUserByProxy[$Proxy] = [Collections.Generic.List[Object]]::new()
}
$ADUserByProxy[$Proxy].Add($_)
}
}
Than list all the values that contain more then 1 user:
$ADUserByProxy.GetEnumerator() |
Where-Object { $_.Value.Count -gt 1 } |
ForEach-Object { $_.Value } |
Export-csv -Path C:\proxyaddresses.csv -NoTypeInformation
Perhaps not the fastest method, but here's an alternative:
Get-ADUser -Filter * -Properties proxyaddresses | Foreach-Object {
$unique = $_.ProxyAddresses | Select-Object -Unique
$dupes = Compare-object -ReferenceObject $unique -DifferenceObject $_.ProxyAddresses -PassThru
if (#($dupes).Count) {
$_ | Select-Object Name, #{Name = 'DuplicateAddresses'; Expression = {$dupes -join ';'}}
}
} | Export-Csv -Path 'C:\proxyaddresses.csv' -NoTypeInformation

Find users with a certain parameter, display the groups they belong to and count how many

I am trying to get a number of how many people with specific titles are in specific groups.
What my approach is:
I am looking for users with specific titles.
I am looping over those users and looking for their groups they are in
Then I am looping over each group and trying to add .csv entry when there is a new one for that specific title, if group is listed, I am trying to just increment the counter.
I think that my approach is slow - every time I export and import .csv file, but I am sure there is a way to work on a imported file.
Also I have strange error: when importing test.csv I have like 10 entries instead of one. How to fix that?
My code:
clear
$Roles = Get-Content 'C:\Users\DWodzinski-admin\Documents\Titles.txt'
$Users = #()
Foreach ($Item in $Roles){
$Users += Get-ADUser -Filter {title -like $Item} -properties title, SamAccountName | Select SamAccountName, title
}
Foreach ($User in $Users){
$AdGroups = Get-ADPrincipalGroupMembership $User.SamAccountName | Select Name
foreach ($thing in $AdGroups) {
$name = $thing.name
$csv = Import-Csv "C:\Users\DWodzinski-admin\Documents\test.csv"
foreach($i in $csv){
if($i.Group -eq $name -and $i.Title -eq $User.title) {
$i.Count += 1
Export-CSV "C:\Users\DWodzinski-admin\Documents\test.csv" -NoTypeInformation
} else {
$NewCsvEntry = #{
Title = $User.title
Group = $name
Count = 0
}
[PSCustomObject]$NewCsvEntry | Export-CSV "C:\Users\DWodzinski-admin\Documents\test.csv" -NoTypeInformation -Append
}
$i
}
}
$csv | Export-CSV "C:\Users\DWodzinski-admin\Documents\test.csv" -NoTypeInformation -Append
}
i changed it so that it only imports your csv once, at the start, and exports (overwrites) it at the end. maybe it also fixes your issue with the 10 entries, try it out.
clear
$csv = Import-Csv "C:\Users\DWodzinski-admin\Documents\test.csv"
$Roles = Get-Content 'C:\Users\DWodzinski-admin\Documents\Titles.txt'
$Users = #()
Foreach ($Item in $Roles) {
$Users += Get-ADUser -Filter { title -like $Item } -properties title, SamAccountName | Select SamAccountName, title
}
Foreach ($User in $Users) {
$AdGroups = Get-ADPrincipalGroupMembership $User.SamAccountName | Select Name
foreach ($thing in $AdGroups) {
$name = $thing.name
foreach ($i in $csv) {
if ($i.Group -eq $name -and $i.Title -eq $User.title) {
$i.Count += 1
}
else {
$newCsvEntry = [PSCustomObject]#{
Title = $User.title
Group = $name
Count = 0
}
$csv += $newCsvEntry
}
$i
}
}
}
$csv | Export-CSV "C:\Users\DWodzinski-admin\Documents\test.csv" -NoTypeInformation -Force
If anyone want to know how I did it, I created ArrayList with CustomObjects:
clear
$Roles = Get-Content 'C:\Users\DWodzinski-admin\Documents\Titles.txt'
$Users = #()
$List = New-Object System.Collections.ArrayList
$Object = [PSCustomObject]#{
Name = 'Pierwszy';
Title = 'Czarny';
Count = 0;
}
$List.add($Object)
Foreach ($Item in $Roles){
$Users += Get-ADUser -Filter {title -like $Item} -properties title, SamAccountName | Select SamAccountName, title
}
Foreach ($User in $Users){
$AdGroups = Get-ADPrincipalGroupMembership $User.SamAccountName | Select Name
foreach($Group in $AdGroups){
if($List | Where Name -eq $Group.name | Where Title -eq $User.title){
$temp = $List | Where Name -eq $Group.name | Where Title -eq $User.title
$temp.Count += 1
} else {
$Object = [PSCustomObject]#{
Name = $Group.Name;
Title = $User.title;
Count = 1;
}
$List.add($Object)
}
}
}
$FilePathLocation = "C:\Users\DWodzinski-admin\Documents\test.csv"
$List | Export-Csv -Path $FilePathLocation -NoTypeInformation

SamAccountName from csv with employeeid

I have this working script:
Import-CSV -Path "$home\desktop\Script\fi13.04.20.csv" | ForEach-Object {
Get-ADUser -Filter "employeeid -like '*$($_.employeeid)*'" -Properties employeeid | select employeeid,name,SamAccountName
} | Export-CSV "$home\desktop\Script\fi-san-out.csv" -Encoding UTF8 -NoTypeInformation
I give to him a CSV file with employeeid and have back file with SamAccountName...
Я даю ему файл с employeeid и получаю обратно файл with certain parameters.
And this is an improved version-
$csv = Import-CSV -Path "$home\desktop\Script\fi13.04.20.csv"
$csvOutput = "$home\desktop\Script\fi-san-out.csv"
$object = #()
foreach($employee in $csv){
$ADUserObject = Get-ADUser -Filter "employeeID -eq '$($employee.employeeID)'" -Properties samAccountName, displayName ,employeeID -ErrorAction SilentlyContinue
if($ADUserObject){
$object += New-Object psobject -Property #{
displayName = $ADUserObject.displayName
samAccountName = $ADUserObject.SamAccountName
employeeID = $ADUserObject.employeeID
}
}else{
Write-Host "No employee found for $($employee.employeeId)" -ForegroundColor Red
}
}
if($object){
$object | Select-Object displayName, samAccountName, employeeID | Export-Csv -Path $csvOutput -Delimiter "," -Encoding UTF8 -NoTypeInformation
}
The first version produces much more results because if the employeeID contains an error "for example, the employeeID in CSV 12345 and employeeID in ActiveDirectory 123456", it gives the result.
In the improved version, it throws an error with any inaccurate ID.
The question is how to change the improved version so that it produces a result even with an error(like in the example)?
I am just starting to learn PS and I wrote an improved version with the help.
Result
$csv = Import-CSV -Path "$home\desktop\Script\ID.csv"
$csvOutput = "$home\desktop\Script\tz-san-out.csv"
$object = #()
foreach($employee in $csv){
$ADUserObject = Get-ADUser -Filter "employeeID -like '*$($employee.employeeID)*'" -Properties samAccountName, displayName ,employeeID -ErrorAction SilentlyContinue
if($ADUserObject){
$object += New-Object psobject -Property #{
displayName = $ADUserObject.displayName
samAccountName = $ADUserObject.SamAccountName
employeeID = $ADUserObject.employeeID
}
}else{
Write-Host "No employee found for $($employee.employeeId)" -ForegroundColor Red
}
}
if($object){
$object | Select-Object displayName, samAccountName, employeeID | Export-Csv -Path $csvOutput -Delimiter "," -Encoding UTF8 -NoTypeInformation
}

export object using Export-Csv doesn't work

I'm trying to export the username and the user's group membership (of specifc groups) to a CSV file using Export-Csv. However, I couldn't accomplish this using several approaches.
My current script works fine but the output is shown on the PowerShell console alone:
$accounts = Get-Content "C:\Scripts\getUserGroups\users.txt"
foreach ($account in $accounts) {
"$account member of:"
Get-ADPrincipalGroupMembership -Identity $account |
select Name |
Where-Object { $_.name -like 'Browsing_Group*' } |
Sort Name
}
I want to export it to a file in an ordered manner:
UserName1
group membership
UserName2
group membership
etc...
I've tried to add to a variable but probably didn't do that correctly:
$ArrList = [System.Collections.ArrayList]#()
$accounts = Get-Content "C:\Scripts\getUserGroups\users.txt"
foreach ($account in $accounts) {
$ArrList.Add($account)
$groups = Get-ADPrincipalGroupMembership -Identity $account |
select Name |
Where-Object {$_.name -like 'Browsing_group*' } |
Sort Name
$ArrList.Add($grops)
}
Might be a different approach.
You need to build custom objects in order to export the data to a CSV via Export-Csv. The 2 main ways of doing that are:
using calculated properties:
$accounts |
Select-Object #{n='Username';e={$_}}, #{n='Groups';e={
(Get-ADPrincipalGroupMembership -Identity $_ |
Select-Object -Expand Name |
Where-Object {$_ -like 'Browsing_group*' } |
Sort-Object) -join ';'
}} |
Export-Csv 'C:\path\to\output.csv' -NoType
building custom objects directly:
$accounts | ForEach-Object {
$groups = Get-ADPrincipalGroupMembership -Identity $_ |
Select-Object -Expand Name |
Where-Object {$_ -like 'Browsing_group*' } |
Sort-Object
New-Object -Type PSObject -Property #{
'Username' = $_
'Groups' = $groups -join ';'
}
} | Export-Csv 'C:\path\to\output.csv' -NoType
With PowerShell version 3 or newer you can replace New-Object with the [PSCustomObject] type accelerator:
[PSCustomObject]#{
'Username' = $_
'Groups' = $groups -join ';'
}