Powershell array searching help

DevilWAHDevilWAH Member Posts: 2,997 ■■■■■■■■□□
Can any one help

With out using a for loop how can a search an array pulled back from AD

So i have list of items

$ADusers = Get-aduser

then I want to say

$user = the item in $aduser where $aduser.givenname = "WAH" -and "$ADusers.surname = "Devil"

$ADusers is returned as a system.object and i can loop through but this is slow. What is the fastest method to pull out one of the objects in the list?

Cheers
  • If you can't explain it simply, you don't understand it well enough. Albert Einstein
  • An arrow can only be shot by pulling it backward. So when life is dragging you back with difficulties. It means that its going to launch you into something great. So just focus and keep aiming.

Comments

  • wastedtimewastedtime Member Posts: 586 ■■■■□□□□□□
    They key is not pulling them into powershell before searching. I'm guessing the DC has all of these fields in a hash table or similar structure that is already optimized.

    This site shows how to use Get-ADUser with filter.

    PowerShell Get-AdUser -filter | Active Directory Cmdlet LDAPfilter

    From what that is saying "Get-ADUser -Filter {(Givenname -eq "WAH") -And (Surname -eq "Devel")}" is more then likely the command you can use.
  • GSXR750K2GSXR750K2 Member Posts: 325 ■■■■□□□□□□
    wastedtime is right. Unless there is something else you want to do with the data other than view it (like pass it as one or more parameters for another cmdlet), there's no need to assign it to an object. You can, it just makes a little more work for you. :)
  • DevilWAHDevilWAH Member Posts: 2,997 ■■■■■■■■□□
    wastedtime wrote: »
    They key is not pulling them into powershell before searching. I'm guessing the DC has all of these fields in a hash table or similar structure that is already optimized.

    This site shows how to use Get-ADUser with filter.

    PowerShell Get-AdUser -filter | Active Directory Cmdlet LDAPfilter

    From what that is saying "Get-ADUser -Filter {(Givenname -eq "WAH") -And (Surname -eq "Devel")}" is more then likely the command you can use.

    Trouble is that doing it this way is making 1,000 of get requests to AD, and then I still need to get the entire AD list to work with later on. I am comparing the AD list to an HR list of users and forming 3 output lists

    1. Users that appear in both.
    2. Users that only appear in the HR list
    3. Users that appear in only the AD list.

    Also because I want to write changes to the users in AD if I write to AD then do a imitate get I am likely to get old data (3DC's) so its nicer simply to pull all the data in to power shell, play about with it "off line" and then write back to AD in one go.

    So your right I could do filtered searches in to AD each time I need data, but that means a lot of repeated AD gets and sets to the same records, and having to pause the script to allow time for the DC's to sync (or force all calls to a single member server)
    • If you can't explain it simply, you don't understand it well enough. Albert Einstein
    • An arrow can only be shot by pulling it backward. So when life is dragging you back with difficulties. It means that its going to launch you into something great. So just focus and keep aiming.
  • DevilWAHDevilWAH Member Posts: 2,997 ■■■■■■■■□□
    GSXR750K2 wrote: »
    wastedtime is right. Unless there is something else you want to do with the data other than view it (like pass it as one or more parameters for another cmdlet), there's no need to assign it to an object. You can, it just makes a little more work for you. :)

    This is exactly it, I am not simply doing a single operation but a lot of cross referencing and updating. working with the data direct in AD means 1000's and 1000's of separate get/set commands and runs the risk of data getting out of sync when a write to one DC is not synced before a new get request hits a different DC.

    IT is better to pull the entire HR database I am working with (or at least fiels / records I need) and the branch of AD users i need in to power shell do all the checking and cross referencing in the code and then do a single out put at the end.
    • If you can't explain it simply, you don't understand it well enough. Albert Einstein
    • An arrow can only be shot by pulling it backward. So when life is dragging you back with difficulties. It means that its going to launch you into something great. So just focus and keep aiming.
  • GSXR750K2GSXR750K2 Member Posts: 325 ■■■■□□□□□□
    Ah, I see now. Are your OUs set up in a way that you only need one or one at a time? You mentioned the "branch of AD users", so didn't know if that's what you were talking about.

    If so, you could use "SearchBase" parameter to specify an OU.

    $ADusers = Get-ADuser -Filter * -SearchBase "OU=<ou name>,DC=<contoso>,DC=<com>

    That would grab all of the users in a given OU. You could extend that to only include the fields you require.

    $ADusers = Get-ADuser -Filter * -SearchBase "OU=<ou name>,DC=<contoso>,DC=<com> | Select GivenName,Surname

    Then you could run a Compare-Object between the names (or whatever other information you need) in that variable against the information in the HR variable.

    Did that help any?

    Alternatively, Get-ADUser -Filter * will grab all AD users, saving you from doing thousands of separate gets, then you can filter, sort, and compare accordingly. Again, pipe Select for only the fields you need.
  • QordQord Senior Member Member Posts: 632 ■■■■□□□□□□
    For comparisons, I normally make two ordered lists and run them through compare-object. As an example, I had to sync our O365 distribution group memberships to AD, I used this to look at it first:
    Compare-Object $adlist $O365list -IncludeEqual
    
    DevilWAH wrote: »
    risk of data getting out of sync when a write to one DC is not synced before a new get request hits a different DC.
    You can force a query to a specific DC using the -server option, something like...
    get-aduser -server 10.10.10.8 -Filter * -SearchBase "OU=<ou name>,DC=<contoso>,DC=<com> | Select GivenName,Surname
    
  • wastedtimewastedtime Member Posts: 586 ■■■■□□□□□□
    I agree with NetworkNewb. A CSV and modifying it in excel would probably be the easiest.

    If this user modification is happening in an automated powershell script you could do a foreach through the adusers and add them to a hashtable. This would require you to walk through each user once. The rest would be hashtable lookups.

    $usertable = @{}
    Foreach ($aduser in $ADusers){
    usertable.Add($aduser.givenname +"." + "$aduser.surname, $aduser)
    }

    then you could fetch a user object by:
    $user = usertable."WHA.Devil"
  • blargoeblargoe Self-Described Huguenot NC, USAMember Posts: 4,174 ■■■■■■■■■□
    Qord wrote: »
    For comparisons, I normally make two ordered lists and run them through compare-object. As an example, I had to sync our O365 distribution group memberships to AD, I used this to look at it first:
    Compare-Object $adlist $O365list -IncludeEqual
    

    You can force a query to a specific DC using the -server option, something like...
    get-aduser -server 10.10.10.8 -Filter * -SearchBase "OU=<ou name>,DC=<contoso>,DC=<com> | Select GivenName,Surname
    

    I agree, compare-object is usually how I handle things like this.

    I would export get-aduser (appropriately filtered) to a CSV and tweak it to match the fields in the HR user file that you are interested in. Then import the fields back into Powershell, the users from AD into object a and HR into object b. You might need to massage the output further if you need a formal report.

    1. Users that appear in both.
    Compare-Object -ReferenceObject $a -DifferenceObject $b -ExcludeDifferent -IncludeEqual


    2. Users that only appear in the HR list
    Compare-Object -ReferenceObject $a -DifferenceObject $b | where { $_.sideindicator -eq "=> }


    3. Users that appear in only the AD list.
    Compare-Object -ReferenceObject $a -DifferenceObject $b | where { $_.sideindicator -eq "<=" }
    IT guy since 12/00

    Recent: 11/2019 - RHCSA (RHEL 7); 2/2019 - Updated VCP to 6.5 (just a few days before VMware discontinued the re-cert policy...)
    Working on: RHCE/Ansible
    Future: Probably continued Red Hat Immersion, Possibly VCAP Design, or maybe a completely different path. Depends on job demands...
  • DevilWAHDevilWAH Member Posts: 2,997 ■■■■■■■■□□
    Oh i have a script already that does
    Foreach ($userH in $hruserlist)
    {
    Foreach($userA in $adlist)
    {
    if((userH.firstname -like $userA.firstname) -and ($userH.surname -like $userA.surname)) 
    
    {
    Put some code here...... 
    }
    
    }
    
    }
    

    This works fine, but it is slow, as for each $hruserlist it potential loops through all the users in $adlsit

    You can do a search using arrays in power shell but it only seems to work on 1D arrays

    for example from

    https://technet.microsoft.com/en-us/library/ee692798.aspx


    $arrColors -contains "violet" will return true/false if "violet is in the arrayand $adlist -contains $UserH.firstname $userH.surname will instantly return a true / false value but I cant see how to return the item from the $adlist adlist is populated using get_aduser -filter {my filter} -search base <search based> so it is a array of objects.
    • If you can't explain it simply, you don't understand it well enough. Albert Einstein
    • An arrow can only be shot by pulling it backward. So when life is dragging you back with difficulties. It means that its going to launch you into something great. So just focus and keep aiming.
  • wastedtimewastedtime Member Posts: 586 ■■■■□□□□□□
    First my assumption is that no one AD user has the same first and surname values as another AD user (i.e. there are not two john smiths).
    This should export all of the users into those list. I am using a Mac so I don't know if it will work out of the box, I may have some syntax errors. This will only loop through the AD users once and the hrlist once. This should be much faster then doing a foreach loop inside a foreach loop.
    [FONT=&amp]
    #Create the AD user hashtable
    [/FONT][FONT=&amp]$usertable = @{}[/FONT]
    [FONT=&amp]Foreach ($aduser in $ADusers){[/FONT]
    [FONT=&amp] usertable.Add($aduser.firstname + "@" + $aduser.surname, $aduser)[/FONT]
    [FONT=&amp]}

    $onlyhrlist = @()
    $bothlist = @()

    #Distribute the values
    Foreach ($hruser in $hruserlist){
    $keyname = $usertable.'$hruser.firstname + "@" + $hruser.surname
    if ( $usertable.ContainsKey($keyname)) {
    $index = $bothlist.Add($usertable[$keyname])
    #do something here with the AD user in bothlist via $bothlist[$index]
    $usertable.Remove($keyname)
    } else {
    $index = $onlyhrlist.Add($hruser)
    [/FONT][FONT=&amp]#do something here with the HR user list via $onlyhrlist[$index][/FONT][FONT=&amp]
    }
    }

    [/FONT][FONT=&amp]#Export the list
    $onlyhrlist | Export-Csv -Path "hronly.csv"
    [/FONT][FONT=&amp]$usertable.Values | Export-Csv -Path "adonly.csv"
    [/FONT][FONT=&amp]$bothlist | Export-Csv -Path "both.csv"[/FONT]
    [FONT=&amp]
    [/FONT]
Sign In or Register to comment.