Category Archives: Active Directory

Norwegian content: How to integrate your on-premise Active Directory with Windows Azure Active Directory

I have published a 5 part blog series on the Norwegian Microsoft TechNet Blog, with step by step instructions for setting up integration between your on-premise Window Server Active Directory Directory Service and Windows Azure Active Directory. It covers concepts, single-sign on with ADFS, Directory Synchronization with the DirSync Tool and troubleshooting. So if you speak my native language; head on over:

Morgan

Enabling notify-driven replication across Active Directory sites

Introduction

Active Directory sites, site links and site link bridges exist to be able to tell the directory service about the network’s physical topology. Specifically to identiy which parts of it are well connected and where there are slow WAN links. The rule of thumb here is that all systems that are connected with at backbone of 10 Mbps or more are considered well connected. These should therefore be contained in a site. Two or more sites of well connected systems can be linked with site links that have a schedule and an associated cost. The cost can be said to represent how fast the link is. Low cost would cause the directory to prefer it while a high cost would be more prohibitive. The way we identify systems in Active Directory is by either IPv4 or IPv6 subnets. A list of IP subnets define a site. Needless to say; the exact same IP subnet cannot be associated with more than one site within the same forest.

However, this functionality also has another application. It can be used to created advanced replicaton topologies that segment your Active Directory.

Segmentation of Active Directory

Whenever you need to control the communication paths between domain controllers you need sites and site links. In these situations, network speed is not the issue, but rather where communication is allowed. The default topology of Active Directory replication within a site is a least-cost spanning tree. If there are any firwall or blocked communication paths between any DCs the directory cannot know about this without defined sites and site links. If you use only one site in such a situation you will not have a functional replication topology.

Let’s say you have a network comprising of two zones with different security levels (Figure 1). Typically these two zones would be segmented by a firewall. However, to make administration and user experience easier and better you want to use the same domain across the zones. If you have 4 DCs in each zone, for a total of 8 DCs in the domain you would have to allow communication from all DCs in one zone to all DCs in the other zone and vice versa if you had only one site. With two sites, one for each security level/zone, with one or two bridgehead servers defined in each site, you would have much fewer openings in your firewall.

Figure 1

This is also very useful for Active Directory aware clients and services who try to use the DCs closest to them. Returning to our example of a network with two zones and a firewall separating them, clients in one zone would not be allowed to access DCs in the other. Clients from a zone with a lower security level typically cannot access resources in higher security levels. Depending on the circumstances the reverse may also be true. Clients from a higher security level cannot access resources on a lower level. With only one site spanning the two zones clients in either zone has a 50 % chance of trying to contact a DC they cannot communicate with (if there is an equal number of DCs in each zone). This is because they think that any DC in the site is available. On the other hand with a correctly defined site topology where the correct IP subnets are associated with the correct site, clients will not attempt to communicate with a DC in another site unless all the DCs in their own site are down.

But there is one challenge with this approach, and that is the speed of replication between sites. In our scenario network speed is not an issue. These configrations are typically segmented LANs with high speed backbones and firewalls. However, Active Directory regards sites as well connected and the links between sites as much slower. Therefore site links only replicate on a schedule, not by using notifications as DCs do within a site. The lowest replication interval you can set on a site link is 15 minutes, meaning that Active Directory updates are replicated across the site link every 15 minutes.

Within a site replication is notificaton-driven. A DC with an originating (a write that is done on the local DC) or replicated (a write done on the local DC but as the result of replication of an update from another DC) update will send a notification message to all its intra site replication partners that it has changes that need to be replicated. The DCs receiving these messages will in turn connect to the originating DC and pull the changes and update their own copies of the database. All Active Directory replication is pull-based meaning that a DC will never push its changes over to any other DC. It will just notify them and they initiate a pull to replicate any changes. This is also true for inter site replication, except between sites there are no notifications, but a schedule and a replication interval (Figure 2).

Figure 2

Each time the interval is reached the DCs will initiate pull replication with their configured parteners across the site links. This works this way because site links are thought to be slow and costly. Thus Active Directory does not want to monopolize them with a lot of singe obejct updates, but rather replicate a lot of objects in a queue at specific intervals. It is this behaviour we want to modify to make Active Directory updates as speedy between sites as they are within sites. Remember, in our scenario network speed is not an issue, we just want to segment our domain.

Configuring site links to use notification based replication

Whether to use notification or a replication interval to replicate across a site link is configured pr. site link. If all your sites are in the same site link configuration is simple, but if you have several sites links, in a hub-spoke topology e.g., you will have to configure each site link.

In this example we will configure the DEFAULTIPSITELINK site link that is created automatically in every Active Directory forest and contains all sites by default.

You can make the change using any tool able to write to Active Directory attributes.  I will use Active Directory Sites and Services since that is the easiest way. The change itself requires Enterpise Admin level permissions.

  1. Find the site link you want to change, right click and select Properties.
  2. Select the Attributes Editor tab.
  3. Find the options attribute (make sure that you display all attributes regardless of whether they have a value or not, since this attribute does not have a value by default).
  4. Set the value of the options attribute to 1. Notice how you will now have an explanation behind the 1 value indicating that you have configured the link to use notifications (USE_NOTIFY).
  5. Replicate this change to all DC manually or wait for regular scheduled Active Directory replication.

image

The options attribute uses a bitmap. Its possible values are:

Decimal Value Binary Value Explanation
1 1 USE_NOTIFY
2 10 TWOWAY_SYNC
4 100 DISABLE_COMPRESSION

You can use any combination of these. If your options attribute already has a value you need to perform a BITWISE OR operation on the existing value. If the value is 4, convert that to binary (100), perform an OR operation with binary 1, the result should be binary 101, which you convert to decimal (5) and enter as the value of the options attribute.

More information

Letting go of Windows NT 4.0…

Windows Server 2008 R2 Domain Controllers have left Windows NT 4.0 behind. Windows Server 2008 would still let the old guy play along, but no more. This fact is not apparant until you start to look closer:

  • DCPromo in Windows Server 2008 R2 will not let you select Windows 2000 mixed mode for your domain functional level.
    This in turn makes it impossible to add a Windows NT 4.0 BDC to your domain.
  • A trust cannot be created between a Windows NT 4.0 domain and a Windows Server 2008 R2 domain.
    The security changes introduced in Windows Server 2008 R2 prevent this. (http://support.microsoft.com/?id=942564)
  • Windows NT 4.0 compatible cryptographic algorithms are not enabled in a Windows Server 2008 R2 domain.
    They can be enabled, but this, still, will not let you create a trust between Windows NT 4.0 and Windows Server 2008 R2. (http://support.microsoft.com/?id=942564)

I guess it  was finally time to move along…

Poor-man’s Active Directory backups (export really)

Sometimes I need a “copy” of an Active Directory domain, partition or LDS instance. Usually this is when I remove decomissioned domains in a multi-domain forest and want to keep a record of what was left when I deleted it. You can do this With LDIFDE.EXE. Here is an example command to make a full export of a domain partition:

ldifde.exe -f <path and name of export file>.ldf -d “<DN of partition” -p subtree -x -l * -j <path to log folder>

I mean; you can never be too sure right?

Script to find outdated computer objects in Active Directory

Computers have accounts in Active Directory and log on just as user accounts do. The “user name” of a computer is its name with a dollar sign appended, e.g: MYPC1$. The password is set by the machine when it is joined to the domain and changed every 30 days by the machine. Just as with user accounts, computer accounts gets left in the domain when the machine is decomissioned or reinstalled under another name etc. This leaves our directory service full of outdated user and computer object. (Outdated in this context means “has not logged on to the domain for X days). This in turn makes it hard to know how many computers are actually active. So how to battle this problem?

On Windows 2000 every DC maintend an attribute on each computer object called lastLogon. It stored the timestamp of the last logon for a computer on that DC. The “on that DC” part is important, because the lastLogon attribute was not replicated beyond the local DC. So to figure out when computer CORP-PC1 last logged on, you would have to query the lastLogon attribute on all the DCs in the domain and find the most recent one. This could be done, but was tedious and time consuming. Enter Windows Server 2003.

In Windows Server 2003 a new attribute was introduced; lastLogonTimestamp. Just like lastLogon, lastLogonTimestamp stored the timestamp of the last logon to the domain for a computer account, but lastLogonTimestamp is a replicated attribute, which means that now every DC knows the most recent logon time for a computer. So to figure out when CORP-PC1 last logged on to any DC in the domain we can query any DC for the lastLogonTimestamp attribute of CORP-PC1. Very nice.

I often find myself in a situation where I need to “clean house” in customer’s directories so I created a script that uses lastLogonTimestamp to find all computers that have not logged on to the domain for X days. X varies greatly from customer to customer, but a good start would be 90 days. The sciprt is called FindOutdatedComputers.vbs and in this post I would like to share it with you.

FindOutdateComptuers.vbs has 3 user changeable variables: intTimeLimit, strDomain and strDC. These are defined at the very beginning of the code and should be the only variables you change.

intTimeLimit: the number of days since a computer logged on to the domain

strDomain: LDAP domain name; e.g. dc=mydomain,dc=com

strDC: FQDN of DC in domain; e.g. dc1.mydomain.com

The script must be run with cscript.exe and takes one command line argument; a 0 (zero) or a 1 (one). 0 mens just echo out the machines that have not logged on since the defined limit and 1 means echo out, but also move the comptuters to the Outdated Computer Objects OU (which the script creates). It will never delete any information from your AD, only move computer accounts to the Outdated Comptuer Objects OU. If you omit this argument, the default action is just to display the results and not move anything.

For FindOutdatedComptuers.vbs to work your domain functional level must be at least Windows Server 2003. The script checks for this and warns you if you are below the needed level.

Here is the code:

https://gist.github.com/morgansimonsen/8040007

Exploring the Global Catalog and examining the “universalness” of Universal Groups

Universal groups (UG) are stored in the Global Catalog (GC). But what exactly is the Global Catalog, and how does it store objects? Does it store anything at all! And how do Universal Groups work anyway?

Active Directory Domain Controllers (DC) have exactly one database. It is stored in %windir%NTDS and is called NTDS.DIT. DIT means Directory Information Tree. It is an MDB database. This database is the only place a Domain Controller stores directory data. So that means that the Global Catalog is not a separate database.

Domain Controller databases store Naming Contexts (NC). These are also called directory partitions, or just partitions, and those names actually makes it easier to understand what they are. By default a DC has at least 3 NCs. The first is the Domain NC, also called the Default Naming Context, which stores all the objects belonging to a domain; users, groups, computers etc. The DC has a copy of this NC because it is a DC for the same domain. The second is the Schema Naming Context which stores all the object definitions for a particular forest. And finally the Configuration Naming Context, which stores the common configuration for a forest, e.g. sites, subnets, forest-wide services etc. In addition to these we can have application partitions, used by DNS for example, but they are not important here. The Configuration and Schema partitions are stored by every DC in a forest and contain the same data on all DCs. If you are a DC in a forest you always store the full Configuration and Schema NCs. The Domain Naming Context is stored in full by every DC in the given domain. A DC can only be a DC for one domain, meaning it only ever stores one complete Domain Naming Context. In addition to its own full Domain Naming Context, the DCs that are designated as Global Catalogs also store partial Naming Contexts for all other Domain Naming Contexts in the forest. So the Global Catalog is not a separate Naming Context.

Now, the point of a Global Catalog is to have one or more places where you can get information, but not all the information, from all the domains in the entire forest. To be a GC, a machine must also be a DC. You cannot just be a GC. A GC knows a little bit about every object in every Domain NC in the forest. It also knows everything in the Configuration and Schema partitions, because the GC is also a DC and every DC holds the Configuration and Schema partitions. A forest must have at least one Global Catalog, and every site in a forest must have a Global Catalog. A user must be able to talk to a Global Catalog to log on. You talk to a GC using LDAP or ADSI. A regular DC listens for LDAP traffic on port 389 TCP (and 689 TCP for LDAPS), and on these ports you can retrieve all the information about any object in any of its full NCs. The Global Catalog, which is not a separate database or naming context, remember, listens for LDAP traffic on port 3268 TCP.

What controls the information the GC has about each object from the Domain NCs in the forest is called the Partial Attribute Set (PAS), and it is defined on the attributes in the Schema. Some attributes are part of the PAS, but most are not. If you would like to add an attribute to the PAS you can do so with the Schema Management Console Snap In. So every attribute definition in the schema has a flag that says if that particular attribute should be replicated to the Global Catalog. Which is not a separate database or naming context, but has its own port, but not its own protocol.

So let’s try to sum this up. Objects in domains in Active Directory are stored in Naming Contexts. An object is made up of several attributes, some of which are flagged as being part of the Partial Attribute Set. A DC designated as a GC stores a copy of every domain NC in the forest, containing all the objects in that NC, but only the attributes that are flagged as being part of the PAS. That means that the Global Catalog is just a copy of a regular domain NCs,but with a little less data. A DC/GC also has only one directory service process, LSASS.EXE, which listens on both port 389 TCP and 3268 TCP. If traffic comes in on port 389 you can ask about the Configuration, Schema or full Domain NCs. But if traffic comes in on port 3268 you can only retrieve data about attributes that are part of the PAS, and only from domain NCs (any domain NC on the DC/GC).

What I am trying to say here is that there really is no difference between the regular DC function and the GC function. To say that something is stored in the Global Catalog really does not make sense, since it is in the same database and in the same Domain Naming Context. The only difference is what you can “see” while looking at the GC, as opposed to looking at the DC. Think of this as having DC glasses and GC glasses. When viewed with the GC glasses you can see a fuzzy outline of an object, but when viewed with the DC glasses, the same object is clear and crisp.

When we say that a universal group is stored in the Global Catalog what we are really saying is that a Universal group is a group, stored in the Domain Naming Context of the domain where it was created, which has its members attribute in the Partial Attribute Set. To demonstrate this lets look at a Global Group in a full domain NC (with our DC glasses). You will see the members attribute populated with its members. Now look at the same global group on a GC (with GC glasses). The members attribute is missing so the GC does not know who is a member of the Global Group. A Universal group’s members attribute will look identical when viewed in the full Domain NC and when viewed in the partial Domain NC.

Also, think about this. If Domain B is a child domain of Domain A. What happens to a Universal group created in Domain B when Domain B is decommissioned? It will disappear! Because, it was just a group who had its members attribute in the PAS. If a Universal group was really stored in a separate Global Catalog, it would not be visible in any domains in the forest, it would only be visible when you connected to the Global Catalog partition. Instead, in real life. you can see Universal groups in Active Directory Users and Computers in the domain where you created them, but not in any other domains in your forest. Another effect of this is that when User A in Domain B is a member of a Universal Group created in Domain C, you cannot see that User A is a member of the Universal Group when you look at him in ADUC in Domain B. But you can see that he is a member when you look at the Universal Group’s members in Domain C.

So the Global Catalog is an illusion and saying that you replicate something to it is nonsense. What you are doing is applying a filter to objects.

(Some illustrations would probably be a good idea for this post, but it’s just too late. Sorry!)

Some Active Directory Migration Tool (ADMT) Notes

The good old Active Directory Migration Tool (ADMT) has reached version 3.2 making it compatible with Windows 7/Server 2008 R2 and x64. ADMT started it’s Microsoft life as licensed software from One point. I’ve been using this baby since version 2.0. It offers what you need to perform intra or inter-forest Active Directory migrations/restructures, but it is very basic. The most annoying thing is that Microsoft have yet to implement PowerShell support in ADMT.

Here are some observations I have made about the use of ADMT over the course of many years:

  • No version of ADMT translates security for Remote Desktop (Terminal Server) Roaming Profiles.
    It will translate Local Profiles using the User Profiles option in the Computer Account Migration wizard, and regular Roaming Profiles using the Translate Roaming Profiles option in the User Account Migrationwizard, but it will not touch the Remote Desktop profile. This seems very strange to me since we are just talking about reading another attribute in Active Directory and doing the same thing as you are already doing with Roaming Profiles. Beats me…(Just to be clear, the regular Roaming Profile is specified on the Profile tab of the user in Active Directory Users and Computers (ADUC), and is stored in the profilePath attribute. The Remote Desktop Services Roaming profile is specified on the Remote Desktop Services Profile tab in ADUC and is stored in the userParameters attribute data blob. In the Windows Server 2008 Active Directory schema; Microsoft have created a new attribute called msTSProfilePath that is meant to store this setting in the future.)
  • ADMT needs Full Control NTFS permissions to translate a profile, roaming or local.
    Translating a profile means updating the NTFS permissions on all the files in the profile directory, but also loading the user’s registry hive; the NTUSER.DAT file, and translating the registry permissions set on all the keys in it. I have written about manually performing this process here.If you have not changed the default settings Windows will create roaming profiles with the following ACL/Owner:
    – SYSTEM – Full Control
    user_name – Full Control
    – Owner = user_name
    Obviously these permissions will not let ADMT, which connects to the computer as the user running it (it does not have a service), translate the profile. To work around this you can either enable a Group Policy setting called Computer ConfigurationPoliciesAdministrative TemplatesSystemUser ProfilesAdd the Administrators security group to the roaming user profiles, before the profile is created or you can grant the Administrators local group (or any other user/group you prefer) Full Control permissions to the profile folder and all its files and subfolders. The easiest way to do the latter, in my opinion, is to use PsExec from Sysinternals to start a command shell as SYSTEM and then use CACLS.EXE/XCACLS.EXE/ICACLS.EXE, or any other ACL editor, to add the Administrators group to the files/folders. Here’s an example using CACLS.EXE (which is included in every version of Windows since Windows 2000):
    1. Start the shell as SYSTEM: psexec.exe -i -s cmd.exe
    2. Change the permissions with CACLS.EXE: cacls.exe <root of profile share> /T /E /C /G Administrators:F
  • ADMT uses Server Message Block (SMB)  and Remote Procedure Calls (RPC) respectively, to install and communicate with it’s agents on machines when performing computer migrations or security translations.
    This means that you need to allow traffic from the ADMT machine to all the computers that you are going to migrate or translate security on, on these ports:
  • Traffic Port Notes
    RPC Endpoint mapper 135 TCP The endpoint mapper is like the RPC address book. Whenever an RPC connection is to be made; the client, in this case the ADMT machine, first connects to the endpoint mapper to find out which dynamic port the service it needs to talk to is listening on. It then makes a connection to that port.
    RPC Dynamic Ports Up to Windows Server 2008/Vista:
    1024 – 65535 TCPWindows Server 2008/Vista and later:
    49152 – 65535 TCP
    Any service using RPC talks to the RPC subsystem on the system where it is starting and asks for a port(s). This operation is performed each time the service starts and thus the port can potentially change on every service startup.
    Server Message Block (SMB) SMB Direct Hosting:
    445 TCPNetBIOS Over TCP/IP: 137/UDP
    137/TCP
    138/UDP
    138/TCP
    ADMT connects to the ADMIN$ share on computers it will either migrate or perform security translation on, and copies its files to the %windir%OnePointDomainAgent folder. It also installs a service on the machine called Active Directory Migration Service.
  • ADMT Agent source files
    Are stored in %windir%ADMTNT4Agent for Windows NT machines, or %windir%ADMTW2kAgent for Windows 2000 and above.
  • ADMT cannot migrate a user object that has child objects
    This is true for both inter and intra-forest migrations. Trying to do so will give you this error:
    ERR2:7422 Failed to move source object <RDN of user being migrated>. hr=0x8007208c  The operation cannot be performed because child objects exist. This operation can only be performed on a leaf object.
    The only case where I have found child objects for users are with Exchange Active Sync partnerships. These are stored in a sub container, called ExchangeActiveSyncDevices, as objects of class msExchActiveSyncDevice. Each device the user has an active partnership with has its own object. A typical name of the device object is iPhone§Appl690509FTA4S. It contains attributes of the device, ID, firmware, protocol version etc.You will have to get rid of these one way or the other if you want to migrate the user. If you just delete them, the user needs to set up sync on their devices from scratch once they are migrated. This is usually not a major problem if the users are warned beforehand that this will happen. Another way is to export the sub container and all the device objects, delete it (leaving the user as a leaf object), migrating, and finally importing the device objects back under the migrated user. This last solution requires that you change the distinguishedName attribute when you import the data, and it is probably not supported by Microsoft…at all. Here are some commands using LDIFDE.EXE to export and import the data. You can use this in a script before and after user migration with ADMT:
    Export:
    ldifde.exe –s <source domain DC> –f <export file> –d <DN of the ExchangeActiveSyncDevices container> -l * -o objectGUID
    Note that I have excluded the objectGUID attribute and included all others (-l *). When you import an object into Active Directory it will be assigned a new GUID. Importing a GUID, potentially causing two objects to have the same GUID, is a security violation, and the DC will refuse to perform it.
    Import:
    ldifde.exe -i –f <import file> –c <<old DN> replace with <new DN> (see LDIFDE help for more info)>
    Now, one thing about this. The device object has an attribute called msExchUserDisplayName. It has a value in the form of e.g.: <domain DNS name>/OU/OU/<user>. In other words an LDAP canonical name. If you perform and export/import as I have shown here, this attribute will contain the canonical name the user had in the source domain, i.e. the OLD canonical name. I know for a fact that this will break remote wipe and the Get-ActiveSyncDeviceStatistics cmdlet on Exchange 2007, and probably Exchange 2010. However, the device will continue to sync. (On a side note, this problem also occurs if you move a user within a domain. The msExchUserDisplayName attribute is not updated; breaking wipe and the cmdlet.) To work around this you can try to exclude the attribute when exporting with LDIDFE.EXE or implement code that changes it before you import it back into your target domain. I have not, as of now, tried any of these yet, but I am thinking that if I use PowerShell instead of LDIFDE.EXE to export the objects it would be easy to replace the old canonical name with the new one. I also do not know if removing the attribute from the device object will break the sync partnership. Another problem though, is the device. It stores the domain and username when you set up the account and usually won’t let you change it. The only thing you get to change is the password. So all this work may be for nothing, since users must update the account information on their devices anyway. Damn!
  • Inter-forest migrations do not exclude any attributes (except the SID…sort of)
    According to the ADMT documentation a system attribute exclusion list is created the very first time you run a user migration. The attributes in the list always include the attributes mail and proxyAddresses. I addition, says the docs, the objectGUID, objectSID and legacyExchangeDn attributes are also always excluded even if they are not on the system attribute exclusion list. However, when you do intra-forest migrations ALL attributes are copied. You can examine the system attribute exclusion list with this script, found in KB937537:
    Set o = CreateObject(“ADMT.Migration”)
    WScript.Echo o.SystemPropertiesToExclude
    When I ran this on an ADMT machine used for intra-forest migrations it returnes exactly zero attributes. Examining the migrated objects also verified that indeed all attributes had been copied. The mail, proxyAddressesobjectGUID and legacyExchangeDN were the same. The object had been given a new SID, which it had to because it was now living in a domain with a different domain SID, but the old SID had been copied into the sIDHistory attribute. Neat!

I will probably update this post with more info in the future. For now; Happy migrating!

Some SID Filtering Notes

  • SID Filtering is also known as Quarantine, Domain Quarantine, or SID Filtering Quarantine.
  • SID Filtering only applies to trusts, it cannot be enabled within a domain.
  • SID Filtering, by default, is not active on automatically created trusts within a forest. You can enable it, but not if the forest functional level is below Windows Server 2003. Doing so on any trust within a forest breaks replication. Additionally, if the forest functional level is Windows Server 2003 or higher; users with universal group memberships from other domains in the forest may loose access to resources if you enable SID Filtering on any of your trusts.
  • You can check the status of SID Filtering with the netdom.exe (Windows Domain Manager) command:
    • To verify the status of SID Filtering between two domains:
      netdom trust <TrustingDomainName> /domain: <TrustedDomainName> /quarantine
      Example output:
      SID filtering is not enabled for this trust. All SIDs presented in an authentication request from this domain will be honored.
      This is the default setting between domains in the same forest.
    • To verify the status of SID Filtering between two forests:
      netdom trust <TrustingDomainName> /domain: <TrustedDomainName> /enablesidhistory
      Example output:
      SID history is disabled for this trust.
      This is the default setting between trusting forests.
    • As you can see the two commands are nearly identical, but /quarantine applies only to domain trusts and /enablesidhistory is only valid for an outbound forest trust. They also output totally different messages making it hard to see that they actually apply to the same thing.
  • More info:

AdminSDHolder, Protected Groups, SDProp and moving mailboxes in Exchange

When you move a mailbox in Exchange 2000 or newer, you sometimes encounter an error saying that you have insufficient permissions to move the mailbox. Although that may be the case, usually this error is caused by the user object associated with the mailbox you are trying to move not having inheritable permissions enabled in Active Directory. This is also known as that the DACL is protected, i.e. it is special and should not be changed. But why is it protected?

Usually it is protected because the user was once a member of one of the protected administrative groups in Active Directory. (Notice the word ‘once’, it will be important later!) These groups are (per Windows Server 2008 R2):

  • Enterprise Admins
  • Schema Admins
  • Domain Admins
  • Administrators
  • Account Operators
  • Server Operators
  • Print Operators
  • Backup Operators
  • Cert Publishers

Any user who is a direct or indirect member of any of these groups will get a protected DACL (inheritable permissions turned off) and the attribute adminCount set to 1. SDProp, or the Security Descriptor Propagator, thread within the LSASS.EXE process is responsible for checking and applying these settings once an hour. Any change you make to a protected object will be reset within the hour.

So back to moving mailboxes.

If the user is a member of any of the protected groups, the move mailbox process completes successfully since Exchange is aware of how to handle that scenario. But if the user has been removed from all protected groups the move process fails (remember I said “once a member of”).

The reason is that the user still has a protected DACL and the adminCount attribute set to 1. SDProp does not reverse its changes when you remove a user from a protected group. This is a scenario that Exchange does not know how to handle and so throws the insufficient permissions error. The easiest way to resolve the problem is to go into the Security tab of the user object, select Advanced and hit the Restore Defaults button. That will enable inheritance of permissions and remove any ACEs that SDProp (or any other process) has set explicitly. One important thing to know is that the Restore Defaults button does not reset the adminCount attribute back to 0, so you still have a user object in a non-consistent state (it is inheriting permissions, but still flagged as a special object). The best practice would be to manually clear adminCount with you favorite DS tool when you also enable permission inheritance.

Now that we know this it would be a good idea to find all the users that would fail when we tried to move them. To do this we would need to construct a query with the following criteria:

  • Not a member of any of the protected groups
  • Has a mailbox
  • Has a protected DACL

My choice would be PowerShell. Here is an example using the Quest Active Directory cmdlets:

  1. Put all the known protected groups in an array:
    $ADProtectedGroups = @(“Enterprise Admins”,”Schema Admins”,”Domain Admins”,”Administrators”,”Account Operators”,”Server Operators”,”Print Operators”,”Backup Operators”,”Cert Publishers”)
  2. Find all the users and put them in an array:
    $mismatchedUsers = Get-qaduser -sizelimit 0 -securitymask DACL –NotIndirectMemberOf $ADProtectedGroups -IncludedProperties homeMDB | Where-Object {(($_.DirectoryEntry.PSBase.ObjectSecurity.AreAccessRulesProtected) –
    and ($_.homeMDB -ne $null))}
  3. View the users if you like:
    $mismatchedUsers | ft
  4. Fix the users:
    $mismatchedUsers | ForEach { Set-QADObjectSecurity $_ –UnLockInheritance | Set-QADObject -ObjectAttributes @{adminCount=$null} }

Note: You may also want to include adminCount=1 in your search to see which objects have it set, but you could then get back users that have been fixed before by pressing Restore Defaults or enabling inheritable permissions.

To search for users with adminCount=1:

$admincountUsers = Get-qaduser -sizelimit 0 -NotIndirectMemberOf $ADProtectedGroups -IncludedProperties homeMDB,adminCount | Where-Object {(($_.adminCount -eq 1) -and ($_.homeMDB -ne $null))}

Or to just find everything with adminCount=1:

Get-ADObject –LDAPFilter “(adminCount=1)”