Author Posts

March 28, 2018 at 8:23 pm

Hi all,

Wonder if someone could point out where I'm going wrong with this one – I'm sure this should be much simpler than I'm making it! I've got a list of people's names (which should match the DisplayName field in Active Directory), for whom I want to the ADUsers (ultimately, I'd look to grab the SAMAccountNames and export them out to another csv).

So, I put the list of names into a csv and tried this:

Import-CSV .\listofnames.csv | %{ Get-ADUser -Filter {DisplayName -eq $_.PersonName}}

...where personname is the column header in the csv file.

When I run this I get an error

Get-ADUser : Property: 'PersonName' not found in object of type 'System.Management.Automation.PSCustomObject'

When I do a Get-Member on the imported CSV, I see PersonName listed, but it is a NoteProperty, rather than a property as you'd normally see it. Not sure if that's significant? Having said that, referencing the property works just fine when I so something slightly simpler like:

Import-CSV .\listofnames.csv | %{ Write-Output $_.PersonName}

Any ideas at all?

Thanks

March 28, 2018 at 9:04 pm

Hmmm ... a little bit strange but I can reproduce this error. I tried a little bit and this seems to work somehow ...

Import-Csv -Path .\listofnames.csv  | 
    ForEach-Object {
        $DisplayName = $_.PersonName
        Get-ADUser -Filter {Displayname -eq $DisplayName} 
    }

But please do not ask me to explain why it does not work the other and why it does work this way. 😉

March 29, 2018 at 4:30 am

Olaf, yours works becasue you are explicitly sending the raw string value.

They way the Darren is calling this, there is no default property called DisplayName in the result set, nor is he calling it before he uses the pipe.

    Import-CSV '.\listofnames.csv' | %{ $_.PersonName}
    Import-CSV .\listofnames.csv | %{ Get-ADUser -Filter {DisplayName -eq $_.PersonName}}

We can see this by calling the Get-ADUser cmdlet without the pipeline for the file with Get-Member.

    Get-ADUser -Filter * | Select -First 1 | Get-Member -MemberType Properties | Select Name

    Name
    ---- 
    DistinguishedName
    Enabled
    GivenName
    Name
    ObjectClass
    ObjectGUID
    SamAccountName
    SID
    Surname
    UserPrincipalName 

It's still odd that it is not matching by value though, which is odd.
Yet, what appears to be happening, is that header name – PersonName, for whatever reason is getting read in and Get-ADUser is barfing on it.

So, like your example, the following work as well.

    Import-CSV '.\listofnames.csv' | %{ Get-ADUser -Filter * -Properties DisplayName | Where DisplayName -eq $_.PersonName}
    (Import-CSV '.\listofnames.csv').PersonName | %{ Get-ADUser -Filter * | Where Name -EQ $_}
    Import-CSV '.\listofnames.csv' | %{ Get-ADUser -Filter * | Where Name -eq $_.PersonName}

    DistinguishedName : CN=5Admin ServiceAc...
    Enabled           : True
    GivenName         : 5Admin
    Name              : 5Admin ServiceAccount
    ObjectClass       : user
    ObjectGUID        : c8b5e...
    SamAccountName    : 5admin
    SID               : S-1-5-21-...
    Surname           : ServiceAccount
    UserPrincipalName : 5admin@contoso.com
    ....

March 29, 2018 at 10:34 am

As always – a great and comprehensive explanation. Thank you very much.

March 29, 2018 at 12:57 pm

The Filter parameter on Get-ADUser does not work directly, it converts it to an ldapFilter behind the scenes. As such, I'm never sure if it's going to do it correctly, so I avoid it for all but the simplest of searches. Try this:

Import-CSV .\listofnames.csv | %{ Get-ADUser -ldapFilter "(DisplayName=$($_.PersonName))"}

March 29, 2018 at 2:14 pm

The alternative, which would work pretty well with OP's original attempt albeit with some extra $( ) around his variable property accessor, is to use double quotes for the filter.

Yes, all the documentation on -Filter tells you to use braces. Just... don't. It is a [string] parameter in the documentation (and implementation), so what can often happen is that PS converts the contents of what is actually a [scriptblock] to [string] directly. When this is done, it can completely skip the part where PS usually expands variables in strings.

Save yourselves the time and hassle, and just do:

Import-CSV .\listofnames.csv | % { Get-ADUser -Filter "DisplayName -eq $($_.PersonName)"}

It'll insert the variable just as it ought to.

March 29, 2018 at 9:39 pm

Thank you all for your help and advice! Those all worked for me!

See, I'd wondered if it didn't want to play nicely with my pair of braces on the filter argument – I think I tried with quotes, but I think I was missing the second $()! One small change I needed to make to Joel's solution was to put the comparison part in single quotes to give:

 Import-CSV .\listofnames.csv | % { Get-ADUser -Filter "DisplayName -eq '$($_.PersonName)'"}

I'm also going to spend a bit more time playing with the -ldapFilter parameter; that looks like it will come in handy.

One more quick follow-up question on this – re the use of -filter *

My assumption was/is that I should probably avoid using this where possible to prevent unnecessary load on the domain controller. Is that right? Not sure exactly how many active users are in my AD, but rough guess is it's in the region of 5,000.

Thanks again everyone!

March 29, 2018 at 10:59 pm

Oop, nice catch! I tend to forget that you still have to quote the second part because... AD cmdlets are silly, really there's no explanation for requiring such odd behaviour!

And yes, where possible have some limiting aspect of your user search. If you try to get all users in the domain it'll cause a good bit of time lag, and probably bog down the network a little bit.

So, either use a filter, or search only in a single OU, or something of that sort to limit the amount of objects the command will return from the DC.

In general, with PS (and I guess with most programming languages!), you should attempt to adhere to the maxim: Filter left, format right.

Basically, that means do all filtering as early as possible to limit the amount of unnecessary memory usage and objects being passed around. Then, do any formatting absolutely last so you retain data in its most useful form as much as possible and save you lots of trouble. 🙂