powershell - compare username to a csv and report non-matches - powershell

I'm migrating users from a legacy system into Active Directory and need to add them into an AD group. Usernames don't match (or have any logic!) between the two systems so I've got a csv with the old and new usernames in:
Input.csv:
SamAccountName,LegacyUsername
ChristopherSpraggons,CSprag
JakeSquirrell,JakeSq
GeorgeCornish,CornishG
I've written the powershell to do the lookup and add the user to the group but ideally need it to report any users that it cannot match out to a txt file so they can be done manually. I'm totally stuck on how to do this.
Here's what I have so far:
$ImportCSV = C:\TEST.csv
Import-module ActiveDirectory
$InputCSV=get-content $ImportCSV
$MatchedUsers = Import-CSV 'Input.csv' | Where{$InputCSV -match $_.LegacyUsername} | ForEach-Object {Add-ADGroupMember -Identity "ADGroupName" -Member $_.SamAccountName}
TEST.csv is just plain a list of legacy usernames with no headings.

Don't filter the CSV data before the loop. Instead, put an conditional inside the loop and add the non-matching names to a second array:
Import-Module ActiveDirectory
$legacyNames = Get-Content 'C:\TEST.csv'
$NonMatchedUsers = #()
$MatchedUsers = Import-CSV 'Input.csv' | % {
if ( $legacyNames -contains $_.LegacyUsername ) {
Add-ADGroupMember -Identity "ADGroupName" -Member $_.sAMAccountName
$_.sAMAccountName
} else {
$NonMatchedUsers += $_.sAMAccountName
}
}
I'd rename TEST.csv to something like LegacyNames.txt, though, because it isn't a CSV in the first place.
Edit: Since you want TEST.csv to be the actual input I'd suggest a slightly different approach. Load Input.csv into a hashtable (a dictionary) and make TEST.csv the input for the pipeline:
Import-Module ActiveDirectory
$mapping = #{}
Import-Csv 'Input.csv' | % { $mapping[$_.LegacyUsername] = $_.sAMAccountName }
$NonMatchedUsers = Get-Content 'TEST.csv' | % {
if ( $mapping.contains($_) ) {
Add-ADGroupMember -Identity "ADGroupName" -Member $mapping[$_]
} else {
$_
}
}

Assuming you have headers in your CSV, you should be able to use PowerShell's array methods easily without manually enumerating both CSVs repeatedly.
$ImportCSV = C:\TEST.csv
Import-Module ActiveDirectory
$InputCSV = Get-Content $ImportCSV | `
ForEach-Object {
if ($ImportCSV -notcontains $_.UserName) {
$_ | Out-File C:\MissingNames.txt -Append
} }

Related

Compare User AD and CSV file column Powershell

I'm not really good in Powershell, I try to write a script to compare a column "User" in a CSV with my all user AD.
I need to get all users in the CSV where not in our AD.
Here what I have wrote :
$csvfile = Import-CSV USERAccountstocompare.csv
$alladusers = Get-ADUser -Filter * | Select sAMAccountName
foreach($user in $alladusers){
$userAD = $alladusers.SamAccountName
foreach($usercsv in $csvfile){
if ($usercsv | where {$_.user -ne "$userAD"}){ write "$usercsv"}
else{}
}
}
When I put a write $usercsv before the if command; I get the good user
but after the if, it write all user with #{User= before, like "#{User=username}" so the comparison not working.
You don't need a foreach loop for this; just filter with Where-Object.
Assuming the User column in the CSV contains SamAccountNames:
$csvUsers = Import-Csv -Path 'D:\Test\USERAccountstocompare.csv'
$allADUsers = Get-ADUser -Filter * | Select-Object -ExpandProperty sAMAccountName
$notADUsers = $csvUsers | Where-Object { $allADUsers -notcontains $_.User }
# output on screen
$notADUsers | Format-Table -AutoSize
# output to new CSV file
$notADUsers | Export-Csv -Path 'D:\Test\UsersNOTinAD.csv' -NoTypeInformation
$alladusers = Get-ADUser -Filter * | Select sAMAccountName is not a very good idea if the Domain you are working on is big. Using Where-Object is also not a very good idea for filtering big objects, there was a really cool article in powershell.org where Dave Wyatt and Don Jones explained the different ways of filtering an object and their efficiency, sadly it was removed for some reason.
I would do something like this, assuming your Csv has a column 'User' for each user:
$result=New-Object System.Collections.ArrayList
#result array will be only the user that do not exist in AD
$csvfile = Import-CSV USERAccountstocompare.csv
foreach($line in $csvfile.User)
{
$filter="(|(Name=$line)(samAccountName=$line))"
$adusr=Get-ADuser -LDAPFilter $filter
if(!$adusr)
{
$result.add($line) > $null
}
}
If instead, you wanna have a list of the users that are on the Csv and on AD and those that are only in the Csv you could do something like this:
$result=New-Object System.Collections.ArrayList
#result array will be only the user that do not exist in AD
$csvfile = Import-CSV USERAccountstocompare.csv
foreach($line in $csvfile.User)
{
$filter="(|(Name=$line)(samAccountName=$line))"
$adusr=Get-ADuser -LDAPFilter $filter
if(!$adusr)
{
$result.add(
[pscustomobject]#{
'Not In AD'=$line
}) > $null
}
else
{
$result.add(
[pscustomobject]#{
'In AD and Csv'=$line
}) > $null
}
}

Exporting Membership groups for users from input file

I have this script that reads samaccountnames from a file and outputs the name of the user with its membership information. However, the output file only shows the last record. It seems that my code is overwriting the previous record. What am I missing? Thank you so much.
ForEach ($user in $(Get-Content -Path C:\MyScripts\UsersInput.csv))
{
$username = Get-ADUser –Identity $user -Properties *
Get-ADPrincipalGroupMembership $user | select $username.DisplayName, name |
export-csv "C:\MyScripts\UsersAndTheirADGroups.csv" -NoTypeInformation
}
Export-Csv has an -append parameter, so you could use that. ie it would append to the csv file with every iteration of the loop. You would need to make sure the file didn't exist before you start the loop or it would just get bigger and bigger each time you ran the code.
Another way it to add the items to an object and then export that at the end. ie $username += Get-ADUser......
You are reading a CSV file using Get-Content. This lets me think the file is simply a list of user SamAccountNames, each on a separate line. No headings.
Something like this perhaps:
jdoe
jsmith
If that is the case, read the input file like this:
$users = Get-Content -Path 'C:\MyScripts\UsersInput.csv'
To get an array of user SAMAccountnames.
If however it is a proper CSV file with headers, looking something like this:
"SamAccountName","Email","More","Stuff"
"jdoe","john.doe#yourdomain.com","blah","blah"
"jsmith","jane.smith#yourdomain.com","blah","blah"
Then you should use the Import-Csv cmdlet to get the entries as objects and obtain an array of SamAccountNames from that:
$users = Import-Csv -Path 'C:\MyScripts\UsersInput.csv' | Select-Object -ExpandProperty SamAccountName
Once you have that array, loop through it and get the group membership info for each user
Untested
$result = foreach ($accountName in $users) {
Get-ADUser –Identity $accountName -Properties DistinguishedName, DisplayName |
Select-Object #{Name = 'User'; Expression = {$_.DisplayName}},
#{Name = 'Groups'; Expression = { ( $_ | Get-ADPrincipalGroupMembership | Select-Object -ExpandProperty name) -join ', '}}
}
$result | Export-Csv "C:\MyScripts\UsersAndTheirADGroups.csv" -NoTypeInformation
You are indeed overwriting the code ForEach user. You included Export-Csv in the ForEach. Instead export the whole array that ForEach creates:
ForEach ($user in $(Get-Content -Path C:\MyScripts\UsersInput.csv))
{
$username = Get-ADUser –Identity $user -Properties *
Get-ADPrincipalGroupMembership $user | select $username.DisplayName, name
} | export-csv "C:\MyScripts\UsersAndTheirADGroups.csv" -NoTypeInformation

How to check a list of groups to see if there are members

I've been working on a script to run through a list of groups in a CSV file to see if they have any members.
Ultimately I'm looking to have the script export the results to a separate CSV file.
$groups = Get-Content 'C:\Users\me\Desktop\testGroups.csv'
foreach ($groups in $groups) {
$users = Get-ADGroupMember -Identity $groups
if (($users | Measure-Object).Count -ne 0) {Write-Output "$groups has something" | Out-File C:\Users\me\Desktop\membersTest.csv -Append}
Else {Write-Output "$groups has nothing" | Out-File C:\Users\me\Desktop\membersTest.csv -Append}
}
This returns the following:
Length
27
31
41
30
...
I've attempted to change Write-Output to Write-Host and that appears to return the correct results, but it only displays it within the CMD window obviously.
Would someone be able to assist me with the process of correcting these IF Else statements?
End result is a csv, so build an object and export it.
$groups = Get-Content 'C:\Users\me\Desktop\testGroups.csv'
$GroupMemberCount = ForEach ($group in $groups) {
[PSCustomObhect]#{
Group = $group
MemberCount = (Get-ADGroupMember -Identity $group).Count
}
}
$GroupMemberCount | Out-Gridview
$GroupMemberCount | Export-Csv 'C:\Users\c002568\Desktop\membersTest.csv' -NoTypeInformation
I had this similar script for another project which might be helpful, I've modified to match your query. If you don't have need to display output, you can ignore that.
$groups = import-csv groups.csv
$outputFile = New-Item -ItemType file testOutputCSV.CSV -Force
foreach ($group in $groups) {
[array]$users = Get-ADGroupMember -Identity $group.samaccountname
if($users.count -ne 0){
Write-Output "$($group.samaccountname) has something "
}
else {
Write-Output "$($group.samaccountname) has 0 members"
}
$group.samaccountname +","+$users.count | Out-File $outputFile -Append
}
If the intention is to highlight the groups not having any members on console, as well as CSV, you can bring this line inside IF Else block, add more columns as needed.
$group.samaccountname +","+$users.count | Out-File $outputFile -Append
If you don't need to display anything on the console, you can omit IF Else block.
If you want to update CSV only for groups having 0 members or non-zero members, you can modify accordingly.
Edit: Masked $Users to be [array] since single member groups would return ADPrincipal

How to get list of selected AD Groups, that a large list of users are members of?

I have the below working script that checks if a large list of users in a CSV file are a member of an AD group and writes the results to results.csv.
Not sure how to convert the script so I can change $group = "InfraLite" to $group = DC .\List_Of_AD_Groups.CSV.
So the script doesn't just return matches for one AD group but so it returns matches for the 80 AD groups contained in the List_of_AD_groups.csv also. Writing a YES/NO for each AD group in a new column in the CSV (or if that's not possible creating a seperate .csv file for each group with results would do also.
I could do this manually by changing the value of $group and export file name, and re-running the script 80 times but must be a quick was with PS to do this?
e.g. results.csv:
NAME AD_GROUP1 AD_GROUP2 AD_GROUP80 etc etc.
user1 yes no yes
user2 no no yes
user3 no yes no
echo "UserName`InfraLite" >> results.csv
$users = GC .\user_list.csv
$group = "InfraLite"
$members = Get-ADGroupMember -Identity $group -Recursive |
Select -ExpandProperty SAMAccountName
foreach ($user in $users) {
if ($members -contains $user) {
echo "$user $group`tYes" >> results.csv
} else {
echo "$user`tNo" >> results.csv
}
}
I played with this for a while, and I think I found a way to get you exactly what you were after.
I think Ansgar was on the right path, but I couldn't quite get it to do what you were after. He mentioned that he didn't access to an AD environment at the time of writing.
Here is what I came up with:
$UserArray = Get-Content 'C:\Temp\Users.txt'
$GroupArray = Get-Content 'C:\Temp\Groups.txt'
$OutputFile = 'C:\Temp\Something.csv'
# Setting up a hashtable for later use
$UserHash = New-Object -TypeName System.Collections.Hashtable
# Outer loop to add users and membership to UserHash
$UserArray | ForEach-Object{
$UserInfo = Get-ADUser $_ -Properties MemberOf
# Strips the LPAP syntax to just the SAMAccountName of the group
$Memberships = $UserInfo.MemberOf | ForEach-Object{
($_.Split(',')[0]).replace('CN=','')
}
#Adding the User=Membership pair to the Hash
$UserHash.Add($_,$Memberships)
}
# Outer loop to create an object per user
$Results = $UserArray | ForEach-Object{
# First create a simple object
$User = New-Object -TypeName PSCustomObject -Property #{
Name = $_
}
# Dynamically add members to the object, based on the $GroupArray
$GroupArray | ForEach-Object {
#Checking $UserHash to see if group shows up in user's membership list
$UserIsMember = $UserHash.($User.Name) -contains $_
#Adding property to object, and value
$User | Add-Member -MemberType NoteProperty -Name $_ -Value $UserIsMember
}
#Returning the object to the variable
Return $User
}
#Convert the objects to a CSV, then output them
$Results | ConvertTo-CSV -NoTypeInformation | Out-File $OutputFile
Hopefully that all makes sense. I commented as much of it as I could. It would be very simple to convert to using ADSI if you didn't have RSAT installed on whatever machine you're running this on. If you need that let me know, and I'll make some quick modifications.
I've also tossed a slightly modified version of this in a Gist for later reference.
The trivial solution to your problem would be to wrap your existing code in another loop and create an output file for each group:
$groups = Get-Content 'C:\groups.txt'
foreach ($group in $groups) {
$members = Get-ADGroupMember ...
...
}
A more elegant approach would be to create a group mapping template, clone it for each user, and fill the copy with the user's group memberships. Something like this should work:
$template = #{}
Get-Content 'C:\groups.txt' | ForEach-Object {
$template[$_] = $false
}
$groups = #{}
Get-ADGroup -Filter * | ForEach-Object {
$groups[$_.DistinguishedName] = $_.Name
}
Get-ADUser -Filter * -Properties MemberOf | ForEach-Object {
$groupmap = $template.Clone()
$_.MemberOf |
ForEach-Object { $groups[$_] } |
Where-Object { $groupmap.ContainsKey($_) } |
ForEach-Object { $groupmap[$_] = $true }
New-Object -Type PSObject -Property $groupmap
} | Export-Csv 'C:\user_group_mapping.csv' -NoType

Writing an array to a specific csv column with powershell

I have a csv file that has a lot of data in it, with one column being a "Username" column. I created a loop to query AD and get each username and now I need to export each of those names to the specific column in the csv. After importing the csv with:
$data = Import-CSV .\data.csv
And using the loop:
foreach($user in $data)
And I use get-aduser $user -server $server and if($? -eq $true){ $user = $user + "01" }
I tried using
$data.Username | Export-CSV .\data.csv and $data.Username | out-file .\data.csv
but so far neither have worked.
You need to keep all the information in the pipeline so you can re-export the whole thing.
Try something like this:
$data | Foreach {
get-aduser $_.Username -server $server
if($? -eq $true){ $_.Username = $_.Username + "01" }
} | export-csv .\data.csv