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


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!

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


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.

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