Hey, Scripting Guy! How Can I Create Users from a CSV File?

ScriptingGuy1

Hey, Scripting Guy! Question

Hey, Scripting Guy! In your last article, you showed how to create random passwords for users and how to write that password out to a CSV file. You said if anyone asked that you would show how to use that CSV file and create a user from it. Well, here I am, asking you. Show me the script. I’ll send chocolate.

– LZ

SpacerHey, Scripting Guy! Answer

Hi LZ,

Mmm…chocolate. I am glad you asked because it gives me a chance to create a really cool script. We are going to use two functions from our previous “Hey, Scripting Guy!” article: the CreatePassword function and the LtrFmAscii helper function that is used to get the letter equivalent from the ASCII integer value. We also need to create two more functions: one that will create a comma separated value (CSV) file that contains user names from a text file, as well as the newly created random passwords for each user. The next function we need to create is one that will read the CSV file and create the user accounts in Active Directory. Here is the complete script, which illustrates several important Windows PowerShell concepts such as creating temporary text files, renaming text files, using regular expressions, and a few others as well:

Param(
      $pwdPass = 2,
      $text = "C:\fso\users.txt"
      )
Function CreatePassword()
{ 
 for($i = 1 ; $i -le $pwdPass ; $i++)
 {
   LtrFmAscii($rnd.Next(33,47))
   LtrFmAscii($rnd.Next(48,57))
   LtrFmAscii($rnd.Next(65,90))
   LtrFmAscii($rnd.Next(97,122))
 }
} #end CreatePassword
Function LtrFmAscii($ascii)
{
  $script:password += [char]$ascii
} #End LtrFmAscii
Function CreateFile()
{
  $script:newText = "C:\fso\UsersAndPassword.csv"
  if(test-path -path $script:newText) { Remove-item -path $script:newtext }
  $tmpText = [io.path]::GetTempFileName()
  $content = "User,Password`r`n"
  Get-Content -path $text |
  Foreach-Object `
  {
   CreatePassword
   $content +=  "$_,`"$Script:password`"`r`n"
   $script:password = $null
  }
  Set-Content $tmpText -value $content 
  Move-Item -path $tmpText -destination $script:newText
} #end CreateFile
Function CreateUsers()
{
 $aryUser= import-csv -Path $script:newText
 $Class = "User"
 $dc = "dc=nwtraders,dc=com"
 foreach($strUser in $aryUser)
 {
  if($strUser.User -match "[a-zA-Z]")
   {
    $ou = "ou=myTestOu"
    $ADSI = [ADSI]"LDAP://$ou,$dc" 
    $cnuser="cn="+$($strUser.user)
    $User = $ADSI.create($Class,$cnuser)
    $User.put("SamaccountName", $($strUser.user))
    $User.setInfo()
    $User.put("userPassword", $($strUser.Password))
    $user.psbase.invokeset("AccountDisabled", "False")
    $User.setInfo()
   } #end if $strUser
 } #end foreach $struser
} #end createUser
# *** ENTRY TO SCRIPT ***
$script:password = $null
$rnd = new-object system.random
CreateFile
CreateUsers

Let’s begin by taking a look at the CreateFile function. The first thing we need to do is assign a string that represents the name and the path of the new file we will create. The new file will be named UsersAndPassword.csv and we store this information in the script-level variable newText. This is seen here:

$script:newText = "C:\fso\UsersAndPassword.csv"

As a convenience we check for the existence of the UsersAndPassword.csv file. If we find it, we delete the file. If you do not want this behavior, I would recommend you comment out the line of code by placing a # in front of the line of code. Here is the uncommented line:

if(test-path -path $script:newText) { Remove-item -path $script:newtext }

The next thing we do is create a temporary file to hold our user names and the associated passwords. We do this to keep from messing up the list of user names. The temporary file name is generated by using the static method GetTempFileName from the system.io.path .NET Framework class. Because we are using a static method, we do not need to create an instance of the system.io.path class (by using New-Object); rather, we can call the method directly by placing the class name in square brackets and using double colons to decorate the method name. In the code below, we do not include the word “system” because it is not required. With Windows PowerShell we assume .NET Framework class namespaces are preceded with “system.” This is seen here:

$tmpText = [io.path]::GetTempFileName()

We now use the variable $content to hold the header information for our CSV file. The end of the line is a carriage return and a line feed (equivalent to VBScript vbcrlf). This is seen here:

$content = "User,Password`r`n"

Now we need to read the text file containing the list of user names. We use the Get-Content cmdlet to do this. We then pass the user names into the pipeline one item at a time, and we use the ForEach-Object cmdlet to work with each item as it comes down the pipeline. The first thing we do is call the CreatePassword function. When we have the password, we append it to the $content variable, which already has our CSV header information. We use the += operator to append to the variable. The user name is stored in the automatic variable $_, which represents the current item on the pipeline. We build a string that contains the user name, a comma, and the password that is offset with quotation marks. To allow us to use quotation marks inside a string we need to escape them by using the back tick character. We then add a carriage return line feed to the end of the line by using `r`n. Note that it must be in this order. You can use a line feed carriage return `n`r, but it will not work the same way. The last thing we need to do is empty the script level $password variable. We do this by assigning it the $null value. This section of code is seen here:

Get-Content -path $text |
  Foreach-Object `
  {
   CreatePassword
   $content +=  "$_,`"$Script:password`"`r`n"
   $script:password = $null
  }

The last two things we need to do is use the Set-Content cmdlet to write the contents of the $content variable to the temporary text file represented by the $tmpText variable. Last, we need to rename the temporary file and move it to the location we specify in the newText variable. We do this by using the Move-Item cmdlet. There is a Rename-Item cmdlet, but it only allows you to rename items—it does not move items. The Move-Item cmdlet will allow you to rename and move at the same time and is what we choose to use here:

Set-Content $tmpText -value $content 
Move-Item -path $tmpText -destination $script:newText

Now we get to the CreateUser function. The first thing we do is use the Import-Csv cmdlet to import our newly created CSV file that is referenced by the script-level variable $newText. We store the array of items from the CSV file into the $aryUser variable. This is seen here:

$aryUser= import-csv -Path $script:newText

We now initialize a couple of local variables. The first one is used to specify the class of object we are going to create in Active Directory. We are going to create users, so we assign the string value “User” to the $Class variable. Now we assign a value for the domain we are going to work with. We are in the nwtraders domain, so we specify dc=nwtraders,dc=com. This section of code is seen here:

$Class = "User"
$dc = "dc=nwtraders,dc=com"

As we walk through the array of user names and passwords, we need to ensure the user name begins with a letter of some kind. To do this, we use the regular expression pattern a-zA-Z, which means any lowercase letter and any uppercase letter. This is shown here:

if($strUser.User -match "[a-zA-Z]")

Now we specify the organizational unit we are going to connect to and use the [adsi] type accelerator to connect to the organizational unit named ou=myTestOu in the dc=nwtraders,dc=com domain. We now need to prepend cn= to each user name, and when it is done, we call the Create method and give it the class of the object to create and the name of the object. We store the returned DirectoryEntry object in the $user variable, and use the Put method to assign the user name to the SamAccountName attribute in Active Directory. We then call the SetInfo method to write the information back to Active Directory. This is seen here:

$ou = "ou=myTestOu"
$ADSI = [ADSI]"LDAP://$ou,$dc" 
$cnuser="cn="+$($strUser.user)
$User = $ADSI.create($Class,$cnuser)
$User.put("SamaccountName", $($strUser.user))
$User.setInfo()

Now we need to set the password for our user object. We use the Put method to write the password from our CSV file into the userpassword attribute in Active Directory. Now we need to enable the user account. To do this, we use the InvokeSet method from the base powershell object. This methodology allows us to set the AccountDisabled value to False—which of course means the account is enabled. (I hate double negatives, but that is another story). This is seen here:

$User.put("userPassword", $($strUser.Password))
$user.psbase.invokeset("AccountDisabled", "False")
$User.setInfo()

Well, LZ, that is it. We read a text file containing only user names, and we used our CreatePassword function to create random passwords, and then we created a new CSV file containing both the user names and the passwords. We used the Import-Csv cmdlet and read the CSV file and created the users in Active Directory. We then set their passwords and enabled the accounts. All in all, a good day’s work. Later gator.

Ed Wilson and Craig Liebendorfer, Scripting Guys

0 comments

Discussion is closed.

Feedback usabilla icon