Creating Active Directory Users with ADSI and PowerShell

The Active Directory module from RSAT is the way to go but sometimes you may want a bit more control. You also may have a need to roll out your own tools. In this blog post I want give you some ideas on how to create user accounts using LDAP and ADSI.

The first thing you need is a ADSI reference to a organizational unit or container. For example:

[ADSI]$OU = "LDAP://OU=Users,OU=Horizon,DC=Horizon,DC=Local"

I this example we want to create a user account for a new hire, for example “Lex van der Horst”. We will do this with the Create() method on the OU object. This method needs an object type and a canonical name. Be sure to save the results to a variable because you will need it.

$new = $OU.Create("user","CN=L. van der Horst")

This account only exists locally. The first property to set is the account name. This is a new object, which means we need to use the Put() method.

$new.put("samaccountname","lvdhorst")

or

$new.InvokeSet("samaccountname","lexvdhorst")

We will setting additional properties, so we need to commit the account to Active Directory before we going further. We will do this with:

$new.setinfo()

The new account is disabled by default and does not have a password. Before proceed we set this things now.

$new.put("userAccountControl",544)

If you want to disable the account, set it to 546. In this example we define a new password and configure the account. The user will have to change the password at next login.

Quick Reference

This table provides a quick reference guide to common userAccountControl values. This is not intended to be a complete reference.

Value Description
512 Enabled Account
514 Disabled Account
544 Enabled, Password Not Required
546 Disabled, Password Not Required
66048 Enabled, Password Doesn’t Expire
66050 Disabled, Password Doesn’t Expire
66080 Enabled, Password Doesn’t Expire & Not Required
66082 Disabled, Password Doesn’t Expire & Not Required
262656 Enabled, Smartcard Required
262658 Disabled, Smartcard Required
262688 Enabled, Smartcard Required, Password Not Required
262690 Disabled, Smartcard Required, Password Not Required
328192 Enabled, Smartcard Required, Password Doesn’t Expire
328194 Disabled, Smartcard Required, Password Doesn’t Expire
328224 Enabled, Smartcard Required, Password Doesn’t Expire & Not Required
328226 Disabled, Smartcard Required, Password Doesn’t Expire & Not Required

Set a initial password:

$new.setpassword("P@ssw0rd")

Force change password at next logon:

$new.Put("pwdLastSet",0)

All of these changes are with the local cached copy of the user account. We will now set some additional user properties.

$new.put("UserPrincipalName","lexvdhorst@horizon.local")
$new.put("DisplayName","Lex van der Horst")
$new.Put("Department","IT")
$new.put("Title","PowerShell Specialist")
$new.put("GivenName","Lex")
$new.put("sn","van der Horst")
$new.put("company","Horizon")
$new.put("description","PowerShell Development Team")

The tricky part is figuring out the LDAP property name. Some of them are not obvious. One thing we can do is use the AttributeEditor on a user account in Active Directory Users and Computers. We started with a disabled dummy account and filled out all of the fields. Next, we used the Attribute Editor tab.

If you do not see this, make sure you have selected the Advanced Features under the View menu.

Do not forget to commit the changes with:

$new.setinfo()

As soon the replication is done, we can see the new account.

Deleting an account is also easy. Invoke the Delete method on the OU or container object:

$OU.Delete("user","CN=L. van der Horst")

Note: The deletion is immediate and there is no -WhatIf support.

We can do this also with a tooling like a function. We could write a version of New-ADUser customized to your environment with different parameters for the different user properties. For Example:

Function New-LDAPUser {
[cmdletbinding()]
Param(
[parameter(Position = 0, Mandatory)]
[ValidatePattern("^\w+\s\w+$")]
[string]$Name,
[string]$DefaultPassword = "P@ssw0rd",
[string]$OU = "OU=Users,OU=Horizon,DC=Horizon,DC=Local",
[hashtable]$Properties,
[switch]$Disable,
[switch]$Passthru
)

#try to get the OU
[ADSI]$Parent = "LDAP://$OU"

#verify the OU exists
if (-Not $parent.distinguishedname) {
    Write-Warning "Can't find  OU $OU"
    #bail out
    Return
}

#split name into two variables
$firstname,$lastname = $name.split()

#define samaccountname
$sam = "{0}{1}" -f $firstname[0],$lastname
Write-Verbose "Testing if $sam already exists in the domain"

#test if name already exists in the domain
[ADSI]$Test = "WinNT://$($env:userdomain)/$sam,user"
If ($test.ADSPath) {
    Write-Warning "A user with the account name of $sam already exists"
    #bail out
    Return
}

Write-Verbose "Creating new user $Name in $OU"

$new = $parent.Create("user","CN=$Name")
$new.put("samaccountname",$sam)
$new.setinfo()

Write-Verbose "Setting name properties"
$new.put("givenname",$firstname)
$new.put("sn",$lastname)
$new.put("userprincipalname","$sam@globomantics.local")
$new.put("Displayname",$name)

if ($hash) {
    Write-Verbose "Setting additional properties"
    foreach ($key in $hash.keys) {
        Write-Verbose "...$key"
        #verify property is valid
        Try {
           $new.invokeGet($key)
           $new.put($key,$hash.item($key))
        }
        Catch {
            Write-Warning "$key is not a valid property name"
        }
     }
}

Write-Verbose "set initial password"
$new.setpassword("P@ssw0rd")

Write-Verbose "force change at next logon"
$new.Put("pwdLastSet",0)

if ($Disable) {
    Write-Verbose "Disabling the account"
    $new.put("userAccountControl",546)
}
else {
    $new.put("userAccountControl",544)
}

Write-Verbose "committing changes"
$new.setinfo()

if ($Passthru) {
    $new.refreshcache()
    $new
}

} #end function

The PowerShell code above does not include support for WhatIf but we could add it. The function needs the distinguishedname for the parent container and the user name. I prever using simple names such as Frank Power. The samAccountname is derived from the first initial of both the first and last name.

The PowerShell code that creates it from the username, we split:

#split name into two variables and define samaccountname
$firstname,$lastname = $name.split()
$sam = "{0}{1}" -f $firstname[0],$lastname

You would need to define your own standard code. We have included some validation to ensure a valid OU without a naming conflict. This version simply reports any of these problems and bails out.

If the function is something that you wish to use regularly in your interactive PowerShell sessions then you can place the function in your PowerShell Profile and it will be available every time you open your PowerShell console.

If you are unsure what a PowerShell profile is or how to use one, there is some good info here. A quick way to create one is:

New-Item -Path $profile -ItemType File -Force

Once you have created a PowerShell profile, place the function in the profile and save and close. Now every time you open your PowerShell console the function will be available. To do this edit file Microsoft.PowerShell_profile.ps1 and copy the code here and save it.

The function uses the steps we outlined above to create and define a user account.

New-LDAPUser -Name "Frank Power" -Verbose -Disable -Passthru

The other feature in this function is that it accepts a hashtable of LDAP property names and values.

$hash=@{
Company = "Horizon"
Department = "IT"
Title = "PowerShell Specialist"
Description = "PowerShell Development Team"
PhysicalDeliveryOfficeName = "XX-0000"
phone = "x842"
}

The function processes this hashtable and sets the value for each entry, assuming it is valid. We use the InvokeGet() method, which will throw an error with an invalid property. We can create a richer user account.

New-LDAPUser -Name "Frank Power" -OU "OU=Users,OU=Horizon,DC=Horizon,DC=local" -Properties $hash -Passthru -Verbose

Understanding the ADSI basics can be very useful and it is not difficult to build custom AD tooling for your environment that does not rely on RSAT.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s