Don Jones


Creating a User
Missing Exchange?
Options, Options, Options

In the previous installment of this column, I created the basis for an automated user provisioning script. I created a function called ProvisionInputCSV, which is designed to read user information from a CSV file and return that information in the form of a hashtable. I took that approach because it allowed me to then create multiple other "import" functions—import from a database, import from a spreadsheet, or whatever—and have each one return an identical-looking hashtable. My actual provisioning function simply needs to accept that hashtable, which might contain elements for a user's first name, last name, city, department, and so forth.

In this month's column, I can begin using that hashtable to create a new, mailbox-enabled user in Active Directory (or a non-mailbox user, if you're not using Exchange Server 2007).

The basic framework of the provisioning function looks like this:

Function Provision { PROCESS { } }

I've already established that the function will receive a hashtable on the pipeline, which it will access via the special $_ variable within the PROCESS scriptblock. The hashtable will have keys that correspond to user attributes, and the values of those keys will contain each user's information. Some examples include:

  • $_['sn'] = "Jones"
  • $_['givenName'] = "Don"
  • $_['samAccountName'] = "donj"

The exact attributes available will depend on what you've put into your ProvisionInputCSV function or other import function.

Keep in mind that user provisioning typically involves several steps, such as creating an account, creating a folder, and so forth. It would be easy to stick all those tasks into my Provision function, but doing so would make the function pretty complicated—and more complicated means harder to debug and troubleshoot. Instead, I'll use the Provision function as a sort of master task list, breaking each task out into its own subsidiary function. In the end, the Provision function will look something like this:

Function Provision { PROCESS { CreateUser $_ CreateHomeFolder $_ AddToGroups $_ UpdateAttributes $_ } }

Windows PowerShell Q&A

Q I don't see a –computerName parameter on very many cmdlets. Can Windows PowerShell be used to manage remote computers?

A Windows PowerShell version 1 has limited remoting capabilities. The Get-WmiObject cmdlet offers, as you might have discovered, a -computerName parameter so that you can retrieve information from one or more remote computers, like so:

Get-WmiObject Win32_Service –computerName "localhost","server2","server3"

But that's about the only built-in remote management capability you'll find in the first version of Windows PowerShell.

However, other technologies, such as Exchange Server and Active Directory, are inherently "remoted"—that is, your client computer knows it isn't a mail server or a domain controller and will contact one as appropriate. So you don't actually need a -computerName parameter for commands that work with those technologies.

Each of the four subsidiary functions handles one of the major tasks that I outlined in the first part of this series, and each is being passed the full set of user information in the $_ variable. So there—my provisioning script is done!

Creating a User

Just kidding. Obviously I still need to write the real functionality, which lives in those four subsidiary functions. In this installment, I focus on the CreateUser function. The first iteration of this code assumes that you have Exchange Server 2007 in your environment. (Sorry, this won't work for older versions of Exchange Server.) Whatever computer you're using to run this script must have the Exchange Server management tools installed, and you'll need to make sure the Exchange Server snap-in for Windows PowerShell is loaded. To check, run Get-PSSnapin and see if it's listed. If it isn't, run Get-PSSnapin -registered to make sure it's installed. Then, to load the snap-in into the shell, run this:

Add-PSSnapin Microsoft.Exchange.Management.PowerShell.Admin.

If you get tired of typing that command, you can add it to the script that contains all the provisioning functions or you can add it to your Windows PowerShell profile to have it load when you open the shell. (I explained how you can create custom profiles in the October 2008 installment of this column, "The Power of Profiles."

Here's the basic framework for my function:

Function CreateUser { Param($userinfo) }

Notice that I've included a param block, which tells the shell what parameters I'm expecting. When I call this function, the hashtable I pass in will be automatically stored in the $userinfo variable.

Here's another way to write this:

Function CreateUser($userinfo) { }

This syntax has exactly the same effect. However, the shell uses the first version internally so I tend to use that version myself. I also think using the param block makes the function easier to read down the road.

Inside the function, I need to run only a single command: New-Mailbox. This actually does more than just create a new mailbox; it also creates a new user in Active Directory. I need to know a couple of pieces of information up front, including the mailbox database where I want the mailbox to be stored. The basic syntax looks like this (which is really all a long, single command line):

New-Mailbox –UserPrincipalName -alias DonJ –database "Storage Group 1\Mailbox Database 1" –name Don Jones –organizationalUnit Users –password $password –FirstName Don –LastName Jones –DisplayName "Don Jones" –ResetPasswordOnNextLogon $true

You can see that I need to come up with a password for the new user and decide what OU (organizational unit) to put the user into. (In this example, "Users" is technically a container, not an OU, but the command will accept either.) Assuming my ProvisionInputCSV function has been modified to produce everything I need, I can expect the $userinfo variable to contain the following ­minimum information:

  • UPN (user principal name)
  • OU (destination OU)
  • MailDatabase
  • givenName (first name)
  • sn (last name)
  • samAccountName (which I'll use as the 'alias')

That gives me the following function:

Function CreateUser { Param($userinfo) New-Mailbox –UserPrincipalName $userinfo['upn'] -alias $userinfo['samAccountName'] –database $userinfo['mailboxDatabase'] –name ($userinfo['givenName'] + ' ' + $userinfo['sn']) –organizationalUnit $userinfo['ou'] –password 'P@ssw0rd!' –FirstName ($userinfo['givenName'] –LastName $userinfo['sn']) –DisplayName ($userinfo['givenName'] + ' ' + $userinfo['sn']) –ResetPasswordOnNextLogon $true }

(Again, the command should be a single line, but I've broken it up here to make it easier to digest.) Combined with my Provision and ProvisionInputCSV functions, this function will create the new users and make them mailbox-enabled.

A few interesting things are worth noting here:

  • Nothing in the command is case sensitive, although Active Directory preserves whatever casing you use in things like names.
  • I'm giving the -name and -displayName parameters an expression as a value. I enclose the expression in (parentheses) to make sure the shell evaluates the expression and passes the results to the parameter. This technique lets me use the givenName and sn attributes to construct a full name.
  • I've hard-coded a temporary password rather than making one up. Making up passwords requires a way to communicate the passwords to users, and that's beyond the scope of this discussion.
  • The variable $true is built into the shell and represents the Boolean value True (the opposite of False).

Missing Exchange?

Windows PowerShell won't help in creating a mailbox-enabled user if you're not using Exchange Server 2007. However, you can still create the basic user account in Active Directory. To do so, you need the free Active Directory cmdlets for Windows PowerShell, which are available at Install those on the computer where you'll be running your script and add the snap-in to the shell by running this:

Add-PSSnapin Quest.ActiveRoles.ADManagement

Note that the snap-in name includes the name of a commercial Quest product (ActiveRoles Server), but you don't need that product in order to use the cmdlets in the snap-in.

You'll use the New-QADUser command, running something like this in the CreateUser function:

New-QADUser –samAccountName $userinfo['samAccountName'] –parentContainer $userinfo['OU'] –FirstName $userinfo['givenName'] –LastName $userinfo['sn'] –Name ($userinfo['givenName'] + ' ' + $userinfo['sn']) –UserPrincipalName $userinfo['UPN'] –displayName ($userinfo['givenName'] + ' ' + $userinfo['sn']) –userPassword "P@ssw0rd!" | Enable-QADUser

Note that I've piped the output of this command—which consists of the newly-created user—to a second command that enables the user account. You can decide whether that design is appropriate for your environment.

The New-QADUser command has dozens of parameters you can use to set additional Active Directory attributes, but I'm leaving this command as-is to make it more parallel with the Exchange Server version. I'll populate additional attributes in the last part of this script.

Options, Options, Options

Even if you run Exchange Server 2007, you might still prefer using New-QADUser to create user accounts. Doing so certainly gives you more flexibility, as this command can handle any directory attribute. And New-QADUser will make your script compatible with any environment, whether or not Exchange Server 2007 is in use.

If you have Exchange Server 2007, you could use an alternate syntax of New-Mailbox to create a mailbox and link it to the just-created Active Directory account. You'll find that Windows PowerShell typically offers several different ways to do something—the right approach is typically the one that works best for you with the least amount of learning involved.

Next month, I'll take this provisioning function a bit further by having it create home folders for users and apply the right Access Control Lists (ACLs) to those folders. ACLs are especially tricky to work with, and you might be surprised by the Windows PowerShell technique that can help you get the job done most easily.

Don Jones is a cofounder of Concentrated Technology, where he blogs weekly about Windows PowerShell, SQL Server, App-V, and other topics. Contact him through his Web site.