In the first parts of the series “.NET Assemblies In PowerShell”, I wrote about managing existing Active Directory groups and user accounts without the PowerShell ActiveDirectory module.
This time, I will show you how you can create new Active Directory objects with PowerShell by using the namespace System.DirectoryServices.AccountManagement. This namespace is available on any Windows computer by default.
As a prerequisite I recommend to read the first two parts of this series:
Part 1: Manage Active Directory groups
Part 2: Manage Active Directory group members and user accounts
If you want to skip this, that´s fine. Just know that the base of all this is the above mentioned namespace.
If you want to skip all the technical details and you are only here to grab some code, that´s fine too. In this case, you can just go to functions.
The name space
To add the namespace to your Powershell script, use the command:
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
Create a new Active Directory user account
To create a new account in Active Directory, we have to do two things in advance: Define the domain and set the organizational unit in which we want the new account to be created in.
Preparations
If we want to use the domain that we are currently logged on to, we can get it by using the GetCurrentDomain method of the System.DirectoryServices.ActiveDirectory.Domain class.
#Get the current domain $dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
This creates a new object of type Domain, from which we only need the name right now. Therefore, we´ll grab only the name property.
#store domain name $domname = $dom.name
If you don´t want the domain name to be set dynamically, you can just define a fixed name in the format “contoso.com”
#Set fixed domain name $domname = "lab.net"
For the organizational unit we just need to provide its distinguished name as a string.
#Define the OU in which the user account will be created $OU = "OU=Users,DC=lab,DC=net"
Next, we´re going to create a PrincipalContext object. This object sets the target location in Active Directory, where the new account will be inserted.
Create the object
The constructor for our purpose needs the parameters ContextType, Name and Container . The ContextType in this case is “Domain”. The parameter “Name” refers to the domain name and “Container” is the distinguished name of the organizational unit.
Since we already stored this info, we can use the domain name and organizational unit variables from before to create the object:
#Create the PrincipalContext object $pc = [System.DirectoryServices.AccountManagement.PrincipalContext]::` new([System.DirectoryServices.AccountManagement.ContextType]::Domain,$domname,$OU)
Because ContextType is just an enumerator with the values “Machine”, “Domain” and “ApplicationDirectory”, we can also just use the string “Domain” as the ContextType to simplify the lines:
#Create the PrincipalContext object $pc = [System.DirectoryServices.AccountManagement.PrincipalContext]::new("Domain",$domname,$OU)
Looks a little tidier, right?
Now, we are all set to create new Active Directory user accounts using our PrincipalContext object.
First, we initialize an object of type UserPrincipal, while passing the PrincipalContext object as a parameter to the constructor.
#Initialize a new UserPrincipal object with the PrincipalContext from before $user = [System.DirectoryServices.AccountManagement.UserPrincipal]::new($pc)
We now basically have an empty user account object, besides domain and organizational attributes.
#Check the new user object PS C:\windows\system32> $user GivenName : MiddleName : Surname : EmailAddress : VoiceTelephoneNumber : EmployeeId : AdvancedSearchFilter : System.DirectoryServices.AccountManagement.AdvancedFilters Enabled : AccountLockoutTime : LastLogon : PermittedWorkstations : PermittedLogonTimes : AccountExpirationDate : SmartcardLogonRequired : False DelegationPermitted : False BadLogonCount : 0 HomeDirectory : HomeDrive : ScriptPath : LastPasswordSet : LastBadPasswordAttempt : PasswordNotRequired : False PasswordNeverExpires : False UserCannotChangePassword : False AllowReversiblePasswordEncryption : False Certificates : {} Context : System.DirectoryServices.AccountManagement.PrincipalContext ContextType : Domain Description : DisplayName : SamAccountName : UserPrincipalName : Sid : Guid : DistinguishedName : StructuralObjectClass : Name :
We can see that everything is empty, except of the domain name and the container in the context property:
PS C:\windows\system32> $user.Context ContextType : Domain Name : lab.net Container : OU=USERS,DC=lab,DC=net UserName : Options : Negotiate, Signing, Sealing ConnectedServer :
Now, let´s give this user some attributes.
Fill the attributes
The attributes are accessible as properties of the UserPrincipal object through dot notation.
#Set some attributes for the account $user.Surname = "User123" $user.GivenName = "Test" $user.DisplayName = "Test User123" $user.Name = "Test User123" $user.Description = "Test account MSB365" $user.UserPrincipalName = "[email protected]" $user.SamAccountName = "test.user123" $user.SetPassword("P@ssW0rd1234")
By default the user is disabled. So, don´t forget to set the “enabled” attribute to true:
#Enable the user $user.Enabled = $true
The option “User must change password at next logon” will automatically be set.
Finalize
If we are sure, that the user account is set with everything it needs, we can save it. This will actually create it in our Active Directory.
#Save the user to AD $user.Save()
If we want to verify the result, we can just search the account in Active Directory:
# Find the user in AD by SAMAccountName # using the PrincipalContext object from before and the SAMAccountname of the new account PS C:\windows\system32> [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($pc,"test.user123") GivenName : Test MiddleName : Surname : User123 EmailAddress : VoiceTelephoneNumber : EmployeeId : AdvancedSearchFilter : System.DirectoryServices.AccountManagement.AdvancedFilters Enabled : False AccountLockoutTime : LastLogon : PermittedWorkstations : {} PermittedLogonTimes : AccountExpirationDate : SmartcardLogonRequired : False DelegationPermitted : True BadLogonCount : 0 HomeDirectory : HomeDrive : ScriptPath : LastPasswordSet : LastBadPasswordAttempt : PasswordNotRequired : True PasswordNeverExpires : False UserCannotChangePassword : False AllowReversiblePasswordEncryption : False Certificates : {} Context : System.DirectoryServices.AccountManagement.PrincipalContext ContextType : Domain Description : Test account MSB365 DisplayName : Test User123 SamAccountName : test.user123 UserPrincipalName : [email protected] Sid : S-1-5-21-4025995969-482537669-3923321509-xxxx Guid : 79556f9a-9af5-4fff-8cee-062731a91b03 DistinguishedName : CN=Test User123,CN=Users,DC=lab,DC=net StructuralObjectClass : user Name : Test User123
There you go. We created a new Active Directory user account. Now, let´s create an Active Directory group.
Create a new Active Directory group
Preparations
The preparation here is the same as for creating Active Directory accounts. We need to create the PrincipalContext object with our domain name and organizational unit in which the group should be created.
#Get the current domain $dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() #store the domain name $domname = $dom.name
Set the path of the organizational unit :
#Define the OU in which the group will be created $OU = "OU=Groups,DC=lab,DC=net"
Create the object
Here, instead of initializing an UserPrincipal object, we create a GroupPrincipal object:
#Initialize a new GroupPrincipal object with the PrincipalContext from before $group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::new($pc)
And again, we have a nearly empty object, which is waiting to be filled with attributes. Let´s not let it wait any longer…
Fill the attributes
#Since I´ll use the group name for multiple attributes I just store it to a variable $groupname = "test-group-MSB365_001" #Set the groups attributes $group.Description = "Test group MSB365" $group.SamAccountName = $groupname $group.DisplayName = $groupname $group.GroupScope = "Global" #Valid values are: "Global", "Local", "Universal" $group.IsSecurityGroup = $true
Finalize
And with all necessary attributes set, we can save the group to Active Directory:
#Save the group to AD $group.Save()
Let´s check the new group and its attributes:
PS C:\windows\system32> [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($pc, $groupname) IsSecurityGroup : True GroupScope : Global Members : {} Context : System.DirectoryServices.AccountManagement.PrincipalContext ContextType : Domain Description : Test group MSB365 DisplayName : test-group-MSB365_001 SamAccountName : test-group-MSB365_001 UserPrincipalName : Sid : S-1-5-21-4025995969-482537669-3923321509-xxxxx Guid : 267c6d7e-5981-4169-8c65-10e8cc3cc45a DistinguishedName : CN=test-group-MSB365_001,OU=GROUPS,DC=lab,DC=net StructuralObjectClass : group Name : test-group-MSB365_001
And there´s our group.
If you want to know how to add members to our new group, you can read the how-to in part 1 of this series.
Extended properties
The UserPrincipal and the GroupPrincipal classes might not provide all attributes which we want to set on a new Active Directory object. For instance the “mail” attribute is not directly accessible. To set values for those attributes, we can access the underlying Active Directory object, after we have saved the PrincipalObject.
To do so, we use the GetUnderlyingObject method of the PrincipalObject. Here´s an example how to add an e-mail address to the “mail” attribute of our new group.
#Get underlying AD object $adobject = $group.GetUnderlyingObject()
This will get us to the DirectoryEntry object through which we can access the Active Directory object more directly. To get a list of all its properties and their values, we can access its property Properties. Properties will give us back a PropertyCollection object.
#Get current properties $adobject.Properties #Get only the property names PS C:\Windows\system32>$adobject.Properties.PropertyNames objectClass cn distinguishedName instanceType whenCreated whenChanged displayName uSNCreated uSNChanged nTSecurityDescriptor name objectGUID objectSid sAMAccountName sAMAccountType groupType objectCategory dSCorePropagationData
We can see, that the “mail” attribute is not in the list. To add it, we use the Add method, while indexing the property by name:
PS C:\Windows\system32> $adobject.Properties["mail"].Add("[email protected]") 0
The return code “0” tells us that everything went fine and the attribute could be set.
As last step, we have to save the object by calling the method CommitChanges:
PS C:\Windows\system32> $adobject.CommitChanges()
When we check in Active Directory now, we can see the group exists and that it has an e-mail address:
This works the same way for user accounts, too. The attribute just has to be present in the Active Directory schema.
As functions
Create an Active Directory user account
Function cCreate-ADUser { Param( [Parameter(Mandatory=$true, ValueFromPipeline=$true, HelpMessage="Enter surname for the new user")] [String] $surname, [Parameter(Mandatory=$true, ValueFromPipeline=$true, HelpMessage="Enter givenname for the new user")] [String] $givenname, [Parameter(Mandatory=$false, ValueFromPipeline=$true, HelpMessage="Enter a description for the new user")] [String] $description, [Parameter(Mandatory=$false, ValueFromPipeline=$true, HelpMessage="Enter a initial password for the new user. Default password will be used if left blank.")] [String] $InitialPW ) #use default password as initial password or use provided if ($InitialPW){ $password = $InitialPW }Else{ $password = "P@ssW0rd1234" } #Add the assembly Add-Type -AssemblyName System.DirectoryServices.AccountManagement #Get the current domain $dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() #Set domain name $domname = $dom.Name #Define the container in which the user account will be created $OU = "OU=Users," + $dom.GetDirectoryEntry().distinguishedname #Create the Principalcontext object $pc = [System.DirectoryServices.AccountManagement.PrincipalContext]::new( ` [System.DirectoryServices.AccountManagement.ContextType]::Domain,$domname,$OU) #Initialize the new UserPrincipal object with the Principalcontext from before $user = [System.DirectoryServices.AccountManagement.UserPrincipal]::new($pc) #Set the attributes of the user $user.Surname = $surname $user.GivenName = $givenname $user.DisplayName = $givenname + " " + $surname $user.Name = $givenname + " " + $surname $user.Description = $description $user.UserPrincipalName = $givenname + "." + $surname + "@" + $domname $user.SamAccountName = $givenname + "." + $surname $user.SetPassword($password) #Enable the user $user.Enabled = $true #Save the user to AD $user.Save() #clean up $user.Dispose() $pc.Dispose() $dom.Dispose() Return "Done" }
The function will create a new Active Directory user with surname, given name and description provided in the parameters. As SAMaccountName and UserPrincipalName it will use a combination of surname and givenname.
You can set your own initial password through the parameter “InitialPW” as plain text string. If this parameter is left empty, the function will set a predefined password.
Here´s an example how to create an account with the function, using a specific initial password:
PS C:\Windows\system32> cCreate-ADUser -surname TestMSB365 -givenname User01 -description "test user MSB365" -InitialPW "W€lc0me2021!"
Create an Active Directory group
Function cCreate-ADGroup { Param( [Parameter(Mandatory=$true, ValueFromPipeline=$true, HelpMessage="Enter a name for the new group")] [String] $groupname, [Parameter(Mandatory=$true, ValueFromPipeline=$true, HelpMessage="Enter a description for the new group")] [String] $description, [Parameter(Mandatory=$false, ValueFromPipeline=$true, HelpMessage="Enter a group scope for the new group")] [ValidateSet("Global", "Local", "Universal")] [String] $Scope = "Global", [Parameter(Mandatory=$false, ValueFromPipeline=$true, HelpMessage="Choose if group is security group. If not set a distribution group will be created")] [Switch] $IsSecurityGroup, [Parameter(Mandatory=$false, ValueFromPipeline=$true, HelpMessage="Only for distribution groups: Enter an e-mail address for the new group. If not specified groupname@domainname will be used.")] [String] $mail ) #Add the assembly Add-Type -AssemblyName System.DirectoryServices.AccountManagement #Get the current domain $dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() #Set domain name $domname = $dom.Name #Define the container in which the group will be created $OU = "OU=Groups," + $dom.GetDirectoryEntry().distinguishedname #Create the Principalcontext object $pc = [System.DirectoryServices.AccountManagement.PrincipalContext]::new( ` [System.DirectoryServices.AccountManagement.ContextType]::Domain,$domname,$OU) #Initialize the new GroupPrincipal object with the PrincipalContext from before $group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::new($pc) #Set the attributes of the group $group.DisplayName = $groupname $group.Description = $description $group.SamAccountName = $groupname $group.IsSecurityGroup = $IsSecurityGroup.IsPresent $group.GroupScope = $Scope #Save the group to AD $group.Save() #Set e-mail address if group type is distribution group if (!$IsSecurityGroup){ #Generate e-mail address if none provided If (!$mail){ $mail = $groupname + "@" + $domname } #get underlying AD object $adobject = $group.GetUnderlyingObject() #Add mail property and save $result = $adobject.Properties["mail"].Add($mail) $adobject.CommitChanges() } #clean up $group.Dispose() $pc.Dispose() $dom.Dispose() Return "Done" }
This function creates an Active Directory group, while using the value in the parameter “Groupname” as SAMaccountName and DisplayName. If the switch parameter “IsSecurityGroup” is not set a distribution group will be created. Then, you can also provide an e-mail address. If none is provided, the function will build one using the group name and the domain name of the current domain.
Here´s an example how to create a distribution group with a specified mail address:
cCreate-ADGroup -groupname testgroup-msb365_002 -description "test distgrp MSB365" -Scope Universal -mail "[email protected]"
And here´s how to create a simple global security group:
PS C:\Windows\system32> cCreate-ADGroup -groupname testgroup-msb365_003 -description "test sec group global MSB365" -Scope Global -IsSecurityGroup
Further info
The “System.DirectoryServices.AccountManagement” namespace
The Add-Type cmdlet