Using Scripts to Delegate Control of Active Directory

Part 2: Working with Property Sets

In Part 1 of our two-part series, Luke Skywalker destroyed the Death Star, the Empire was defeated, and – oh, wait. Sorry. In our trilogy something far more exciting occurred: in Part 1, we showed you how you could use extended rights to help delegate control over Active Directory. (If that doesn’t sound more exciting than destroying the Death Star, well, maybe you should read Part 1 before passing judgment. It’s hard to believe that anyone would find a cosmic battle between the forces of good and evil more exciting than writing ADSI scripts to delegate control of Active Directory.)

Note. If you’re wondering why it took so long for Part II of this two-part series to appear, well, you know how they say that one dog year is the equivalent of seven people years? Unfortunately, one Scripting Guy month is sometimes the equivalent of seven people months. Sigh….

We began this series by looking at extended rights simply because two of those rights – the right to change a user’s password and the right to reset a user’s password – are two of the most-common Active Directory scripting requests we Scripting Guys receive. However, extended rights represent only one way that you can delegate control over Active Directory accounts. Equally useful is the ability to grant someone the right to change specified account attributes. For example, each time a user changes jobs you – as the system administrator – have to go into Active Directory and update everything from the user’s job title to their department to their phone number and office location. Needless to say, in larger companies that could be a full-time job, leaving very little time for you to do your system administration duties.

But what’s the alternative? Well, the alternative is to delegate control over these account attributes; in other words, to give a non-administrator the right to modify attribute values for a user or set of users (such as all the users in an OU). This user will have the right to modify a specific set of attributes for a specific set of attributes, but that’s it; you are not making this user a full-fledged administrator, and you’re not handing over the keys to Active Directory. You’re just delegating control over a small, well-defined portion of Active Directory.

Of course, while that might sound good at first it does get a bit scarier the more you think about it. After all, how do you know which attributes you need to give this user access to? How do you know which attributes are the right attributes? How do you write a script that grants access to each and every one of these attributes? And how in the world can you free Princess Leia from the evil clutches of Jabba the Hutt?

Relax: this is what property sets are for. Granted, they might not help Princess Leia, but they’ll definitely help you in your quest to delegate control of Active Directory. And after you’ve delegated control of Active Directory, you’ll have plenty of time to finish your Jedi training and go around rescuing princesses.

Part 2: Working with Property Sets

Property sets represent a collection of related attributes. For example, the Personal Information property set encompasses these 41 user account attributes:

  • Address
  • Address-Home
  • Assistant
  • Comment
  • Country-Name
  • Facsimile-Telephone-Number
  • International-ISDN-Number
  • Locality-Name
  • MSMQ-Digests
  • MSMQ-Sign-Certificates
  • Personal-Title
  • Phone-Fax-Other
  • Phone-Home-Other
  • Phone-Home-Primary
  • Phone-Ip-Other
  • Phone-Ip-Primary
  • Phone-ISDN-Primary
  • Phone-Mobile-Other
  • Phone-Mobile-Primary
  • Phone-Office-Other
  • Phone-Pager-Other
  • Phone-Pager-Primary
  • Physical-Delivery-Office-Name
  • Picture
  • Post-Office-Box
  • Postal-Address
  • Postal-Code
  • Preferred-Delivery-Method
  • Registered-Address
  • State-Or-Province-Name
  • Street-Address
  • Telephone-Number
  • Teletex-Terminal-Identifier
  • Telex-Number
  • Telex-Primary
  • User-Cert
  • User-Shared-Folder
  • User-Shared-Folder-Other
  • User-SMIME-Certificate
  • X121-Address
  • X509-Cert

If you wanted to, you could create a script that methodically gives a user permission to read and write each of these 41 attributes, one attribute at a time. Alternatively*,* you could create a script that assigns read/write permissions to the Personal Information property set, which in turn gives the user read/write permissions to each attribute in that property set. As you might expect, this latter option is much faster and easier than the former.

Active Directory includes 11 predefined property sets:

Property Set / Object GUID Description
DNS Host Name Attributes
{ 72e39547-7b18-11d1-adef-00c04fd8d5cd}
Contains the DNS-Host-Name and ms-DS-Additional-Dns-Host-Name attributes. Applies to Computer objects.
Other Domain Parameters
{ b8119fd0-04f6-4762-ab7a-4986c76b3f9a}
Property set permitting control to a list of domain attributes. Applies to the DomainDNS object.
Domain Password and Lockout Policies
{ c7407360-20bf-11d0-a768-00aa006e0529}
Property set containing all lockout and password age related attributes on user account. Applies to Domain and DomainDNS objects.
Phone and Mail Options
{ e45795b2-9455-11d1-aebd-0000f80367c1}
Property set containing user attributes that describe user email related information. Applies to User, Group, and inetOrgPerson objects.
General Information
{ 59ba2f42-79a2-11d0-9020-00c04fc2d3cf}
Property set containing a set of user attributes that constitute general user information. Applies to User and inetOrgPerson objects.
Group Membership
{ bc0ac240-79a9-11d0-9020-00c04fc2d4cf}
Property set containing user attributes that describe group membership information. Applies to User and inetOrgPerson objects.
Personal Information
{ 77b5b886-944a-11d1-aebd-0000f80367c1}
Property set containing user attributes that describe personal user information. Applies to User, Contact, Computer, and inetOrgPerson objects.
Public Information
{ e48d0154-bcf8-11d1-8702-00c04fb96050}
Property set containing user attributes that describe user public information. Applies to User, Computer, and inetOrgPerson objects.
Account Restrictions
{ 4c164200-20c0-11d0-a768-00aa006e0529}
Property set containing user attributes that describe account restrictions. Applies to User, Computer, and inetOrgPerson objects.
Logon Information
{ 5f202010-79a5-11d0-9020-00c04fc2d4cf}
Property set containing user attributes that describe user logon information. Applies to User and inetOrgPerson objects.
Web Information
{ e45795b3-9455-11d1-aebd-0000f80367c1}
Property set containing user attributes that describe user web related information. Applies to User, Contact, and inetOrgPerson objects.

Best of all, you can assign these property sets using ADSI scripts. (And a good thing, too, seeing as how the rest of this article is supposed to be about assigning property sets using ADSI scripts.)

Assigning a Property Set

Let’s start off by looking at a script that gives a user the right to modify the Personal Information property set for all the users in an OU. We’ll take a look at the script and then explain how it works.

Note. OK, that’s only partly true: we aren’t going to discuss every line in this script. If you’re looking for a basic introduction to using scripts to manage Active Directory, take a peek at Part 1 in this series.

Here’s the script:

 

Const ADS_ACETYPE_ACCESS_ALLOWED_OBJECT = &H5
Const ADS_RIGHT_DS_READ_PROP = &H10
Const ADS_RIGHT_DS_WRITE_PROP = &H20
Const ADS_FLAG_OBJECT_TYPE_PRESENT = &H1
Const ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT = &H2
Const ADS_ACEFLAG_INHERIT_ACE = &H2

Set objSdUtil = GetObject("LDAP://OU=Finance, DC=fabrikam,DC=Com")
Set objSD = objSdUtil.Get("ntSecurityDescriptor")
Set objDACL = objSD.DiscretionaryACL

Set objAce = CreateObject("AccessControlEntry")

objAce.Trustee = "FABRIKAM\kmyer"
objAce.AceFlags = ADS_ACEFLAG_INHERIT_ACE
objAce.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
objAce.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT OR ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT
objAce.ObjectType = "{77b5b886-944a-11d1-aebd-0000f80367c1}"
objACE.InheritedObjectType = "{BF967ABA-0DE6-11D0-A285-00AA003049E2}"
objAce.AccessMask = ADS_RIGHT_DS_READ_PROP OR ADS_RIGHT_DS_WRITE_PROP
objDacl.AddAce objAce

objSD.DiscretionaryAcl = objDacl

objSDUtil.Put "ntSecurityDescriptor", Array(objSD)
objSDUtil.SetInfo

For the most part, you have to worry about only two things when writing a script that grants access to an Active Directory property set; everything else is boilerplate. First, you need to specify the object being delegated. Remember, all we’re doing here is adding an Access Control Entry (ACE) to a security descriptor. The object being delegated is simply the object whose security descriptor is being modified.

For example, in our sample script we’re delegating control of the Finance OU, which happens to reside in the Fabrikam domain. Therefore, our binding string looks like this:

Set objSdUtil = GetObject("LDAP://OU=Finance, DC=fabrikam, DC=Com")

To bind to a different OU in a different domain, we just modify the binding string:

Set objSdUtil = GetObject("LDAP://OU=Research, DC=contoso, DC=Com")

Second, you need to assign the correct values to the properties of the ACE. Here’s a slightly more detailed look at the ACE properties and why we configured them the way we did. The good news? Many of these values can be used as-is any time you write a new property set script:

Property Description
Trustee The Trustee is simply the user (or group) to which you want to grant the extended right. Simply specify the name using the domain\name format (e.g., fabrikam\kmyer).
AceFlags

AceFlags is a bitflag property that indicates whether the ACE can be inherited by other objects. We want all the user accounts in the OU to inherit this ACE; hence we set the value to &H2, which causes the ACE to be propagated to the appropriate child objects.

Note that here – and throughout the script – we use a constant which has been assigned the appropriate value. Thus instead of having a line of code that looks like this:

 objAce.AceFlags = &H2 

We used a line of code that looks like this:

 objAce.AceFlags = ADS_ACEFLAG_INHERIT_ACE 

ADS_ACEFLAG_INHERIT_ACE is a constant we defined at the beginning of the script.

AceType The AceType property indicates whether this ACE will grant the trustee access to the object or whether the ACE will be used to deny the trustee access to the object. We set the value to &H5, which allows access to a property set (or to any other item that defines an object type and/or an inherited object type).
Flags The Flags property indicates whether the ACE has an object type and/or an inherited object type. Because we have both an object type and an inherited object type, we need to specify both the value &H1 and the value &H2 in the script (using the bitwise OR operator as opposed to word and).
ObjectType The GUID that represents the right being granted. GUIDs for all 11 property sets are listed in the property set table we showed you previously.
InheritedObjectType

The GUID for the user account object. We use that GUID because we want this ACE to apply to all the user account objects in the Finance OU. If we wanted the ACE to apply to the computer account object we’d need to specify the GUID for that object instead:

 objAce.ObjectType = _ 
FakePre-ac8f5c9cfe154ade82afdf6bb2fd84b9-a93f9871f2654fb89f41770512a7a94e

Handy hint. How do we know the GUID for the computer account object? The easiest way to do that is to just look up the object in the Active Directory Schema Reference on MSDN.

AccessMask A bitflag property containing all the access privileges being granted (or denied) by the ACE. We need to assign both Read and Write permissions; hence we assign two values (&H10 and &H20) to the AccessMask property. (Again, note the use of the bitwise OR operator.)

The net result is a block of code that looks like this:

Set objAce = CreateObject("AccessControlEntry")

objAce.Trustee = "FABRIKAM\kmyer"
objAce.AceFlags = ADS_ACEFLAG_INHERIT_ACE
objAce.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
objAce.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT OR ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT
objAce.ObjectType = "{77b5b886-944a-11d1-aebd-0000f80367c1}"
objACE.InheritedObjectType = "{BF967ABA-0DE6-11D0-A285-00AA003049E2}"
objAce.AccessMask = ADS_RIGHT_DS_READ_PROP OR ADS_RIGHT_DS_WRITE_PROP
objDacl.AddAce objAce

We create a new AccessControlEntry object, configure the properties, and then call the AddAce method to apply the new ACE to the local copy of the security descriptor. (As with almost all ADSI operations, property sets are first assigned to a copy of the object, one that exists only in local memory. Later we’ll use the Put and SetInfo methods to write the revised security descriptor to the actual object in Active Directory.)

What Happens When You Assign a Property Set?

If you run the script that gives Ken Myer access to the Personal Information property set nothing will appear to happen; that’s because this simple little script doesn’t do any kind of reporting. However, if you use Active Directory Users and Computers to check the security descriptor for the Finance OU you should see Ken Myer listed among the object’s trustees:

Of course, at first glance it appears that Ken Myer hasn’t been granted (or denied) much of anything. That’s because property sets fall under the “Additional permissions” category. To view those permissions you’ll need to click the Advanced button to bring up the Access Control Settings dialog box:

That’s a bit closer, but still not fully informative. To get the lowdown once and for all select Ken Myer (or the appropriate user) and click View/Edit; at that point you’ll see that Ken has been granted Read/Write access to the Personal Information property set:

That’s all you have to do to give user Ken Myer the right to manage the Personal Information attributes for all the users in the Finance OU.

Incidentally, if you want to remove this access privilege you can use a script similar to this:

Set objSdUtil = GetObject("LDAP://OU=Finance,DC=fabrikam,DC=Com")
Set objSD = objSdUtil.Get("ntSecurityDescriptor")
Set objDACL = objSD.DiscretionaryACL

For Each objACE in objDACL
    If objACE.Trustee = "FABRIKAM\kmyer" Then
        objDACL.RemoveAce objACE
    End If
Next

objSD.DiscretionaryAcl = objDacl
objSDUtil.Put "ntSecurityDescriptor", Array(objSD)
objSDUtil.SetInfo

How Property Sets Impact the Trustee

So what does this mean to Ken Myer, our newly-appointed trustee? Well, suppose Ken logs on to a computer and calls up Active Directory Users and Computers. Suppose he then double-clicks a user account in the Finance OU. The property page for that user account will look something like this:

As you can see, the Office and Telephone number boxes are editable; that’s because Ken now has the right to modify the physicalDeliveryOfficeName and telephoneNumber properties because they’re both in the Personal Information property set. Text boxes for the other properties displayed on this page are greyed-out; that’s because we didn’t delegate the right to modify attributes like Description or wWWHomePage, which are not in the Personal Information property set.

Just for the heck of it, this next graphic compares the Properties dialog box for two users: Amy Recker, who has an account in the Finance OU, and Bjorn Rettig, who has an account in a different OU. As you can see, Ken has the right to modify some of the attributes for Amy’s account, but does not have the right to modify those same attributes for Bjorn’s account:

And that’s exactly the way we’d expect this to work.

Delegating Permissions for a Single User Account

Up to this point we’ve shown you how to write a script that delegates control over all the user accounts in a specified OU; this is probably the most common way to delegate control of a property set. Alternatively, we could bind to the domain root instead of an OU; in that case, the user would be granted property set permissions for all the user accounts in the domain.

But what about more fine-grained delegation of control? Could you, say, give someone property set permissions for a single user account? Of course you can:

Const ADS_ACETYPE_ACCESS_ALLOWED_OBJECT = &H5
Const ADS_RIGHT_DS_READ_PROP = &H10
Const ADS_RIGHT_DS_WRITE_PROP = &H20
Const ADS_FLAG_OBJECT_TYPE_PRESENT = &H1
Const ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT = &H2

Set objSdUtil = GetObject("LDAP://CN=Rob Young, OU=Finance, DC=fabrikam,DC=Com")
Set objSD = objSdUtil.Get("ntSecurityDescriptor")
Set objDACL = objSD.DiscretionaryACL

Set objAce = CreateObject("AccessControlEntry")

objAce.Trustee = "FABRIKAM\kmyer"
objAce.AceFlags = 0
objAce.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
objAce.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT
objAce.ObjectType = "{77b5b886-944a-11d1-aebd-0000f80367c1}"
objAce.AccessMask = ADS_RIGHT_DS_READ_PROP OR ADS_RIGHT_DS_WRITE_PROP
objDacl.AddAce objAce

objSD.DiscretionaryAcl = objDacl

objSDUtil.Put "ntSecurityDescriptor", Array(objSD)
objSDUtil.SetInfo

So how is this script different from the script that delegates control over an entire OU? Funny you should ask. There are three differences between the two kinds of scripts:

The AceFlags property is different. Because this ACE will not be inherited we don’t want to set the AceFlags to &H2. Instead, we set the value of AceFlags to 0.

The Flags property is different. In this script there is an object type (the GUID for the property set) but no inherited object type. Consequently, we have to specify only a single value: ADS_FLAG_OBJECT_TYPE_PRESENT.

The InheritedObjectType is missing. That’s because the ACE is supposed to apply only to the Rob Young user account. All we have to do is remove that line of code.

Realistically, would you ever delegate control over a single user account? Well, maybe. Alternatively, you might use this approach to delegate control over a group of user accounts. For example, suppose you want to give Ken Myer the right to read and write the Personal Information property set for all the users in the Accounting department. However, because you don’t have an Accounting OU you can’t simply delegate control over the OU. Instead, you would need to individually grant access to each and every user account.

In Our Next Episode

Hopefully that will be enough to get you started with Active Directory property sets. Be sure to tune in next month, when Yoda, Chewbacca, and the Scripting Guys team up to bring you the exciting conclusion to this two-part series: Delegating Control of Individual Attributes!