Tuesday, 30 August 2011

Creating bulk Active Directory user and group accounts in PowerShell using Server 2008 R2 cmdlets

This one is a departure from my normal SharePoint-related articles. Back in the day, I maintained a few batch and VBS scripts that created multiple Active Directory users and groups from CSV files. These had obvious uses for creating test accounts on a development environment, but I also used them from time to time on customer production environments when it came to provisioning new user accounts on mass.

One of the new features introduced in Windows Server 2008 R2 was the inclusion of 76 cmdlets delivering extensive Active Directory management capabilities using PowerShell. The challenge was to upgrade my old batch files to PowerShell versions using these new cmdlets, building in the following features:

  • Create bulk user accounts from a CSV file, including specifying attributes and account options (e.g., change password at next logon)
  • Create bulk group accounts from a CSV file, specifying a description and group type (e.g., global, security)
  • Assign multiple users to groups as members using a CSV file
  • Exception handling so that the script would check first to see if the account or group member existed before attempting to make the change
  • To achieve this, I have created three PowerShell functions – one to create users, one to create groups, and a final one to read the CSV file, kick off one of the other two functions depending on the type of account to be created, and finally add users as group members. Once these functions are loaded in a PowerShell console, it only requires a single command to create either users or groups – along with an appropriate CSV file, of course.

    Rather than go through each stage of these functions in this article, I have instead decided to annotate the script at various points so that you see what is happening. To get the functions ready for use, save them into a PS1 file using notepad or your favourite script editor and load it from a PowerShell console on a Windows Server 2008 R2 domain controller.

    #Import the PowerShell module containing AD cmdlets
    Import-Module ActiveDirectory
       
    #Read a CSV file with the user or group details and create an account in AD for each entry
    Function Create-ADAccountsFromCSV {
        Param (
            [parameter(Mandatory=$true)][string]$CSVPath,
            [parameter(Mandatory=$true)][string]$Type,
            [parameter(Mandatory=$true)][string]$OrgUnit
            )
     
        if (($Type -ne "Group") -and ($Type -ne "User"))
        {
            Throw New-Object System.ArgumentException("Type parameter must be specified as either 'User' or 'Group'.")
        }
     
        #Read the CSV file
        $csvData = Import-CSV $CSVPath
        foreach ($line in $csvData) {
           
            #Create a hash table of the account details
            $accountTable = @{
                'givenName'=$line.FirstName
                'sn'= $line.LastName
                'displayName'= $line.DisplayName
                'sAMAccountName'= $line.sAMAccountName
                'password' = $line.Password
                'description' = $line.Description
                'ou' = $OrgUnit 
            }
                   
            if ($Type -eq "User")
            {
                #Call the function to create a user account
                CreateUser -AccountInfo $accountTable
            }
     
            if ($Type -eq "Group")
            {
                #Call the function to create a group account
                CreateGroup -AccountInfo $accountTable
               
                #Get new group
                $groupFilterString = "samAccountName -like `"" + $line.sAMAccountName + "`""
                $group = Get-ADGroup -Filter $groupFilterString
               
                #Walk through each member column associated with this group
                $memberColumnNumber = 1
                $memberColumn = "Member" + $memberColumnNumber
               
                #While a member column still exists, add the value to a group
                while ($line.$memberColumn)
                {
                    #Check if user is already a member of the group
                    $member = Get-ADGroupMember $group | where { $_.samAccountName -eq $line.$memberColumn }
                   
                    #If not already a member, add user to the group
                    if ($member -eq $null)
                    {
                        write-host "Adding" $line.$memberColumn "as a member to group" $group.Name
                        try
                        {
                            $userFilterString = "samAccountName -like `"" + $line.$memberColumn + "`""
                            $user = Get-ADUser -Filter $userFilterString
                            Add-ADGroupMember -Identity $group -Members $user
                        }
                        catch
                        {
                            write-host "There was a problem adding" $line.$memberColumn "as a member to group" $group.Name "-" $_ -ForegroundColor red
                        }
                    }
                    else
                    {
                        write-host "User" $line.$memberColumn "not added to group" $group.Name "as it is already a member" -ForegroundColor blue
                    }
                   
                    $memberColumnNumber = $memberColumnNumber + 1
                    $memberColumn = "Member" + $memberColumnNumber
                }
            }
        }
    }        

    #Create an Active Directory user
    Function CreateUser {
      Param($AccountInfo)
     
        try
        {
            #Check to see if the user already exists
            $userFilterString = "samAccountName -like `"" + $AccountInfo['sAMAccountName'] + "`""
            $user = Get-ADUser -Filter $userFilterString
           
            #If user not already created, create them
            if ($user -eq $null)
            {
                write-host "Creating user account:" $AccountInfo['sAMAccountName']
               
                #Create the user account object
                New-ADUser -SamAccountName $AccountInfo['sAMAccountName'] `
                           -Name $AccountInfo['displayName'] `
                           -DisplayName $AccountInfo['displayName'] `
                           -GivenName $AccountInfo['givenName'] `
                           -Surname $AccountInfo['sn'] `
                           -Path $AccountInfo['ou'] `
                           -ChangePasswordAtLogon $true `
                           -AccountPassword (ConvertTo-SecureString $AccountInfo['password'] -AsPlainText -Force) `
                           -Description $AccountInfo['description'] `
                           -Enabled $false
           
                #Set 'User must change password at next logon' to true after user has been created
                #For some reason, the option wasn't set during New-ADUser - could be a bug?
                $user = Get-ADUser -Filter $userFilterString
                Set-ADUser $user -ChangePasswordAtLogon $true          
            }
            else
            {
                write-host "User" $AccountInfo['sAMAccountName'] "not created as it already exists" -ForegroundColor blue
            }
        }
        catch
        {
            write-host "There was a problem creating the user" $AccountInfo['sAMAccountName'] "-" $_ -ForegroundColor red
        }
    }

    #Create an Active Directory group
    Function CreateGroup {
        Param($AccountInfo)
     
        try
        {
            #Check to see if the group already exists
            $groupFilterString = "samAccountName -like `"" + $AccountInfo['sAMAccountName'] + "`""
            $group = Get-ADGroup -Filter $groupFilterString
           
            if ($group -eq $null)
            {  
                write-host "Creating group account:" $AccountInfo['sAMAccountName']
               
                #Create the group account object
                New-ADGroup -SamAccountName $AccountInfo['sAMAccountName'] `
                            -Name $AccountInfo['sAMAccountName'] `
                            -Path $AccountInfo['ou'] `
                            -GroupScope Global `
                            -GroupCategory Security
            }
            else
            {
                write-host "Group" $AccountInfo['sAMAccountName'] "not created as it already exists" -ForegroundColor blue
            }
        }
        catch
        {
            write-host "There was a problem creating the group" $AccountInfo['sAMAccountName'] "-" $_ -ForegroundColor red
        }  
    }

    Creating users

    Once the functions have been loaded in a PowerShell console, you are ready to start creating user accounts from a CSV file. The header of the CSV file for this example should be, as follows:

    sAMAccountName,FirstName,LastName,DisplayName,Description,Password

    Something to note is that I have used a very limited number of attributes when creating a user account – for example, I haven’t specified an Office, Department, Phone Number, Address, etc. You can extend the script to include these by adding extra columns to the CSV file and adapting the CreateUser function in the script, using the appropriate parameters for the New-ADUser cmdlet, as specified on TechNet here.

    Once you have these columns populated with user accounts, save the CSV file to a local or network path. For this example, I am going to copy the file into C:\Scripts and call it UserAccounts.csv.

    You now need to decide which Organisational Unit (OU) to store the users in Active Directory. I could have added this information to an extra column in the CSV file and made a slight modification to the script, but I decided to use a single OU in the PowerShell command instead. You can always move them to a different OU afterwards using the Active Directory Users and Computers console, if required.

    To create my users in the OU “Staff” for the domain “acme.local”, I would need to use the following command:

    Create-ADAccountsFromCSV -CSVPath "C:\Scripts\UserAccounts.csv" -OrgUnit "OU=Staff,DC=acme,DC=local” -Type "User"

    The script will output operation progress to the console, including a warning when it discovers that an account already exists and will not attempt to create it.

    image

    Creating groups and assigning members

    The CSV file for adding groups should have the following header:

    sAMAccountName,Member1,Member2,Member3,Member4,Member5

    The first column should contain the group name, followed by a column for each group member. Note that although I have only included Member 1 to 5 in the example above, you can have as many members as you like by creating extra columns in the header for Member6, Member7, etc.

    As with adding users. there are also extra parameters available for the New-ADGroup cmdlet, as specified on TechNet here.

    Once you have the CSV file formatted with the correct groups and their members, save it to a local or network path for use with the script. For this example, I am going to copy the file into C:\Scripts and call it GroupAccounts.csv. To create my groups in the OU “Groups” for the domain “acme.local”, I would need to use the following command:

    Create-ADAccountsFromCSV -CSVPath "C:\Scripts\GroupAccounts.csv" -OrgUnit "OU=Groups,DC=acme,DC=local” -Type "Group"

    As with creating users, the script will output progress to the PowerShell console, including warnings when either a group already exists or a user is already a member of the group - in both cases it will ignore these operations and move on without making a change.

    image

    31 comments:

    1. I created the ad domain accounts but the users can't login to the SharePoint site using this format domain\user. It might be a permissions thing. If anyone has any ideas I would appreciate the help.

      Thanks!

      ReplyDelete
    2. If you used the script as is above, then the user accounts will be disabled by default. Either enable them using Active Directory Users and Computers, or re-provision with the -Enabled switch set to $true

      ReplyDelete
    3. Hi Phil,

      I've created a .ps1 file with your script, loaded it in powershell (.\CreateGroups.ps1) and it doesn't recognise Create-ADAccountsFromCSV.

      i.e.
      The term 'Create-ADAccountsFromCSV' is not recognized as the name of a cmdlet, function, script file, or operable progr
      am. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

      Am I doing something wrong?

      Cheers

      ReplyDelete
    4. Yes :-)

      If the script is located in the path you are residing in currently then type . .\CreateGroups.ps1 - that is [dot][space].\CreateGroups.ps1

      If you are not in the path currently, you can type . "c:\path\CreateGroups.ps1"

      See my article here on this: http://get-spscripts.com/2011/03/running-powershell-ps1-script-files-in.html. It applies to the standard PowerShell console as well as the SharePoint Management Shell.

      ReplyDelete
    5. Hi Phil,

      Thanks for sharing this; worked a treat.

      I ran into the issue of the accounts being disabled. Is there any reason you didn't include the -Enabled switch by default in the script? Is it recommended to enable them as a separate process?

      Cheers,

      Chris

      ReplyDelete
    6. Hi Chris, it looks as though the Enabled switch is in the script at the end of the New-ADUser command, unless there is a problem with the way I have implemented it or I have misunderstood you?

      Phil

      ReplyDelete
    7. Hi Phil,

      I am getting a problem creating the user - The search filter cannot be recognized using the following:

      Create-ADAccountsFromCSV -CSVPath "dir\to\file.csv" -OrgUnit "ou=year07,ou=students,dc=school,dc=local" -Type "User"

      Not sure if i'm doing anything wrong here :)

      ReplyDelete
      Replies
      1. CSV file must contains an header that can be decoded
        @echo sAMAccountName,FirstName,LastName,DisplayName,Description,Password >>
        users.csv

        Delete
    8. Hi Phil, can you post a sample csv file with the header?
      Thanks!

      ReplyDelete
    9. http://raghuitadmin.blogspot.in/2012/02/create-bulk-users-in-active-directory.html

      ReplyDelete
    10. Hi Phil,

      How much time should it take to load about 1,000,000 Users?
      I dont know if that times that im getting are reasonable(~24hrs)

      Thanks,
      Gil

      ReplyDelete
    11. I appreciate what you have done and it works great for me but I do have one question.

      I would like to make this a single executable application (primal script) or be able to run it as a windows task daily but I am having issues.

      I have all my additional parameters integrated in the script (path to CSV, OU Path Account type) but I cannot get it to run without typing in "Create-ADAccountsFromCSV" I have tried passing this as a parameter in the task scheduler no luck (it loads the AD module then disappears). I though that it is just calling the first function to create the users so I added it in the script under "Import-Module ActiveDirectory" no luck I get the following error "The term 'Create-ADAccountsFromCSV' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

      Is there a way I can just type in . 'C:\Scripts\AD_NewUsers.ps1 and have it execute?

      Thanks for any help.

      ReplyDelete
    12. You could try taking a look at these articles: http://get-spscripts.com/2011/01/running-sharepoint-powershell-script.html, http://get-spscripts.com/2011/03/running-powershell-ps1-script-files-in.html

      ReplyDelete
    13. Awesome thanks for the script and info but I do have one question.

      I modified it so the account are enabled when created by adding "-Enabled $true" everything seems to work accounts are created and enabled but you are unable to log into any of the accounts until you unlock them first (they don't say or show they are unlocked) I stumbled across this while trying to figure out why I could not log in with the accounts.

      Is this something that the module is calling or is there something I am missing and need to add so when the accounts are created they work?

      Thanks.

      ReplyDelete
    14. I have just tried it myself with the -Enabled $true option and it works ok, so not sure what this is, sorry

      ReplyDelete
    15. Question when I run the following script it error "The term 'CreateUser' is not recognized as a name of a cmdlet" but if I run it again right after in the same powershell window it works on the second time.

      How do I get it to work the first time

      Here is what I have and the modification changes I have made to the original.

      Import-Module ActiveDirectory
      #Read the CSV file
      $csvData = Import-CSV "C:\Scripts\Accounts\XXXX.csv"

      foreach ($line in $csvData) {
      #Create a hash table of the account details
      $accountTable = @{
      'GivenName'=$line.givenName
      'Sn'= $line.sn
      'DisplayName'= $line.displayName
      'SamAccountName'= $line.samAccountName
      'Password' = $line.password
      'Description' = $line.description
      'Department' = $line.department
      'Initials' = $line.initials
      'Office' = $line.physicalDeliveryOfficeName
      'UPN' = $line.userPrincipalName
      'ou' = $line.OU
      }

      #Call the function to create a user account
      CreateUser -AccountInfo $accountTable
      }

      #Create an Active Directory user
      Function CreateUser {
      Param($AccountInfo)

      The rest is the same from this function on down.

      Thanks for any input or help.

      ReplyDelete
    16. Phil, this is fantastic...exactly what I was looking for! I had a heck of a time getting it to install but eventually got it working. I changed -Enabled $false to $true and -ChangePasswordAtLogon $false to $true (since I need to manually configure all 37 accounts!), and breezed through creating all 37 accounts. Also created 26 groups and added (up to 11) members to all of them. Went like clockwork. Thank you so much!!

      In case anybody's still using 2008R2 and getting the "'Create-ADAccountsFromCSV' is not recognized" error like I was, here's what I had to do:

      Close PS
      Right-click the PS icon in the taskbar, and click "Import System Modules"
      type Set-ExecutionPolicy RemoteSigned in the PS window
      Go to the folder containing your .ps1 file, rt-click it and run in powershell
      Back in PS, run the Create-ADAccountsFromCSV commands
      Viola! Users, Groups, and Group Members!!

      ReplyDelete
    17. Hi Bill, thanks for the comments and extra info - much appreciated

      ReplyDelete
    18. does it create home directories and set their permissions as well?

      ReplyDelete
    19. for "does it create home directories and set their permissions as well?" no it don't please read the post from the start

      ReplyDelete
    20. Hi Phil,

      Your scripts work great for AD account. Is it possible to include the exchange mailbox creation in your scripts? Usually creating a new user and mailbox should occur at the same time.

      Thanks,

      ReplyDelete
    21. Phil,
      Thanks for the awesome script. How do I add user email address in this script?

      ReplyDelete
    22. I keep getting the same error with each user "There was a problem creating the user juan.palopolo - Directory object not found""

      ReplyDelete
    23. Kindly try ad manager tool, i am using this ,pretty well so only i suggest you guys.

      find here : http://adsysnet.com/downloads.aspx

      ReplyDelete
    24. الاول خدماتها تغطى جميع انحاء المملكة فهى افضل شركات التنظيف بجدة ومكة والرياض وينبع والاحساء والدمام نتميز باننا نوفر افضل العماله المدربة الماهرة نقدم تنظيف منازل وخزانات وبيوت وفلل وشقق ومجالس وسجاد وموكيت
      شركة تنظيف خزانات بجدة
      شركة تنظيف منازل بالدمام
      شركة نقل اثاث بينبع
      شركة تنظيف خزانات بينبع


      شركة تنظيف بمكة
      شركة تنظيف بالقطيف
      شركة تنظيف بالاحساء
      شركة شراء اثاث مستعمل بالرياض
      شركة نقل اثاث بجدة
      شركة تنظيف كنب بشرق الرياض
      مكافحة الصراصير
      تنتشر في السعودية عدة انواع من الصراصير كالصراصير الامريكية والالمانية والشرقية وتعد خطيره لانها تنقل الميكروبات وتسبب الامراض للانسان من هنا جاءت اهمية مكافحتها والتخلص منها نستخدم افضل مبيدات مكافحة الصراصير المثبت فعاليتها وتغطى خدماتنا كافة مناطق المملكة فالاول تشتهر بانها احسن شركة مكافحة صراصير بالرياض واحسن شركة مكافحة صراصير بمكة واحسن شركة مكافحة صراصير بجدة واحسن شركة مكافحة صراصير بالمدينة المنورة

      شركة مكافحة صراصير بالجبيل

      ReplyDelete