This blog has moved to http://ThePowerShellGuy.com
Greetings /\/\o\/\/
This is the third part in the PowerShell Active directory series,in this part we are going to do the rest of the work to completely configure a User.
*Warning* As you have RC2 Installed all my AD posts will not work anymore, it is verry confusing, but fixable if you read explaination here PowerShell RC2 and Active Directory :
As I did switch back on all my work PC's (I did call all my colleagues not to upgrade as all our AD tools are messed up, I will not update them, as I would not recommend to upgrade to RC2 of you work with AD a lot.
Part 1 and 2 you can find here :
PowerShel and Active Directory Part 1PowerShell and Active Directory Part 2We did create a bounce of users last time, but as I did say in last post we are not really ready yet with the users created, as we want also to fill in some more properties and enable them, you can take a user created in last post or as here start with a Fresh one.
# Make a new User called Test
$mowOu = New-Object DirectoryServices.DirectoryEntry("LDAP://OU=MowOu,DC=mow,DC=local")
$user = $mowOu.get_Children().Add("CN=Test",'user')
$user.CommitChanges()
# Or use an existing User :
PoSH>$user = $mowou.get_Children().find('cn=test')
PoSH>$user
distinguishedName
-----------------
{CN=test,OU=MowOu,DC=mow,DC=local}
# Or directly again :
$User = New-Object DirectoryServices.DirectoryEntry("LDAP://cn=test,OU=MowOu,DC=mow,DC=local")
# Remove it and make a new one
PoSH>$mowou.get_Children().remove($user)
PoSH>$user = $mowou.get_Children().add("cn=Test",'user')
PoSH>$user.CommitChanges()
# list all the properties :
PoSH>$user | fl *
objectClass : {top, person, organizationalPerson, user}
cn : {Test}
distinguishedName : {CN=Test,OU=MowOu,DC=mow,DC=local}
instanceType : {4}
whenCreated : {7/2/2006 2:10:58 PM}
whenChanged : {7/2/2006 2:10:58 PM}
uSNCreated : {System.__ComObject}
uSNChanged : {System.__ComObject}
name : {Test}
objectGUID : {4.0.208.232.208.190.162.64.184.228.97.225.109.37.99.125}
userAccountControl : {546}
badPwdCount : {0}
codePage : {0}
countryCode : {0}
badPasswordTime : {System.__ComObject}
lastLogoff : {System.__ComObject}
lastLogon : {System.__ComObject}
pwdLastSet : {System.__ComObject}
primaryGroupID : {513}
objectSid : {1.5.0.0.0.0.0.5.21.0.0.0.94.172.186.232.167.29.117.70.12.60.36.84.61.12.0.0}
accountExpires : {System.__ComObject}
logonCount : {0}
sAMAccountName : {$T13000-LM8U3IDDHKKL}
sAMAccountType : {805306368}
objectCategory : {CN=Person,CN=Schema,CN=Configuration,DC=mow,DC=local}
nTSecurityDescriptor : {System.__ComObject}
After this refresh of things done in former posts and now we have a situation with a Fresh User again, we can go further adding the properties to configure the User compleetly, let's start with that SAMAccountName that is auto generated,
sAMAccountName : {$T13000-LM8U3IDDHKKL}as we still do need it a lot in day to day work and such a generated name is not handy, so we want it to be the same as the CN lets rename it :
(note this first part does not work for ADAM users they need to use the Native Methods here, more info later in this post.).# Change sAMAccountName
PoSH>$user.sAMAccountName = 'test'
PoSH>$user.sAMAccountName
test
Note that we do not have to call Commit Changes here, this will be done automatically the .NET DirectoryEntry wrapper-object will handle this.
Now Lets start with adding some more properties :
# Add a Property that does not exist (AD only)
$user.Department = 'foo'
PoSH>$user | fl *
objectClass : {top, person, organizationalPerson, user}
cn : {Test}
distinguishedName : {CN=Test,OU=MowOu,DC=mow,DC=local}
instanceType : {4}
whenCreated : {7/2/2006 2:10:58 PM}
whenChanged : {7/2/2006 2:12:34 PM}
uSNCreated : {System.__ComObject}
uSNChanged : {System.__ComObject}
department : {Foo}
......
Now you can see that, even when the property was not there before, It just got created and The Object saved again.
Now as I noted before the examples above, these will not work for ADAM users, you can still do it, but need to use Invoke or InvokeSet here as I will show below.:
# ADAM example
PoSH>$u.department = 'foo'
Exception setting "department": "The specified directory service attribute or value does not
exist. (Exception from HRESULT: 0x8007200A)"
At line:1 char:4
+ $u.d <<<< epartment = 'foo'
# Using Invoke
PoSH>$u.department
PoSH>$u.InvokeSet('department','foo')
PoSH>$u | fl *
objectClass : {top, person, organizationalPerson, user}
cn : {test}
distinguishedName : {CN=test,OU=MowOu,DC=mow,DC=adam}
instanceType : {4}
whenCreated : {6/28/2006 7:45:25 PM}
whenChanged : {6/28/2006 7:45:25 PM}
uSNCreated : {System.__ComObject}
uSNChanged : {System.__ComObject}
name : {test}
objectGUID : {0.18.124.80.174.38.202.68.189.94.92.39.181.158.201.85}
badPwdCount : {0}
badPasswordTime : {System.__ComObject}
pwdLastSet : {System.__ComObject}
objectSid :
{1.5.0.0.18.108.99.8.192.67.92.229.143.126.164.172.231.8.44.75.160.22.205.75.212.176.144.138}
objectCategory : {CN=Person,CN=Schema,CN=Configuration,CN={95B8575E-7CB6-4F6F-8F90-
85B1EBE795FF}}
nTSecurityDescriptor : {System.__ComObject}
# We have to Manualy save here :
PoSH>$u.CommitChanges()
PoSH>$u | fl *
department : {foo}
objectClass : {top, person, organizationalPerson, user}
cn : {test}
distinguishedName : {CN=test,OU=MowOu,DC=mow,DC=adam}
instanceType : {4}
whenCreated : {6/28/2006 7:45:25 PM}
whenChanged : {6/28/2006 10:02:26 PM}
uSNCreated : {System.__ComObject}
uSNChanged : {System.__ComObject}
name : {test}
objectGUID : {0.18.124.80.174.38.202.68.189.94.92.39.181.158.201.85}
badPwdCount : {0}
badPasswordTime : {System.__ComObject}
pwdLastSet : {System.__ComObject}
objectSid :
{1.5.0.0.18.108.99.8.192.67.92.229.143.126.164.172.231.8.44.75.160.22.205.75.212.176.144.138}
objectCategory : {CN=Person,CN=Schema,CN=Configuration,CN={95B8575E-7CB6-4F6F-8F90-
85B1EBE795FF}}
nTSecurityDescriptor : {System.__ComObject}
# Using invoke
PoSH>$u.invoke("Put",("displayName","test"))
PoSH>$u.CommitChanges()
PoSH>$u.displayName
test
# after it is created we can directly change it in ADAM
PoSH>$u.displayName = 'Testing'
PoSH>$u.displayName
Testing
This also works in an Active Directory environment ofcourse and as we shall see later in this post, there are also uses for those InvokeGet and InvokeSet methods in Active Directory use. Also when we Enable the account and set terminal serversettings, but first lets find out what properties are available.As we are busy with a User let's find out what properties are available. We will look this up in the Active Directory Schema.For this we need another .NET class, now from the System.DirectoryServices.ActiveDirectory NameSpace the ActiveDirectorySchema Class, and as a lot of the classes in the ActiveDirectory Namespace do, it does not have a constructor (see outputbelow), we have to use a static method .
# does not work :
PoSH>New-Object System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema
New-Object : Constructor not found. Cannot find an appropriate constructor for type
System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema.
At line:1 char:11
+ New-Object <<<< System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema
# it has no constuctors :
PoSH>[System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema].GetConstructors()
# listing the static Methods :
PoSH>[System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema] | gm -static
TypeName: System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema
Name MemberType Definition
---- ---------- ----------
Equals Method static System.Boolean Equals(Object objA, Object objB)
GetCurrentSchema Method static System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema GetCurrentSchema()
GetSchema Method static System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema GetSchema(DirectoryContextcontext)
ReferenceEquals Method static System.Boolean ReferenceEquals(Object objA, Object objB)
# using the static GetCurrentSchema() method
$schema = [System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema]::GetCurrentSchema()
Note that static methods get called with ::,and as you use get-member without the -static switch you get the Methods of the RunTime Object, (for example the get_constructors Method is very handy, but more about that in later post)
also note that this is a long type, for that I made an extension to the Tabcompletion funtion that makes it a lot easier to work with .net types directly from powershell as you could see in the Demo, you can find it here (http://mow001.blogspot.com/2006/06/powershell-tab-completion-part-4.html) but still making a function for it is more easy, so lets turn this in to a small function :
# Shortcut function for getting Schema Object
function get-Schema [System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema]::GetCurrentSchema()}
# Get the Schema
$schema = get-Schema
# ask what it can do for us .
PoSH>$Schema | gm
TypeName: System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema
Name MemberType Definition
---- ---------- ----------
Dispose Method System.Void Dispose()
Equals Method System.Boolean Equals(Object obj)
FindAllClasses Method System.DirectoryServices.ActiveDirectory.ReadOnlyActiveDirectorySchemaClassCollectio
FindAllDefunctClasses Method System.DirectoryServices.ActiveDirectory.ReadOnlyActiveDirectorySchemaClassCollectio
FindAllDefunctProperties Method System.DirectoryServices.ActiveDirectory.ReadOnlyActiveDirectorySchemaPropertyCollec
FindAllProperties Method System.DirectoryServices.ActiveDirectory.ReadOnlyActiveDirectorySchemaPropertyCollec
FindClass Method System.DirectoryServices.ActiveDirectory.ActiveDirectorySchemaClass FindClass(String
FindDefunctClass Method System.DirectoryServices.ActiveDirectory.ActiveDirectorySchemaClass FindDefunctClass
FindDefunctProperty Method System.DirectoryServices.ActiveDirectory.ActiveDirectorySchemaProperty FindDefunctPr
FindProperty Method System.DirectoryServices.ActiveDirectory.ActiveDirectorySchemaProperty FindProperty(
get_Name Method System.String get_Name()
get_SchemaRoleOwner Method System.DirectoryServices.ActiveDirectory.DirectoryServer get_SchemaRoleOwner()
GetDirectoryEntry Method System.DirectoryServices.DirectoryEntry GetDirectoryEntry()
GetHashCode Method System.Int32 GetHashCode()
GetType Method System.Type GetType()
RefreshSchema Method System.Void RefreshSchema()
ToString Method System.String ToString()
Name Property System.String Name {get;}
SchemaRoleOwner Property System.DirectoryServices.ActiveDirectory.DirectoryServer SchemaRoleOwner {get;}
# Getting the Schema of a user :
PoSH>$SchemaUser = $schema.FindClass('user')
PoSH>$SchemaUser.get_MandatoryProperties() | fl name
Name : cn
Name : instanceType
Name : nTSecurityDescriptor
Name : objectCategory
Name : objectClass
Name : objectSid
Name : sAMAccountName
# Get All Optional Properties of the User Class
PoSH>$SchemaUser.get_OptionalProperties() | ft name,Syntax,IsSingleValued,IsIndexed,IsInGlobalCatalog
Name Syntax IsSingleValued IsIndexed IsInGlobalCatalog
---- ------ -------------- --------- -----------------
accountExpires Int64 True False False
accountNameHistory DirectoryString False False False
aCSPolicyName DirectoryString True False False
adminCount Int True False False
adminDescription DirectoryString True False False
adminDisplayName DirectoryString True False False
allowedAttributes Oid False False False
allowedAttributesEff... Oid False False False
allowedChildClasses Oid False False False
allowedChildClassesE... Oid False False False
altSecurityIdentities DirectoryString False True True
assistant DN True False False
attributeCertificate... OctetString False False False
audio OctetString False False False
.....
# Get Only Properties in Global Catalog :
PoSH>$SchemaUser.get_OptionalProperties() |? {$_.IsInGlobalCatalog}| ft name,Syntax,IsSingleValued,IsIndexed,IsInGlobalC
atalog
Name Syntax IsSingleValued IsIndexed IsInGlobalCatalog
---- ------ -------------- --------- -----------------
altSecurityIdentities DirectoryString False True True
c DirectoryString True False True
description DirectoryString False False True
displayName DirectoryString True True True
distinguishedName DN True False True
dSCorePropagationData GeneralizedTime False False True
flags Int True False True
givenName DirectoryString True True True
homePhone DirectoryString True False True
ipPhone DirectoryString True False True
isDeleted Bool True False True
l DirectoryString True True True
legacyExchangeDN CaseIgnoreString True True True
mail DirectoryString True True True
manager DN True False True
mSMQDigests OctetString False True True
mSMQDigestsMig OctetString False False True
mSMQSignCertificates OctetString True False True
mSMQSignCertificatesMig OctetString True False True
name DirectoryString True True True
o DirectoryString False False True
objectGUID OctetString True True True
otherIpPhone DirectoryString False False True
ou DirectoryString False True True
partialAttributeDele... OctetString True False True
partialAttributeSet OctetString True False True
primaryGroupID Int True True True
proxiedObjectName DNWithBinary True False True
replPropertyMetaData OctetString True False True
replUpToDateVector OctetString True False True
repsFrom ReplicaLink False False True
repsTo ReplicaLink False False True
sAMAccountType Int True True True
securityIdentifier Sid True False True
servicePrincipalName DirectoryString False True True
sIDHistory Sid False True True
sn DirectoryString True True True
st DirectoryString True False True
street DirectoryString True False True
subRefs DN False False True
telephoneNumber DirectoryString True False True
userAccountControl Int True True True
userCert OctetString True False True
userCertificate OctetString False False True
userPrincipalName DirectoryString True True True
userSMIMECertificate OctetString False False True
uSNChanged Int64 True True True
uSNCreated Int64 True True True
uSNLastObjRem Int64 True False True
wellKnownObjects DNWithBinary False False True
whenChanged GeneralizedTime True False True
whenCreated GeneralizedTime True False True
# Or export to CSV file for later use :
$SchemaUser.get_OptionalProperties() |? {$_.IsInGlobalCatalog}| select name,Syntax,IsSingleValued,IsIndexed,IsInGlobalC
atalog | export-csv -not
Now that we did see how to use the Actice Directory Schema to look up information about the properties, lets take a look at the UserAcountControl Property and UserParameters and note the type(syntax) and Next to properties in global catalog you might also look at the indexed properties, more about that later in the series with directorySearcher.Now we did see how to get all Properties and did set them the way that we did want (think about displayname,GivenName,SN and so on),
An Other trick to get the properies is fill them from the MMC and then look at the names created in PowerShell e.g.:
# Original
PoSH>$user | fl *
objectClass : {top, person, organizationalPerson, user}
cn : {Test}
sn : {}
description : {}
physicalDeliveryOfficeName : {}
telephoneNumber : {}
givenName : {}
initials : {}
distinguishedName : {CN=Test,OU=MowOu,DC=mow,DC=local}
instanceType : {4}
whenCreated : {7/2/2006 2:10:58 PM}
whenChanged : {7/2/2006 2:16:11 PM}
displayName : {}
uSNCreated : {System.__ComObject}
uSNChanged : {System.__ComObject}
department : {Foo}
name : {Test}
objectGUID : {4.0.208.232.208.190.162.64.184.228.97.225.109.37.99.125}
userAccountControl : {544}
badPwdCount : {0}
codePage : {0}
countryCode : {0}
badPasswordTime : {System.__ComObject}
lastLogoff : {System.__ComObject}
lastLogon : {System.__ComObject}
pwdLastSet : {System.__ComObject}
primaryGroupID : {513}
objectSid : {1.5.0.0.0.0.0.5.21.0.0.0.94.172.186.232.167.29.117.70.12.60.36.84.61.12.0.0}
accountExpires : {System.__ComObject}
logonCount : {0}
sAMAccountName : {test}
sAMAccountType : {805306368}
userPrincipalName : {}
objectCategory : {CN=Person,CN=Schema,CN=Configuration,DC=mow,DC=local}
mail : {}
nTSecurityDescriptor : {System.__ComObject}
# Fill some properties in the Management Console.
PoSH>$user.RefreshCache()
PoSH>$user | fl *
objectClass : {top, person, organizationalPerson, user}
cn : {Test}
sn : {Orsouw}
description : {/\/\o\/\/ Test Account}
physicalDeliveryOfficeName : {PowerShell}
telephoneNumber : {12345}
givenName : {Marc}
initials : {van}
distinguishedName : {CN=Test,OU=MowOu,DC=mow,DC=local}
instanceType : {4}
whenCreated : {7/2/2006 2:10:58 PM}
whenChanged : {7/2/2006 2:20:24 PM}
displayName : {Marc van Orsouw}
uSNCreated : {System.__ComObject}
uSNChanged : {System.__ComObject}
department : {Foo}
name : {Test}
objectGUID : {4.0.208.232.208.190.162.64.184.228.97.225.109.37.99.125}
userAccountControl : {544}
badPwdCount : {0}
codePage : {0}
countryCode : {0}
badPasswordTime : {System.__ComObject}
lastLogoff : {System.__ComObject}
lastLogon : {System.__ComObject}
pwdLastSet : {System.__ComObject}
primaryGroupID : {513}
objectSid : {1.5.0.0.0.0.0.5.21.0.0.0.94.172.186.232.167.29.117.70.12.60.36.84.61.12.0.0}
accountExpires : {System.__ComObject}
logonCount : {0}
sAMAccountName : {test}
sAMAccountType : {805306368}
userPrincipalName : {test@mow.local}
objectCategory : {CN=Person,CN=Schema,CN=Configuration,DC=mow,DC=local}
mail : {test@mow.local}
nTSecurityDescriptor : {System.__ComObject}
We now go on to setting the password and enable the Account. The Set_Password method that is show in the get-member output will only allow you to set your own password, so we need to use the Invoke Method again to pass it to the Native COM object. You can set the Password Like This :
Now we need to enable it, as we did see from Your look in the Schema this is a Integer, we also see this as we look at it :
PoSH>$mow.userAccountControl
546
but this is a BitArray setting a whole list of settings. For more info See :
ADS_USER_FLAG_ENUM on MSDN. You can see From that MSDN entry that it is the 2nd Bit that controls "account disabled" True or False.Lets see how we can change that from PowerShell.
#Change userAccountControl BitArray
# We can go counting and come up with this
$mow.userAccountControl = 544
# Or let PowerShell do the counting.
$user.userAccountControl = $user.userAccountControl[0] -band (-bnot 2)
# We need the userAccountControl[0] as AD porerties are PropertyValueCollections
# while setting you will not notice that as this gets wrapped.
PoSH>$mow.userAccountControl[0] = $mow.userAccountControl -band (-bnot 2)
Cannot convert "System.DirectoryServices.PropertyValueCollection" to "System.Int32".
At line:1 char:59
# try a gettype :
$user.userAccountControl.gettype()
the reason behind the extra [0] will workuserAccountControl is a PropertyValueCollection, you need to set the item.
But in the ADSI COM provider are also some shortcuts available that we can use amongs others the Accountdisabled property that does the setting of the bit for us
We can use this functionality like this :
#Change userAccountControl BitArray By using ADSI COM Object Methods :
# Get
PoSH>$user.InvokeGet('AccountDisabled')
True
PoSH>$user.userAccountControl
546
# Set
PoSH>$mow.invokeSet('Accountdisabled',$false)
PoSH>$mow.CommitChanges()
PoSH>$mow.userAccountControl
544
For a list of those ADSI properties and methods see :
IADsUser on MSDN.
Also setting Terminal TeminalService settings needs to be done with invoke :
PoSH>$mow.InvokeSet('TerminalservicesHomeDrive',"z:")
PoSH>$mow.CommitChanges()
PoSH>$mow | fl *
objectClass : {top, person, organizationalPerson, user}
cn : {mow}
distinguishedName : {CN=mow,OU=MowOu,DC=mow,DC=local}
instanceType : {4}
whenCreated : {5/18/2006 7:18:00 PM}
whenChanged : {6/28/2006 8:37:27 PM}
uSNCreated : {System.__ComObject}
uSNChanged : {System.__ComObject}
department : {foodar}
name : {mow}
objectGUID : {231.189.223.77.11.205.61.64.178.101.27.206.71.175.101.75}
userAccountControl : {546}
badPwdCount : {0}
codePage : {0}
countryCode : {0}
badPasswordTime : {System.__ComObject}
lastLogoff : {System.__ComObject}
lastLogon : {System.__ComObject}
pwdLastSet : {System.__ComObject}
primaryGroupID : {513}
userParameters : { P?CtxCfgPresent?????CtxCfgFlags1?????Ctx
Shadow????*??CtxMinEncryptionLevel??&?CtxWFHomeDir???????????????????"??CtxWFHomeDirDrive???}
objectSid : {1.5.0.0.0.0.0.5.21.0.0.0.94.172.186.232.167.29.117.70.12.60.36.84.84.4.0.0}
accountExpires : {System.__ComObject}
logonCount : {0}
sAMAccountName : {lol}
sAMAccountType : {805306368}
objectCategory : {CN=Person,CN=Schema,CN=Configuration,DC=mow,DC=local}
msNPAllowDialin : {False}
nTSecurityDescriptor : {System.__ComObject}
PoSH>$mow.InvokeGet('TerminalservicesHomeDrive')
Z:
You can see this is stored in a DirectoryString named userParameters that is not really readable , but again there are special Methods here for setting this in the ADSI COM object you can Use with the invoke function , for a Complete list look here on MSDN : IADsTSUserEx Property Methods
IADsTSUserEx Property MethodsWe did see in this post how to fill different User Properties and how to enable the Account . And that most properties you can easy set either on the DirectoryEntry or using the Native Com Object,but there are some properties that are stored in a special object therefor the Shortcut Methods in the Com object are more handy (UserAccountControl) or Needed (TerminalService Configuration). Also in ADAM native invokes are more needed.Also if you Come from VbScript and already know the native Method this could be handy, but for the rest is nice to use the wrapped properties as we not need to worry about arrays that much, and can do some exploring in PowerShell.OK, for the first time we needed to do some documentation Lookups, for the invokes but still not that bad I think. and we have all we need for creating the user now, you might want to set some more properties like DisplayName,GivenName,SN,Description etc also but they should be easy to find and set now, in the next post I will do a roundup in some functions, and take a look at CSV import possibilities and then go on to the DirectorySearcher Object.
I know this has grown to a big post and there are some things a bit less intuitive as we would want, but thats also the case with other languages and scripting against Active Directory, this are not standard LDAP things and are about how its stored in AD, but we are a bit spoiled with the discover ability of PowerShell already ;-)I will provide more background links later, but I hope this post did clear some things up that seem a bit difficault at first but that they are not that bad, as I did see more examples and questions coming of people using PowerShell with AD in the Comments, the NG and other blogs
(Also more about PowerShell in Common for example Scott Hanselmann did post a couple of cool PowerShell usages for links see : http://del.icio.us/powershell and you will see that the PowerShell fueled fire is burning harder and harder ) .
In next post I will make an example combining the Creating and configuring of a User in a Script that is using CSV files to create users.(and the Example how I did create the Users form the AdventureWorks SQL example DBase I created on my Demo DC.I promised Jeffrey Snover ;-) ). And go on to the the Searching of Objects in AD.
Enjoy,
Greetings /\/\o\/\/
Tags : Monad PowerShell