Exporting Users from AD OU then Adding Exported users to Security Group

This topic contains 3 replies, has 3 voices, and was last updated by Profile photo of Mike Camero Mike Camero 1 year, 2 months ago.

  • Author
  • #27935
    Profile photo of Mike Camero
    Mike Camero


    I've been going through multiple OUs in AD, exporting the users to CSV, then I've been placing those users in a security group in AD. Here is what I'm currently using to export:

    Get-ADUser -Filter * -SearchBase "OU=Test,OU=All_Users,DC=Test,DC=com" | Select-Object SamAccountName | Export-Csv 'C:\PS_Scripts\TestUsers.csv'

    Then, once I combine all the CSV files so they contain multiple users from multiple OUs I run this script (Thanks to Matt McNabb):

    Import-Module ActiveDirectory
    $ADGroup = "SCCM_AppCat_HR"
    $Members = Get-ADGroupMember -Identity $ADGroup
    $Users = Import-Csv "C:\PS_Scripts\TestUsers.csv"
    $Failures = @()
    $Added = @()
    $Existing = @()
    Foreach ($User in $Users)
         if ($User.SAMAccountName -in $Members.SAMAccountName)
             $Existing += $User
             try {
                 Add-ADGroupMember -Identity $ADGroup -Members $User.SAMAccountName
                 $Added += $User
             catch { $Failures += $User }
     $FailLog = 'C:\PS_Scripts\FailLog.csv'
     $AddedLog = 'C:\PS_Scripts\AddedLog.csv'
     $ExistingLog = 'C:\PS_Scripts\ExistingLog.csv'
     $ErrorLog = 'C:\PS_Scripts\ErrorLog.csv'
     if ($Failures)
         $Failures | Export-Csv $FailLog -NoTypeInformation
         $Error | Export-Csv $ErrorLog -NoTypeInformation
     if ($Added)
         $Added | Export-Csv $AddedLog -NoTypeInformation
     if ($Existing)
         $Existing | Export-Csv $ExistingLog -NoTypeInformation

    I'm fairly new to PowerShell and although this has helped me tremendously, I thought I would try to improve upon this by searching multiple OUs and attempting to export all users from multiple OUs at the same time and pipe to 1 CSV file.

    So, I tried this:

    Get-ADOrganizationalUnit -Filter 'Name -like "*ACD*"' | Format-List -Property Name, DistinguishedName

    This gives me a list of 20 different OUs and their 'path' if that is the correct term.

    Then I tried this:

    $OU = Get-ADOrganizationalUnit -Filter 'Name -like "*ACD*"'
    Get-ADUser -Filter * -SearchBase "$OU" | Select-Object SamAccountName | Export-csv 'C:\PS_Scripts\TestCSR.csv'

    Of course, this kicks back an error At line: 2 Char: 1

    Get-ADUser : The object name has bad syntax
    At line:2 char:1
    + Get-ADUser -Filter * -SearchBase "$OU" | Select-Object SamAccountName | Export-c ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : NotSpecified: (:) [Get-ADUser], ADException
        + FullyQualifiedErrorId : The object name has bad syntax,Microsoft.ActiveDirectory.Management.Commands.GetADUser

    Any help that can be given is much appreciated! Thank you!

  • #27938
    Profile photo of kvprasoon

    Hi Mike,

    $OU = Get-ADOrganizationalUnit -Filter 'Name -like "*ACD*"'
    # As you Can See
    gcm Get-ADUser -Syntax
    Get-ADUser -Filter  [-AuthType ] [-Credential ] [-Properties ] [-ResultPageSize ] [-ResultSetSize ] [-SearchBase 
    ] [-SearchScope ] [-Server ] []
    # Searchbase won't take Multiple inputs and Accepts only string Value,So make sure the $OU contains Only a string
    # And
    $OU = Get-ADOrganizationalUnit -Filter 'Name -like "*ACD*"'
    # SearchBase parameter needs Distinguished Name as its input
    # Try this and make sure it contains only one Value.
    $OU = Get-ADOrganizationalUnit -Filter 'Name -like "*ACD*"'|select -expandproperty DistinguishedName


  • #27946
    Profile photo of Rob Simmers
    Rob Simmers

    To answer your question about the OU, kvprasoon gave you part of the solution. If a parameter accepts a string array, it is indicated with a double square bracket (string[]). When you start writing Powershell functions, you will define your parameters and build foreach logic for those parameters because a user can pass an array of data. So, ForEach logic is built-in for the command.

    With that said, the -SearchBase parameter only accepts a string, so if you want to search multiple OU's, you need to add foreach logic to the data returned from Get-ADOrganizationUnit to run separate searches for each OU. In kvprasoon's post, Select -ExpandProperty DistinguishedName is extracting the property and creating a string array, but this is unnecessary because we can just pass the DistinguishedName property from the object.

    $OUs = Get-ADOrganizationalUnit -Filter 'Name -like "*ACD*"'
    foreach ($OU in $OUs) {
        Get-ADUser -Filter * -SearchBase $OU.DistinguishedName | Select-Object SamAccountName

    You are actually taking some of the functionality away using -ExpandProperty in this instance. Let's look at this snippet:

    $OUs = Get-ADOrganizationalUnit -Filter 'Name -like "*ACD*"'
    foreach ($OU in $OUs) {
        $users = Get-ADUser -Filter * -SearchBase $OU.DistinguishedName -Properties DisplayName | Select-Object DisplayName, SamAccountName
        foreach ($user in $users) {
            "{0} ({1})is in OU {2}" -f $user.DisplayName, $user.SamAccountName, $OU.Name

    Since we have the entire OU object, we can get other properties like Name from that object. If you don't care about additional OU information like the Name, ManagedBy, etc. and are strictly using it for searching, then you can use the -ExpandProperty to just return the distinguishedName.

    Lastly, your first chunk of code looks more like VBScript than Powershell using an array to indicate Added, Existing, etc., so let's look at more of a Powershell approach:

    #Using the code samples above, we would do a foreach logic for every OU
    #Anything that is returned in this loop, is saved to the $results variable
    $results = foreach ($OU in $OUs) {
        #The OU will contain multiple users, so we do a foreach user as well
        foreach ($User in $Users) {
             if ($User.SAMAccountName -in $Members.SAMAccountName) {        
                 $Status = "Existing"
             else {
                 try {
                     Add-ADGroupMember -Identity $ADGroup -Members $User.SAMAccountName
                     $Status = "Added"
                 catch { 
                    #It's not only import to catch the failure, but WHY it failed. We
                    #take the $_ which is what is "caught" and get the exception message
                    $Status = "Failed: {0}" -f $_.Exception.Message 
             #You are getting the same properties for each loop and we 
             #set status based on the logic above
             $props = @{
                DisplayName = $User.DisplayName;
                SamAccountName = $User.SamAccountName;
             #Create a new PSObject with the properties, which is returned to $results
             New-Object -TypeName PSObject -Property $props
         } #foreach user
    } #foreach OU

    Now, you are returning a PSObject. You can run queries against it and get reporting just by doing some Powershell commands:

    #Find all add operations that failed, but now we have the OU, SamAccountName and #DisplayName to assist with troubleshooting
    $results | Where{$_.Status -like 'Failed*'}
    #Show how many of each status there are in the results
    $results | Group-Object -Property Status -NoElement | Sort-Object -Property Count -Descending

    You can also put the results in s a CSV ($results | Export-CSV C:\MyCSV -NoTypeInformation) and then put it into Excel to look at, but you can see from the commands above you don't really need to do that unless you just want to do some basic reporting.

  • #27956
    Profile photo of Mike Camero
    Mike Camero

    Thank you Kvprasoon and Rob Simmers. I really appreciate your help and explanation! Now I just need to wrap my head around it all! 🙂

You must be logged in to reply to this topic.