I hope you have been enjoying our exploration of PowerShell Active Directory alternatives. Certainly, 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 your own tools. Today, I want to give you some ideas on how to create user accounts using LDAP and ADSI. If you are just jumping in, I recommend you take a few minutes to get caught up with the previous articles:

The first thing you will need is an ADSI reference to the organizational unit or container.



1 [ ADSI ] $OU = "LDAP://OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=Local"



I want to create a user account for a new hire, Ginger Snaps. I 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.



1 $new = $OU . Create ( "user" , "CN=Ginger Snaps" )



Right now, 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.



1 $new . put ( "samaccountname" , "gsnaps" )



You can also use the InvokeSet() method.



1 $new . InvokeSet ( "samaccountname" , "gsnaps" )



Even though I will be setting additional properties, I need to commit the account to Active Directory before going any further.



1 $new . setinfo ( )



Don’t worry. The account is disabled by default and does not have a password. So let’s set those things now.



1 $new . put ( "userAccountControl" , 544 )



If you want to disable the account, do not set it to this or 546. You can define a new password and configure the account. The user will have to change the password at next login.



1 2 3 4 5 #set initial password $new . setpassword ( [email protected]" ) #force change at next logon $new . Put ( "pwdLastSet" , 0 )



All of these changes are with the local cached copy of the user account. While I am at it, I might as well set some additional user properties.



1 2 3 4 5 6 7 8 $new . put ( "UserPrincipalName" , [email protected]" ) $new . put ( "DisplayName" , "Ginger Snaps" ) $new . Put ( "Department" , "IT" ) $new . put ( "Title" , "PowerShell Specialist" ) $new . put ( "GivenName" , "Ginger" ) $new . put ( "sn" , "Snaps" ) $new . put ( "company" , "Globomantics" ) $new . put ( "description" , "PowerShell/DevOps Team" )



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

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

Do not forget to commit the changes one more time.



1 $new . setinfo ( )



As soon as replication converges, I can see the new account.

Deleting an account is even easier. Invoke the Delete method on the OU or container object.



1 $OU . Delete ( "user" , "CN=Ginger Snaps" )



Be careful. The deletion is immediate and there is no -WhatIf support.

Of course, this would be much easier with some sort of tooling like a function. You could write a version of New-ADUser customized to your environment with different parameters for the different user properties. I wrote something a bit more flexible.



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 Function New-LDAPUser { [ cmdletbinding ( ) ] Param ( [ parameter ( Position = 0 , Mandatory ) ] [ ValidatePattern ( "^\w+\s\w+$" ) ] [ string ] $Name , [ string ] $DefaultPassword = [email protected]" , [ string ] $OU = "OU=Employees,DC=Globomantics,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" , [email protected]" ) $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 ( [email protected]" ) 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



This version does not include support for WhatIf but you could add it. The function needs the distinguishedname for the parent container and the user’s name. I am using simple names such as John Deere. The samAccountname is derived from the first initial of both the first and last name. I have a code that creates it from the username, which I have split.



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



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

Otherwise, the function uses the steps I outlined above to create and define a user account.

Here is the account in ADUC:

Subscribe to Petri Newsletters Office 365 Insider Our Petri Office 365 Insider is dedicated to sharing detailed knowledge from top Office 365 experts. Delivered once a month to your inbox. All Newsletters Petri.com may use your contact information to provide updates, offers and resources that may be of interest to you. You can unsubscribe at any time. To learn more about how we manage your data, you can read our Privacy Policy and Terms of Service. !Already a Petri.com member? Login here for 1-click registration.

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



1 2 3 4 5 6 7 8 $hash = @ { Company = "globomantics" Department = "IT" Title = "DevOps Engineer" Description = "Project Phoenix" PhysicalDeliveryOfficeName = "QK-456" phone = "x8732" }



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



1 New-LDAPUser -Name "Ken Dew" -OU "OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=local" -Properties $hash -Passthru -Verbose





Understanding the ADSI basics is key. It is not that difficult to build custom AD tooling for your environment that does not rely on RSAT. Next time, we will turn our attention to managing groups and group membership with ADSI and PowerShell.