I am trying to write a script to pull all e-mail addresses but I am finding that the output of my first get command is adding alot of white space to the result text file
Get-AdUser -Filter * -Properties * | Select EmailAddress | Out-File "C:\T2\EMailAddresses.txt"
Can anyone tell me what I am doing wrong here?
You are seeing whitespace because not every AD account has a value assigned to that property in your environment so it appears as a blank line, I get the same thing upon a quick test.
This should help.
$Emails = Get-ADUser -Filter * -Properties EmailAddress
$Emails | select EmailAddress | Where {$_.EmailAddress -ne $null} | Out-File "C:\T2\EMailAddresses.txt"
td;dr
The following writes all email addresses to the target file, ignoring AD users that don't have one:
([string[]] (Get-AdUser -Filter * -Properties EmailAddress).EmailAddress) -ne '' |
Set-Content C:\T2\EMailAddresses.txt
By writing just the - non-empty - .EmailAddress property values to the file, you're avoiding the problems that stem from saving for-display formatted object representations, which is what your attempt does (see below).
Note that -ne '' acts as a filter here, because its LHS operand is an array; that is, the result of the operation is the sub-array of those LHS elements that aren't the empty string ('').
As for what you tried:
By using Out-File in combination with objects subject to PowerShell's output formatting system, you're saving a for-display representation of your objects to a file, which, in the case at hand includes a table header, a leading and a trailing blank line and - in Windows PowerShell (but no longer in PowerShell (Core) 7+) - right-space-padding to the full console-line width of each line.
Even though you're only asking for one property - EmailAddress - Select-Object outputs not just that property's value for each input object, but a [pscustomobject] instance with an .EmailAddress property, and the resulting objects are implicitly formatted with Format-Table.
To get just the EmailAddress property values, use Select-Object -ExpandProperty EmailAddress. The resulting string values are not subject to formatting, so your command would work as intended except that it would still include $null values from those AD users who happen not to have a value stored in their .EmailAddress property.
While it often won't matter, for string input it's slightly faster to use Set-Content than Out-File / >; note that in Windows PowerShell you'll end up with different character encodings by default (ANSI vs. UTF-16 LE a.k.a "Unicode") - use the -Encoding parameter as needed; PowerShell Core 7+ fortunately now consistently defaults to BOM-less UTF-8.
The - faster, but more potentially memory-intensive - alternative to using Select-Object -ExpandProperty EmailAddress for extracting the EmailAddress property values is to use member-access enumeration ((...).EmailAddress, as shown above).
Related
Below is the code I am working with. I have verified that the initial import-csv is working as it should be, and if I change out the variable object for a concrete object, the script works as it should. It just seems to not recognize/use the variable the way that it should.
$CSVOutput = "C:\temp\output.csv"
$Output = foreach($u in $userlastname)
{
Get-ADComputer -Filter {Description -Like '*$u*'} -properties Description | Select Name, Description
}
$Output | Export-Csv $CSVOutput
If I replace the $u in the filter with one of the values from the $userlastname variable, it works, but it just runs the search with the set value as many times as it runs the foreach loop. I am expecting to see several different computer objects that have the different values from $userlastname in their description. Currently it returns nothing, as if it found no values that matched in the description field.
While it’s technically possible to use a scriptblock as a filter in the ADCommands, it isn’t recommended - use a string instead:
Get-ADComputer -Filter "Description -like '*$($u.name)*'" -Properties ...
Using a string will solve your variable substitution issue.
ETA: Comments indicated that you were getting #{Name=User} as the expansion for $u in the filter expression. This is because $u was a structured [PSCustomObject], and you were looking for a single field from that object. The easiest way to get the value of the desired field of the object is simply to use the PowerShell evaluation construct, as given in the edited answer.
I'm somewhat basic to Powershell and use one-liner commands only to keep it short and basic.
I would like to do the following: I have a list of users in a text file in the form of UserPrincipalName. I'd like to query this list of users if their accounts are still active/enabled or not. To do so, I'm trying to run the following command, which just reveals nothing in the end (blank output):
gc .\users.txt | foreach {get-aduser -server "corp.xxx.com"
-f 'name -like "$_"' -properties *}| select displayname,enabled
As mentioned, the output is blank with no errors or whatsoever.
I read that aduser doesn't work with pipelines, but I need to find a solution.
Kindly request your support :)
Thanks
Your use of single quotes in your filter is not allowing the expansion of the variable. Double-quotes should be wrapping the filter expression so as to allow the interpolation of the automatic variable $_:
Get-ADUser -Filter "name -like '$_'" ...
Single-quoted strings:
A string enclosed in single quotation marks is a verbatim string. The string is passed to the command exactly as you type it. No substitution is performed.
Also note, you mention in your question that the file has the user's UserPrincipalName attribute, yet you're querying the Name attribute, if that's the case, the filter should be:
Get-ADUser -Filter "UserPrincipalName -eq '$_'" ...
Note the use of -eq instead of -like, for exact matches you should always use this operator, see about_ActiveDirectory_Filter for usage details and examples of each operator.
If you're only interested in DisplayName and Enabled for your output, there is no reason in querying all the user's attributes, -Properties * should be just -Properties DisplayName since Enabled is already part of the default attributes returned by Get-ADUser.
Finally, the -Identity parameter can be bound from pipeline, and this parameter accepts a UserPrincipalName as argument, hence ForEach-Object is not needed in this case:
Get-Content .\users.txt |
Get-ADUser -server "corp.xxx.com" -Properties DisplayName |
Select-Object DisplayName, Enabled
I'm parsing filenames in Powershell, and when I use Get-ChildItem | select name, I get a clean output of the files:
file1.txt
file2.txt
file3.txt
But when I try to narrow down those files with Select-String, I'm getting a weird # and { in front of my output:
Get-ChildItem | select name | Select-String -Pattern "1"
#{file1.txt}
Is there a parameter I'm missing? If I pipe with findstr rather than Select-String it works like a charm:
Get-ChildItem | select name | Findstr "1"
file1.txt
You can simplify and speed up your command as follows:
#((Get-ChildItem).Name) -match '1'
Note: #(), the array-subexpression operator, is needed to ensure that -match operates on an array, even if only one file happens to exist in the current dir.
(...).Name uses member-access enumeration to extract all Name property values from the file-info objects returned by Get-ChildItem.
-match, the regular-expression matching operator, due to operating on an array of values, returns the sub-array of matching values.
To make your original command work:
Get-ChildItem | select -ExpandProperty Name |
Select-String -Pattern "1" | select -ExpandProperty Line
select -ExpandProperty Name makes select (Select-Object) return only the Name property values; by default (implied -Property parameter), a custom object that has a Name property is returned.
select -ExpandProperty line similarly extracts the Line property value from the Microsoft.PowerShell.Commands.MatchInfo instances that Select-String outputs.
Note that in PowerShell [Core] v7+ you could omit this step by instead using Select-String's (new) -Raw switch to request string-only output.
As for what you tried:
As stated, by not using -ExpandProperty, select name (implied -Property parameter) created a custom object ([pscustomobject] instance) with a Name property.
Select-String stringifies its input objects, if necessary, so it can perform a string search on them, which results in the representation you saw; here's a simulation:
# Stringify a custom object via an expandable string ("...")
PS> "$([pscustomobject] #{ Name = 'file1.txt' })"
#{Name=file1.txt}
As an aside:
The above stringification method is essentially like calling .ToString() on the input objects[1], which often results in useless string representations (by default, just the type name); a more useful and intuitive stringification would be to use PowerShell's rich output-formatting system, i.e. to use the string representation you would see in the console; changing Select-String's behavior to do that is the subject of this feature request on GitHub.
[1] Calling .ToString() directly on a [pscustomobject] instance is actually still broken as of PowerShell Core 7.0.0-rc.2, due to this bug; the workaround is to call .psobject.ToString() or to use an expandable string, as shown above.
I was assisting a user with this question, linked to my answer here: Powershell script to add users to A/D group from .csv using email address only?
Initially I wrote the script as follows, using a bracket-based filter for Get-AdUser like follows:
Import-CSV "C:\users\Balbahagw\desktop\test1.csv" |
Foreach-Object {
# Here, $_.EmailAddress refused to resolve
$aduser = Get-ADUser -Filter { EmailAddress -eq $_.EmailAddress }
if( $aduser ) {
Write-Output "Adding user $($aduser.SamAccountName) to groupname"
Add-ADGroupMember -Identity groupname -Members $aduser
} else {
Write-Warning "Could not find user in AD with email address $($_.EmailAddress)"
}
}
However, $_.EmailAddress failed to populate a value. However, changing the Get-ADUser filter to a string-based filter worked as intended:
$aduser = Get-ADUser -Filter "EmailAddress -eq '$($_.EmailAddress)'"
What is the strangeness I'm experiencing, and why? Is it because when I'm using brackets, it's treated as a new scope and the $PSItem won't follow?
-Filter parameters are generally string parameters (verify with
Get-Help Get-AdUser -Parameter Filter)
They generally do not accept PowerShell code - filters are provider-specific and often have their own syntax, although it happens to be PowerShell-like in the case of the AD cmdlets.
Also, they generally have no knowledge of PowerShell variables (see below).
Thus, when a script block ({ ... }) is passed, it is converted to a string, which evaluates to its literal contents (everything between the opening { and the closing }):
{ EmailAddress -eq $_.EmailAddress }.ToString() yields the literal string EmailAddress -eq $_.EmailAddress - without any evaluation - and that's what Get-AdUser sees - no evaluation takes place.
In a presumably well-meaning but misguided effort to support the widespread, but ill-advised practice of passing script blocks to the -Filter parameter of AD cmdlets, it seems that these cmdlets actually explicitly expand simple variable references such as $_ in the string literal they receive, but that doesn't work with expressions, such as accessing a property of a variable ($_.EmailAddress)
Therefore, -Filter arguments should generally be passed as expandable strings ("..."); in the case at hand:
-Filter "EmailAddress -eq '$($_.EmailAddress)'"
That is, the only robust solution is to use strings with the variable parts baked in, up front, via string expansion, as shown above.
For values that are neither numbers nor strings, such as dates, you may have to use a literal string ('...') and rely on the AD provider's ability to evaluate simple references to PowerShell variables (e.g., $date) - see this answer of mine for details.
As stated, the syntax of AD filters is only PowerShell-like: it supports only a subset of the operators that PowerShell supports and those that are supported differ subtly in behavior - see Get-Help about_ActiveDirectory_Filter.
It is tempting to use script blocks, because the code inside requires no escaping of embedded quotes / no alternating of quote chars and no use of subexpression operator $(...). However, aside from using script blocks as strings being inefficient in general, the problem here is that the script block is making a promise that it cannot keep: it looks like you're passing a piece of PowerShell code, but you're not - and it works only in simple cases (and then only due to the misguided accommodation mentioned above); generally, it's hard to remember under what circumstances it doesn't work and how to make it work if it fails.
It is therefore really unfortunate that the official documentation uses script blocks in its examples.
For a more comprehensive discussion, see this answer of mine.
You're not wrong, it's the module's fault
The type of payload you have to use with the -Filter parameter differs depending on which provider you're working with, a design decision which can be pretty confusing!
The output of Get-Help Get-ADUser -Parameter Filter gives you some pretty detailed examples of the different syntax options you can use with the Active Directory Provider's implementation of Filter syntax.
Here's an example:
#To get all user objects that have an e-mail message attribute, use one of the following commands:
Get-ADUser -Filter {EmailAddress -like "*"}
It looks like the ActiveDirectory provider places the specific restriction that you must wrap the input in quotes. Here's what happens when I look for my account without putting quotes around my e-mail.
Get-ADUser -Filter {EmailAddress -eq stephen#foxdeploy.com}
Get-ADUser : Error parsing query: 'EmailAddress -eq stephen#foxdeploy.com'
Error Message: 'syntax error' at position: '18'.
But adding quotes? It works!
Get-ADUser -Filter {EmailAddress -eq "stephen#foxdeploy.com"}
DistinguishedName : CN=Stephen,CN=Users,DC=FoxDeploy,DC=local
Enabled : True
GivenName : Stephen
Name : Stephen
ObjectClass : user
ObjectGUID : 6428ac3f-8d17-45d6-b615-9965acd9675b
SamAccountName : Stephen
SID : S-1-5-21-3818945699-900446794-3716848007-1103
Surname :
UserPrincipalName : Stephen#FoxDeploy.local
How to make yours work
Now, because of this confusing filter implementation, you will need to change your user lookup on line 5 to the following:
$aduser = Get-ADUser -Filter "EmailAddress -eq `"$($_.EmailAddress)`""
We are providing the -Filter payload as a String. Next we want to use String Expansion to pull out the .EmailAddress property, so we wrap the string in $( ) to signal string expansion. Finally, the provider wants our filter comparison wrapped in quotes, so we put double quotes around it, and then escape the quotes using the backtick character.
And now it should work.
TLDR - blame the provider and blame the module, there are so many inconsistencies with the Active Directory module.
I am attempting to get these Custom headers to display correctly when bringing in content from Import-CSV
The CSV contains a column titled Surname with a Surname in each row, it also contains a header titled "Email" with no data in any of the cells below.
$csv = Import-csv C:\temp\tester.csv
$csv | select Surname,#{Name='Email';Expression={Get-ADUser -Filter 'Surname -like "$_.Surname"' | Select -ExpandProperty UserPrincipalName}}
executes correctly when I hardcode the surnames in. Any ideas?
You enclose the filter in single quotes, this prevents Powershell from substituting $_.Surname for a value. If a string is enclosed in single quotes, it is treated as is, without any attempts to replace symbols, including escape symbols, prior to passing the string as an argument or an expression result. Replace the expression with this:
Expression={Get-ADUser -Filter "Surname -like `"$_.Surname`"" | Select -ExpandProperty UserPrincipalName}
The backticks are used to escape the quotes that should be passed into AD filter.