Find AD-user by Email - powershell

I'm trying to get users from active directory, who have a certain email address.
I've got a CSV-File, which contains the emails.
$data = import-csv -path .\are.csv -delimiter +
foreach($i in $data)
{
get-aduser -filter {UserPrincipalName -like '$i.email'}
}
My problem is, that I don't get any output. No error, no data. When I replace $i.email with one of the email addresses, I get the right user information for the one user. So hard coding works.
When I do:
foreach($i in $data)
{
$i.email
}
I get a list of all the emails.
What is wrong with the code?

Your variable is within single quotes thus doesn't get populated. You have to three options to fix that:
Use double quotes with a sub expression $():
UserPrincipalName -like "$($i.email)"
Just omit the quotes:
UserPrincipalName -like $i.email
And finally you could use a format string (even with single quotes):
UserPrincipalName -like ('{0}' -f $i.email)
Here is an example to demonstrate what actual value gets passed using the Write-Host cmdlet:
$data =
#'
email, id
myname#web.de, 1
'# | convertfrom-csv
foreach($i in $data)
{
Write-Host '$i.email' # This is your current approach
Write-Host "$($i.email)"
Write-Host $i.email
Write-Host ('{0}' -f $i.email)
}
Output:
$i.email # This is the output of your current approach
myname#web.de
myname#web.de
myname#web.de

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.

How to fix A null key is not allowed in a hash literal in PowerShell

I been following this Microsoft doc https://learn.microsoft.com/en-us/powershell/module/exchange/search-unifiedauditlog?view=exchange-ps and I'm trying to write a PowerShell Script that download Audit Log. So far everything is going well but I'm just wondering how can I read User Id from my csv file instead of having a user Id directly in my script?.
This is how my CSV file look right now.
C:\AuditLogSearch\Reference\User.csv
Name Email Id
................................
1. Ronaldo ronaldo#gmail.com KLIEKN
2. Messi messi#gmail.com LEK89K
3. LeBron lebron#gmail.com IKNLM9
I was hard coding like this in the script and it's working fine but
$Global:user = "KLIEKN", "LEK89K", "IKNLM9",
Search-UnifiedAuditLog -SessionId $sessionID -SessionCommand ReturnLargeSet -UserIds $user
I don't think that'll be good idea so I try to do read it from my CSV file like this
$Global:userPath = "C:\AuditLogSearch\Reference\User.csv"
function Log-Search {
Import-Csv $userPath | ForEach-Object -Property #{
$userId = $($_.Id)
}
Search-UnifiedAuditLog -SessionId $sessionID -SessionCommand ReturnLargeSet -UserIds $userId
}
but I'm getting this error
A null key is not allowed in a hash literal.
I'll be really appreciated any help or suggestion.
{} defines a [ScriptBlock] — which is what you'd pass to the ForEach-Object cmdlet to be invoked for each element — whereas #{} defines a [Hashtable]. $userId is $null because you have not assigned a value, so where you have...
#{
$userId = $($_.Id)
}
...you are trying to define a [Hashtable] with an element with a key of $null, hence the error.
There is also no such -Property parameter of ForEach-Object, so when you remove "-Property #", you end up with a valid script...
Import-Csv $userPath | ForEach-Object {
$userId = $($_.Id)
}
This is reading your CSV file but not yet passing the data to your Search-UnifiedAuditLog call. There are several ways to retrieve the Id field of each CSV record, but the shortest transformation from the previous snippet would be...
Import-Csv $userPath | ForEach-Object {
$_.Id
}
...which can be rewritten using the -MemberName parameter...
Import-Csv $userPath | ForEach-Object -MemberName Id
...and then all that's left is to store the pipeline results in $userId...
$userId = Import-Csv $userPath | ForEach-Object -MemberName Id
By the way, the CSV data you posted cannot be readily parsed by Import-Csv. If possible, save your data without the second line and using comma or tab as the delimiter (the latter being read with Import-Csv ... -Delimiter "`t"); otherwise, the script will have to do some manipulation before it can read the data.
Try this perhaps?
$userId=#()
Import-Csv $userPath | ForEach-Object {
$userId += $_.Id
}
or
$userId = Import-Csv $userPath | Select-Object -ExpandProperty Id

[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

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.

How to skip empty cells in a csv when using PowerShell to import Email Addresses?

I am trying to run the following script to import email addresses in powershell:
Import-CSV "C:\AliasesTest.csv" | foreach-object {
Set-Mailbox -Display Name $_.Name -EmailAddresses #{add=$_.Alias1,$_.Alias2,$_Alias3}}
It works fine, unless the csv has an empty cell under one of the Alias columns, at which point the following error is produced:
"The address '' is invalid: "" isn't a valid SMTP address..:
How can I construct my script to just ignore empty cells when it comes across them?
Check each property (alias) to see if it is empty, and only add the ones with values to the array inside your hash table:
Import-CSV "c:\AliasesTest.csv" | ForEach-Object {
#Save the CSV row for use in another loop later
$CSV = $_
Set-Mailbox -DisplayName $_.Name -EmailAddresses #{add = ("Alias1","Alias2","Alias3" | ForEach-Object { $Csv.$_ } | Where-Object{$_}) }
}
What that craziness does is, create a new hashtable with a key "add" that has a value of a sub expression. The sub expression has an array of property names that you want to check that it iterates over, converting each name to the value of that property, then filters out the empty ones.
Use a Where-Object filter prior to ForEach-Object like this:
Import-CSV "C:\AliasesTest.csv" | Where-Object {
$_.Alias1 -and $_.Alias2 -and $_Alias3
} | foreach-object { $_ }
If the alias' are empty strings they will be skipped. You don't have to use -and for all of them, if just one value is fine change to use -or.
Filter the aliases to take out the empty ones:
Import-CSV "C:\AliasesTest.csv" | foreach-object {
$aliases = #($_.Alias1,$_.Alias2,$_Alias3) | where-object{$_};
Set-Mailbox -Display Name $_.Name -EmailAddresses #{add=$aliases}}