Export Powershell output to CSV - powershell

I have the following code intended to take a list of user names and output a CSV report of username - GroupMembership. At the command line the output looks great as i get "name" on the left and recursive "group memberships" on the right (see pic http://i.stack.imgur.com/zLxUR.jpg for command line output, sorry can't post imbedded Pics yet)
I would like to have the output written to a CSV file with the same format, namely Username in one column and GroupMemberships in the second column.. Original code from: http://thesurlyadmin.com/2013/03/21/get-a-users-group-memberships/ with a few small changes.
Param (
[Parameter(Mandatory=$true,ValueFromPipeLine=$true)]
[Alias("ID","Users")]
[string[]]$User
)
Begin {
Try { Import-Module ActiveDirectory -ErrorAction Stop }
Catch { Write-Host "Unable to load Active Directory module, is RSAT installed?"; Break }
}
Process {
ForEach ($U in $User)
{ $UN = Get-ADUser $U -Properties MemberOf
$Groups = ForEach ($Group in ($UN.MemberOf))
{ (Get-ADGroup $Group).Name
}
$Groups = $Groups | Sort
ForEach ($Group in $Groups)
{ New-Object PSObject -Property #{
Name = $UN.Name
Group = $Group
}
}
}
}
I tried using this "$PSObject | Export-CSV C:\Scripts\GroupMembershipList.csv" but it only writes the first line to the CSV and nothing after that.

Nate,
In Powershell v3.0, the Export-CSV cmdlet introduced the -Append parameter.
Reference: http://technet.microsoft.com/en-us/library/hh849932.aspx
Not knowing the version of Powershell you are using, this may require an update on your side to make use of the new functionality.
In my own cases, I generally see the opposite issue if I forget to -Append to my CSV; I will only end up with the LAST entry as opposed to just the first.
I won't claim this to be your fix, but might be worth a shot...
Example: $PSObject | Export-CSV C:\Scripts\GroupMembershipList.csv -Append

Related

Working with a list of AD 'displayNames' in Powershell. How to indicate which users were not found?

I have written enough PS code to go through a list of displayNames (e.g "John Smith", "Taylor Hanson" - all stored on seperate lines of a txt file) to spit back enough data into another text file that can be used for mailmerge etc. Convincing thousands of employees to simply update Windows is like breaking stones! It has to be automatted to some degree...
Here is the code... the functions that let the user open a specific text file and later save are out of view...
$displayname = #()
$names = get-content $FileIN
foreach ($name in $names) {
$displaynamedetails = Get-ADUser -filter { DisplayName -eq $name } | Select Name, GivenName, Surname, UserPrincipalName
$displayname += $displaynamedetails
}
$displayname | Export-Csv -NoTypeInformation -path $fileOUT -Encoding UTF8
From time to time, a name might be spelled incorrectly in the list, or the employee may have left the organisation.
Is there any way that a statement such as 'Not Found' can be written to the specific line of the text file if an error is ever made (so that an easy side-by-side comparison of the two files can be made?
For most of the other solutions I've tried to find, the answers are based around the samAccoutName or merging the first and last names together. Here, i am specifically interested in displaynames.
Thanks
You can give this a try, since -Filter or -LDAPFilter don't throw any exception whenever an object couldn't be found (unless you're feeding a null value) you can add an if condition to check if the variable where the AD User object is going to be stored is not null and if it is you can add this "not found" user into a different array.
$domain = (Get-ADRootDSE).DefaultNamingContext
$names = Get-Content $FileIN
$refNotFound = [System.Collections.Generic.List[string]]::new()
$displaynamedetails = foreach($name in $names)
{
if($aduser = Get-ADUser -LDAPFilter "(DisplayName=$name)")
{
$aduser
continue
}
$refNotFound.Add(
"Cannot find an object with DisplayName: '$name' under: $domain"
)
}
$displaynamedetails | Select-Object Name, GivenName, Surname, UserPrincipalName |
Export-Csv -NoTypeInformation -path $fileOUT -Encoding UTF8
$refNotFound # => Here are the users that couldn't be found
Side note, consider stop using $displayname = #() and += for well known reasons.
As for AD Cmdlets, using scriptblock based filtering (-Filter {...}) is not supported and even though it can work, it can also bring you problems in the future.

[PowerShell]Get-Content and Add Column Entry?

I am trying to input a list of users into PowerShell and get a specific security group attached to the user's account. At this current time, I have two pieces - an Excel sheet with multiple pieces of data, and a .txt with just the user's usernames. The script I have currently just inputs the user's usernames from the .txt and gets the security group from their account that matches a specific prefix, however I noticed doing it this way doesn't give any specific order. Even though the users are in a specific order (copied and pasted exactly from the excel document), the actual output doesn't come back well.
So, here's what I'd Like to do now, I just don't know how. I would like to get the content from the Excel document, take all of the usernames and do Get-ADPrincipalGroupMembership like I am now, and then write the security group Back to the line that matches the username. For example, if I looked up the SG for msnow, it would get the SG for msnow and then write the SG back to the row that has msnow, and continues through the list. Instead of just doing an Out-GridView, it would actually write this to the Excel document.
Any help on making this work?
Here is the code I have right now.
Import-Module ActiveDirectory
$Names = Get-Content C:\Temp\Users.txt
$Records = #()
Foreach ($ADUsers in $Names) {
Try {
$SG = Get-ADPrincipalGroupMembership -Identity $ADUsers | Select Name | Where {$_.Name -Like "SG - *"}
$SGName = $SG.Name
}
Catch [ADIdentityNotFoundException] {
$SGName = "User not found"
}
$Records += New-Object PSObject -Property #{"UserName" = $ADUsers;"Security Group" = $SGName}
}
Write-Host "Generating CSV File..."
$Records | Out-GridView
Thank you!
If you save the Excel as CSV, so it will look something like
"UserName","Security Group","InsideInfo"
"bloggsj","","tall guy"
"ftastic","","nothing worth mentioning"
things shouldn't be that hard to do.
$out = 'D:\Test\Updated_usersandgroups.csv'
$csv = Import-Csv -Path 'D:\Test\usersandgroups.csv'
Write-Host "Updating CSV File..."
foreach ($user in $csv) {
try {
$SG = Get-ADPrincipalGroupMembership -Identity $user.UserName -ErrorAction Stop
# if more groups are returned, combine them into a delimited string
# I'm using ', ' here, but you can change that to something else of course
$SGName = ($SG | Where-Object {$_.Name -Like "SG - *"}).Name -join ', '
}
catch [ADIdentityNotFoundException] {
$SGName = "User $($user.UserName) not found"
}
catch {
# something else went wrong?
$SGName = $_.Exception.Message
}
# update the 'Security Group' value
$user.'Security Group' = $SGName
}
Write-Host "Generating updated CSV File..."
$csv | Export-Csv -Path $out -UseCulture -NoTypeInformation
# show output on screen
$csv | Format-Table -AutoSize # or -Wrap if there is a lot of data
# show as GridView (sorts by column)
$csv | Out-GridView
Output in console would then look like
UserName Security Group InsideInfo
-------- -------------- ----------
bloggsj SG - Group1, SG - Group1 tall guy
ftastic SG - Group1 nothing worth mentioning
Note: I don't know what delimiter your Excel uses when saving to CSV file. On my Dutch machine, it uses the semi-colon ;, so if in your case this is not a comma, add the delimiter character as parameter to the Import-Csv cmdlet: -Delimiter ';'
Excel uses whatever is set in your locale as ListSeparator for the delimiter character. In PowerShell you can see what that is by doing (Get-Culture).TextInfo.ListSeparator. On output, the -UseCulture switch will make sure it uses that delimiter so Excel will understand

Adding a ROW for missing Attribute values to Export-CSV

I using the following POWER SHELL script, to extract ( to csv ) managers name , from a "Manager" user attribute.
#This script, , Exports the Manager name of the employee`s in the TXT file.
# users.txt file - contains a simply list of user names ( samaccount-names )
Get-Content D:\powershell\permmisions\Users.txt | Foreach-Object {
Get-ADUser -Identity $_ -Properties Manager | Select-Object name, Manager | Export-Csv D:\Powershell\ADuserinformation\Export-Managers-of-specific-users.csv
-Append
}
The challenge i am facing, is when is on the exported CSV file,
the list "SKIPS" blank value-fields,In case there is no manager set for the user.
And a ROWS is not created , where MANAGER is missing.
What i would like to do , is the script to enter a charcter ( ~ ) for example, where, value is blank.
That way , a row will be created for the blank MANAGER value, on the CSV file
Please help ,
Thanks all in advance.
Note: At least the Name property should exist on all AD users retrieved, so you would get a row even for users where Manager is empty, but with an empty Manager column. If you do need to deal with possibly not all users named in Users.txt actually existing, see Theo's helpful answer.
The simplest approach is to use a calculated property:
Get-ADUser -Identity $_ -Properties Manager |
Select-Object Name, #{ Name='Manager';
Expression={ if ($_.Manager) { $_.Manager } else { '~' } } }
Note:
It is common to abbreviate the key names of the hashtable that defines the calculated property to n and e.
The if statement takes advantage of the fact that an empty string (or $null) evaluates to $false in a Boolean context; for an overview of PowerShell's implicit to-Boolean conversion, see the bottom section of this answer.
In PowerShell [Core] 7.0 or above, you could additionally take advantage of the ternary operator (<condition> ? <valueIfTrue> : <valueIfFalse>) to further shorten the command:
# PSv7+
Get-ADUser -Identity $_ -Properties Manager |
Select-Object Name, #{ n='Manager'; e={ $_.Manager ? $_.Manager : '~' } }
Note: If $_.Manager were to return $null rather than the empty string ('') if no manager is assigned, you could use ??, the PSv7+ null-coalescing operator instead: $_.Manager ?? '~'
Not concise at all, but this allows you to insert more properties of interest in your report, and does some error-checking if the user listed in your input file does not exist:
$report = foreach ($account in (Get-Content D:\powershell\permmisions\Users.txt)) {
$user = Get-ADUser -Filter "SamAccountName -eq '$account'" -Properties Manager -ErrorAction SilentlyContinue
if ($user) {
if (!$user.Manager) { $mgr = '~' }
else {
# the Manager property is the DistinghuishedName for the manager.
# if you want that in your report, just do
$mgr = $user.Manager
# if you want the Name for instance of that manager in your report,
# comment out the above line and do this instead:
# $mgr = (Get-ADUser -Identity $user.Manager).Name
}
# now output an object
[PsCustomObject]#{
UserName = $user.Name
Manager = $mgr
}
}
else {
Write-Warning "User '$account' does not exist"
}
}
# output on screen
$report | Format-Table -AutoSize
# output to CSV file
$report | Export-Csv -Path 'D:\Powershell\ADuserinformation\Export-Managers-of-specific-users.csv' -NoTypeInformation

Mix found values and error msg in csv - Get-ADUser search

I would like to take a csv of e-mail addresses and find users that match those addresses. Output should be either the found user info OR if a matching user is not found a line that puts the searched for e-mail address then "Not Found"
$base_path = "C:\scripts\validate_users\"
$source_file = "input_emails.csv"
$out_file = "results.csv"
#read the file, look them up
$users = Import-csv -Path ($base_path + $source_file) -delimiter ";" | ForEach {
try {
Get-ADUser -Filter "EmailAddress -eq '$($_.email)'" -Properties EmailAddress
}
catch {
"No user for" + '$_.email'
}
}
# Output the resultant collection to a csv file
$users | Export-csv -Path ($base_path + $out_file)
Which gives me all the found records and no error messages.
I'd like to avoid making $users into an array and adding a value there. Is there a way to add in-line "searchedforuser#fakedomain.com NOT FOUND" inline with the results I get now.
Input is along the lines of
joesmith#ourdomain.com
janejones#ourdomain.com
freddielee#ourdomain.com
guywhoquit#ourdomain.com <== won't find this one
realuser#ourdomain.com
Right now the output is just the results for the four found users with no indication the "guywhoquit#ourdomain.com" was ever in the original list
Sorry if this is a newb question. I am a ps newb, but I searched for quite a bit and I'm missing if a similar question has already been answered.
Since you're using Get-AdUser with the -Filter parameter, it will simply return $null if no matching user is found (assuming the -Filter argument is well-formed) - it won't report an error.
Therefore, check the Get-ADUser's output to see if a user was found.
The -ov (-OutVariable) common parameter allows you to capture a cmdlet's output in a variable (independently of its output behavior), which you can inspect later:
$base_path = "C:\scripts\validate_users"
$source_file = "input_emails.csv"
$out_file = "results.csv"
Import-csv -Path (Join-Path $base_path $source_file) -delimiter ";" | ForEach {
# Get and output the user for the email address at hand;
# also store the output in variable $user, via `-ov user`
Get-ADUser -Filter "EmailAddress -eq '$($_.email)'" -Properties EmailAddress -ov user
if ($user.Count -eq 0) { # User not found?
# Emit a dummy object with an .EmailAddress property
# whose value indicates that the user wasn't found.
# This will show up in the CSV file as a row with all columns
# except the "EmailAddress" one empty.
[pscustomobject] #{ EmailAddress = "No user for $($_.email)" }
}
} | Export-csv -Path (Join-Path $base_path $out_file)
Note: The reason that just emitting string "No user for" + '$_.email' to the output stream wouldn't be enough is that Export-Csv locks in the columns it outputs based on the 1st input object.
A [string] instance has no properties in common with an AD users object, so you'd get a CSV row without any values.
By constructing a dummy custom object with an .EmailAddress property ([pscustomobject] #{ EmailAddress = "..." }), that property value will show up in the file (though all other column values will be empty).
Your problem here is that Powershell only catches "Terminating exceptions" to solve this you could try either of this 2 following modifications:
Get-ADUser -Filter "EmailAddress -eq '$($_.email)'" -Properties EmailAddress -ErrorAction Stop #This will only affect this cmdlet.
Or
$ErrorActionPreference = 'Stop' #This affects every cmdlet execution you have after this line.

Trying to find a way to speed up this powershell script

I have this script that works fine (output looks good), but it is taking longer than 12 hours now. There are 34220 records in the csv and it's only now on 2110. Maybe I need to load up all the user data first, then compare to the csv file? Thx for help...
import-module ActiveDirectory
$CCure = Import-csv C:\Scripts\CCure\CCure-Personnel-enabled.csv
ForEach ($Row in $CCure) {
[string]$ID = $Row.ObjectID
[string]$Name = $Row.Name
[string]$EmpID = $Row.Int5
If ($EmpID.Trim() -ne "0") {
$User = Get-ADUser -LDAPFilter "(&(&(&(objectclass=user)(objectcategory=person)(!userAccountControl:1.2.840.113556.1.4.803:=2))))((employeeId=*$EmpID))" -SearchBase 'DC=Enterprise,DC=mycompany,DC=org' -Properties SamAccountName,DisplayName,EmployeeId,enabled |
Select #{Name="CCure ObjectID";Expression={$ID}},SamAccountName,DisplayName,#{Name="CCure Name";Expression={$Name}},EmployeeId,#{Name="CCure Int5 Row";Expression={$EmpID}},enabled | Export-csv c:\scripts\ccure\EmployeeIds4-10-2016.csv -NoTypeInformation -append
}
}
Maybe I need to load up all the user data first, then compare to the csv file?
That's exactly what you need to do!
Since you want to correlate the users in the CSV by the EmployeeId attribute, I'd recommend pulling out all the (enabled) users that have the EmployeeId populated, and then store them in a hashtable where the EmployeeId is used as the key:
$ADUserTable = #{}
Get-ADUser -LDAPFilter "(&(!userAccountControl:1.2.840.113556.1.4.803:=2)(employeeId=*))' -SearchBase 'DC=Enterprise,DC=mycompany,DC=org' -Properties SamAccountName,DisplayName,EmployeeId |ForEach-Object {
$ADUserTable[$_.EmployeeId] = $_
}
Then, as you iterate over the rows in the CSV, lookup the user in the hashtable instead of searching AD again:
$ExistingUsers = ForEach ($Row in $CCure) {
# Import-Csv always creates string properties anyways
$ID = $Row.ObjectID
$Name = $Row.Name
$EmpID = $Row.Int5.Trim()
if ($EmpID -ne "0" -and $ADUserTable.ContainsKeys($EmpID))
{
$ADUserTable[$EmpID] |Select #{Name="CCure ObjectID";Expression={$ID}},SamAccountName,DisplayName,#{Name="CCure Name";Expression={$Name}},EmployeeId,#{Name="CCure Int5 Row";Expression={$EmpID}}
}
}
Do NOT export them to Csv until AFTER you've collected all the information - otherwise you're opening, writing to and closing the same file 35000 times!
So, at the very end:
$ExistingUsers |Export-csv c:\scripts\ccure\EmployeeIds4-10-2016.csv -NoTypeInformation
This will undoubtedly speed up execution of your script
Note: I've removed the Enabled property from Get-ADUser and Select-Object. Your LDAP Filter already guarantees that only Enabled users are returned, so I don't really see any value in adding it to the CSV