How to use Sort Keys in LDP

I knocked up an example PowerShell one liner for a colleague to get the oldest item in the Deleted Objects container because of a need to identify the default tombstone lifetime (i.e. when there’s no value on the nTDSService object’s tombstoneLifetime attribute) but the environment in question didn’t have Active Directory Web Services (ADWS) thus the PowerShell one liner (I’ve listed it at the end of this post for those who care) wouldn’t work.  In these cases (when ADFIND isn’t available) I fall back to LDP –you can probably tell from this blog that I use LDP a lot.  It took me a good ten minutes to work out how to use the LDAP_SERVER_SORT_OID control (1.2.840.113556.1.4.473) so I thought I’d post how to sort results here.

For the purpose of this example I’ll describe how to list the tombstoned objects in descending order.

  1. Open LDP(Start | Run | LDP), connect and bind to the directory.
  2. Click Options | Controls(Ctrl + L).
  3. Check in the LDAP_SERVER_SHOW_DELETED_OID control by simply selecting “Return deleted objects” from the “Load Predefined” list and click OK.
  4. Click View | Tree (Ctrl + T) and press enter(leaving the Base DN empty).
  5. Expand the domain NC and right-click on CN=Deleted Objects, <your DN goes here> and click Search.
  6. Set the Scope to One Level.  Change the attributes to objectClass;name;whenChanged and click Options.
  7. Select Extended for the Search Call Type and click Sort Keys.
  8. Enter whenChanged for the Attribute Type, leave Match Rule OID empty, click Reverse Order and then Check In >>
  9. Click OK to close the Search Options dialog and click Run to execute the search.

The number of results is based on the values in Search Options.  By default it’s pretty low so you just need to look at the top most result to see when that object was deleted and then gauge the tombstone lifetime.

It’s pretty simple really.  Server-side sorting is pretty limited as a whole – you can only sort ascending or descending on one attribute and only a subset of attribute types are allowed; and also expensive.  In almost all cases you’ll want to order the data at the client side (like I do in PowerShell below) but from time to time the server-side sorting feature comes in handy.

Anyway, here’s the PowerShell that uses the ActiveDirectory module:

[String]$aDPSModuleName = "ActiveDirectory";
if(@(Get-Module -Name $aDPSModuleName).Count -eq 0)
{
    if(@(Get-Module -ListAvailable | ? { $_.Name -eq $aDPSModuleName }).Count -eq 1)
    {
        Import-Module -Name $aDPSModuleName;
    }
}

[String]$delObjFilter = 'objectClass -like "*"';
[String]$delObjearchBase = "CN=Deleted Objects,DC=corp,DC=contoso,DC=com";
[String[]]$delObjPropertiesToFetch = @( "whenChanged", "lastKnownParent" );

Get-ADObject `
    -Filter $delObjFilter `
    -IncludedelObj `
    -SearchBase $delObjearchBase `
    -SearchScope OneLevel `
    -Properties $delObjPropertiesToFetch |
        Sort-Object -Property whenChanged |
            Select-Object -Last 3 | Format-Table whenChanged;

When I said one line I slightly exaggerated.  There’s an if statement to load the module if it isn’t already and is available and I make use of some attributes to make the command easier to read.  Smile

Posted in Active Directory, Active Directory Domain Services, PowerShell, Scripting | Tagged , , , , , , | Leave a comment

An update on the error: A directory service error has occurred. (Exception from HRESULT: 0×80072095)

Back in December last I posted Synchronization Service Manager: A directory service error has occurred. (Exception from HRESULT: 0×80072095).  I have updated this post with some additional information, namely the alternate option:

Create the following containers in the configuration container of the AD DS that you are unable to create an ADMA for:

  • CN=Organisation Name, CN=Microsoft Exchange, CN=Services, CN=Configuration, DC=corp, DC=contoso, DC=com
  • CN=Microsoft Exchange, CN=Services, CN=Configuration, DC=corp, DC=contoso, DC=com

Both of these containers are of the type (objectClass of): msExchOrganizationContainer.  Set the objectVersion attribute of the child container (CN=Organisation Name in the above example).

I want to point something else out too.  If you create the containers and forget to set the objectVersion (or possibly use an incorrect version –haven’t tested that one) you will get the following error:

clip_image002

Repeated in textual format:

Object reference not set to an instance of an object.

Setting a valid objectVersion should fix this issue.  In my basic testing I set the objectVersion attribute to 13214 –the value that another Exchange 2010 organisation had.  After doing this I was able to create an ADMA again.

Note

Only the CN=<organisation name> object requires an objectVersion attribute.

Posted in ADMA, FIM, FIM 2010, Troubleshooting | Tagged , , , , , , , , | Leave a comment

The INPROC service partition

Have you ever noticed that some requests have a service partition that is nothing to do with you?  Over time you might notice that certain system operations, such as the application of an update, create requests.  If you inspect these requests you’ll notice that the service partition name is of the format:

  • <hostname>-INPROC

Where <hostname> is the computer name of one of your FIM Service nodes (the FIM Service node processing the request actually), e.g.

  • WSVAPPFIMSVC03-INPROC

The reason for –INPROC is that the operation was performed by the system.  The FIM MA makes use of the FIM Service EXE (the private APIs) to make changes to the FIM database.  Service partition name is a mandatory attribute of the Request resource, i.e. every request requires a service partition name, therefore requests that come in from a “non-running FIM service” will have the partition HOSTNAME–INPROC.  The same is true of requests that are created as a result of upgrade.

Hopefully that clears that up.  A colleague reminded me about this piece of inane trivia after running a my script to update the ma-data and mv-data resources and I felt the need to post the information here.

 

Posted in FIM, FIM 2010, FIM 2010 R2 | Tagged , , , , , , , , , | Leave a comment

Seriously off topic: Ignoring spelling and grammar in certain text styles in Microsoft Word

When writing detailed design and deployment guides I get frustrated by Word rightly having issue with the spelling and grammar of my PowerShell snippets.  It makes the Spelling and Grammar checker almost redundant, for example, in my 374 page detailed design document.  I’ve recently realised how to make life easier.  I’m slowly but surely moving from a raw document formatted using indentation, bold, italic and the direct application of fonts to styles, i.e. the way things are supposed to be done in Word.  As I make this transition I’m more productive, but I digress.  Here’s my off-topic tip:  Exclude certain styles from the spelling and grammar checker.

I’ve modified the style Code Block such that I have a paragraph in the Courier New font, size 8pt, enclosed in single, black-line borders.  To stop the spelling and grammar checker from complaining about lowercase sentence beginnings and misspelled words I did the following:

  • Selected some text that has the Code Block style applied.
  • Clicked the Styles button (Alt + Ctrl + Shift + S) –I use this a lot (the inspector too), and then clicked Style Inspector.
  • From the style inspector I click the drop-down on the Code Block style and choose Modify…
  • Next I click Format| Language and then check Do not check spelling or grammar
  • Followed by OK and OK.

And then I’m done.  Any text marked as Code Block (which I use for not just code but PowerShell syntax examples and console output from deployment scripts, etc.) is excluded from the spelling and grammar checker.  Awesome!

I use a similar technique for attribute names.  I have created a custom style called attributeNames that formats on a per-word basis a word into Courier New (same pt. size as Normal).  When writing lower camel case (e.g. metaverse attribute naming) or upper camel case names (e.g. FIM Service attribute naming), e.g. mDBUseDefaults or DisplayName, I apply this style.  The attribute is emphasised through the font but also exempt from the spelling and grammar checker.

 

Posted in Off-topic | Tagged , , , , , | Leave a comment

Delegating the minimum set of permissions for mailbox-enabled user and linked mailbox provisioning

In my previous post I described the minimum set of permissions required by the ADMA account to provision an AD DS user object.  In this post I’d like to expand on that and provide the minimum set of permissions required to provision a mailbox-enabled user and a linked-mailbox.  As with the previous post I will also cover additional non-essential but expected and required (from an implementation perspective) attributes.

Creating a user

In the previous post I stated that the minimum set of permissions to create an enabled user and delete and move a user are as follows.

Note.  I’m using the DSACLS nomenclature including the inheritance options.  From the DSACLS help:

  • T.  The object and its child objects.
  • S.  The child objects only.
  • P.  The object and child objects down to one level only (propagate inheritable permissions to one level only)
Permission/Access right Object type/ Attribute Inherited object type Inheritance
CC user    
DC user    
CC user organizationalUnit S
DC user organizationalUnit S
Reset Password   user S
WP cn user S
WP distinguishedName user S
WP name user S
WP userAccountControl user S

Those are the core permissions.  You’ll obviously want more permissions than that.  Read the rest of the post for more info.  The purpose of this post is mailbox-enabled users and linked mailboxes.  The next two sections describe the minimum permissions required for creating a mailbox-enabled user and linked mailbox respectively.  Each section is written in isolation, i.e. the linked mailbox section is not dependent on the mailbox-enabled user section, however both sections are dependent on the information written above.

Creating a mailbox-enabled user

To create a mailbox-enabled user we need to write the following additional attributes to the user before we run Update-Recipient and complete the process.

  • homeMDB
  • mailNickname
  • msExchHomeServerName

An optional (from the perspective that the mailbox will created without it) but essential (from the perspective of recommended settings) addition to the list is:

  • mDBUseDefaults

Therefore to grant the permissions to write these attributes we need to add the following ACEs.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP homeMDB user S
WP mailNickname user S
WP mDBUseDefaults user S
WP msExchHomeServerName user S

Next you need permissions to run the Update-Recipient cmdlet.  The current guidance states that you should add the ADMA account to the Recipient Administrators group however that grants too much access therefore you should create a custom role group as I describe in this post.

Note.

The FIM PG will publish revised documentation on minimum permissions and firewall ports and the advice in this post will align with the information in that documentation refresh.

It is highly likely that you will want to synchronise additional attributes. The previous post defines a bunch of common attributes and describes how to implement the permissions for those attributes.

Creating a linked mailbox

A linked mailbox requires provisioning code.  I don’t think we can create a linked mailbox via declarative provisioning but I might be wrong.  But I digress.  To create a linked mailbox you would use either ExchangeUtils::CreateMailbox(ConnectedMA, ReferenceValue, String, String, Byte[]) or ExchangeUtils::CreateMailbox(ConnectedMA, ReferenceValue, String, String, long, long, long, Byte[]) which sets the following attributes (that we have not yet factored into our permissions list):

  • homeMDB
  • mailNickname
  • mDBUseDefaults
  • msExchHomeServerName
  • msExchMailboxSecurityDescriptor
  • msExchMasterAccountSid
  • nTSecurityDescriptor

The following permissions therefore need to be granted.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP homeMDB user S
WP mailNickname user S
WP mDBUseDefaults user S
WP msExchHomeServerName user S
WP msExchMailboxSecurityDescriptor user S
WP msExchMasterAccountSid user S
WP nTSecurityDescriptor user S

Next you need permissions to run the Update-Recipient cmdlet. The current guidance states that you should add the ADMA account to the Recipient Administrators group however that grants too much access therefore you should create a custom role group as I describe in this post.

Note.

The FIM PG will publish revised documentation on minimum permissions and firewall ports and the advice in this post will be included in that documentation refresh.

In addition a type of privilege is also required – a user right: SeRestorePrivilege, a.k.a. Restore files and directories.  This user right must be granted to the ADMA account on all domain controllers.  For more information see this post.

Note.

When you provision a linked mailbox you will receive an exported-change-not-reimported error on the subsequent import.  This is because we need to normalise the SD just like AD does.  The registry value ADMADoNormalization turns this feature on however doing so when implementing least privilege will break the export.  More information in this post.  Until the bug is fixed (I have logged it) I am choosing to not enable ADMADoNormalization and instead ignore the exported-change-not-reimported error.

Wrap up

I’ve discussed the additional permissions required for a mailbox-enabled user and a linked mailbox.  I haven’t discussed GAL attributes as they’re covered in the previous post.  If there are other attributes that you need you can now see how to grant access to those attributes.

This post specifically targets Exchange Server 2010.  The permissions are pretty much the same as Exchange Server 2007.  IIRC e2k7 doesn’t require msExchHomeServerName.  Exchange Server 2003 is a little different but the premise is the same.

The import points I want to get across are this:

  1. Endevour to follow the practice of least privilege in all aspects of FIM.  And with the ADMA don’t put up with administrative access!
  2. The “protect this container from accidental deletion” option in 2008+ tools will break move and delete permissions.
  3. Linked mailboxes require SeRestorePrivilege on domain controllers.

Hopefully this helps someone.

Posted in Active Directory, ADMA, Exchange, FIM, FIM 2010, FIM 2010 R2, ILM, Provisioning | Tagged , , , , , , , , , , , , , , , , | Leave a comment

Delegating the minimum set of permissions for user provisioning

The purpose of this post is to provide information on the permissions required by the user account that the Active Directory Domain Services (AD DS) Management Agent (MA) or ADMA uses when it interfaces with an AD domain.  I’ve seen far too many implementations of FIM –both in the various types of lab and in production that grant the ADMA accounts more permissions and privileges than they require.  In this post I’d like to discuss the minimum permissions required to perform certain operations as well as a general approach on how to actually deploy the permissions to an AD domain.

Note. I’m using the DSACLS nomenclature including the inheritance options. From the DSACLS help:

  • T. The object and its child objects.
  • S. The child objects only.
  • P. The object and child objects down to one level only (propagate inheritable permissions to one level only)

We’ll start off by discussing the permissions required to create, delete and move (between containers) users.

Creating, deleting and moving a user

To create a user you require create child permissions on the parent container.  No other permissions are required.  The created user is disabled as it has no password.  To create an enabled user you require the following permissions.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
CC user
WP userAccountControl user S
Reset Password user S

In addition to create child you also need the ability to set the password (the Reset Password control access right is required for this operation) and then change the userAccountControl bitmask such that the user is enabled, e.g. you might set the password and set userAccountControl to 512.

To delete a user you obviously require delete permissions too.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
DC user

If the permissions need to be set such that users can be created in child OUs the following are also required.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
CC user organizationalUnit S
DC user organizationalUnit S

If you want to move a user then you need to add the following permissions in addition to those defined for create and delete.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP cn user S
WP distinguishedName user S
WP name user S

Moving a user is a rename. We write the distinguishedName but the LDAP operation also requires that we change the CN and the name (the RDN, i.e. CN={CN}). The operation also requires delete child on the source and create child on the target.

Note.

The Windows Server 2008 Active Directory Users and Computers (ADUC) snap-in option “Protect object from accidental deletion” writes a deny { delete, delete tree } ACE for the EVERYONE principal that will interfere with the above permissions.  The ACE is explicit, i.e. not inherited, and therefore takes precedence over other permissions.

Additional properties

The preceding example provided the minimum permissions.  Creating a user with nothing more than a DN isn’t really all that helpful.  Additional attributes are generally required.  The sAMAccountName is pretty much mandatory from a provisioning perspective.  It is a mandatory attribute from the perspective of the security accounts manager (SAM) however the directory server will automatically generate one if you don’t provide it (Windows Server 2003 and later).  The user principal name is also a common expectation although it isn’t strictly necessary as users have an implicit UPN (sAMAccountName @ domain DNS name).  The following tables define some additional permissions required for typical account provisioning scenarios.

GAL/ADUC properties

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP c user S
WP co user S
WP company user S
WP countryCode user S
WP department user S
WP displayName user S
WP facsimileTelephoneNumber user S
WP givenName user S
WP initials user S
WP l user S
WP manager user S
WP mobile user S
WP pager user S
WP physicalDeliverOfficeName user S
WP postalCode user S
WP sn user S
WP st user S
WP streetAddress user S
WP telephoneNumber user S
WP title user S

Other attributes

Some additional attributes that are often flowed.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP employeeID user S
WP employeeType user S

Deploying permissions

The permissions need to be deployed on a per-container basis.  For example, assuming the following OU structure (over and above the default containers which are out of scope):

  • OU=employees, OU=people, DC=corp, DC=contoso, DC=com
  • OU=contractors, OU=people, DC=corp, DC=contoso, DC=com
  • OU=employees, OU=people, DC=corp, DC=contoso, DC=com
  • OU=vip,DC=corp, DC=contoso, DC=com
  • OU=deleted-users,DC=corp, DC=contoso, DC=com

The aforementioned permissions need to be applied to each OU.  It doesn’t make sense to apply the permissions at the root of the naming context (DC=corp, DC=contoso, DC=com) as then they’ll apply to all the other containers not listed above.  We don’t want to delegate permissions throughout the domain.  What if there’s also an OU=privileged-accounts,DC=corp, DC=contoso, DC=com and an OU=service-admins,DC=corp, DC=contoso, DC=com?

Applying the consistent set of permissions means that FIM can create and delete users as well as move users between containers and write the necessary properties.  The only way to set granular permissions in a consistent way is via script.  DSACLS should be the tool of choice.

An example script

Up until recently I’ve just used a CMD script –an extension of the script here.  However recently I’ve been trying to write all scripts and commands in PowerShell to make my deployment documents more consistent so here’s an example PS script that can enable the permissions described above for the containers also listed above.

# UserProvisioningPermissions.ps1 v1.0 
#   Paul Williams (pawill@microsoft.com) Microsoft Services Feb. 2010

PARAM
(
    [Parameter(Mandatory = $true)]
    [String]$Target,

    [Parameter(Mandatory = $true)]
    [String]$Trustee
);

Write-Host "`nTarget:  $Target`nTrustee: $Trustee`n";

[String[]]$attributes = @(
    "c",
    "cn",
    "co",
    "company",
    "countryCode",
    "department",
    "displayName",
    "distinguishedName",
    "facsimileTelephoneNumber",
    "givenName",
    "initials",
    "l",
    "manager",
    "mobile",
    "name",
    "pager",
    "physicalDeliverOfficeName",
    "postalCode",
    "sn",
    "st",
    "streetAddress",
    "telephoneNumber",
    "title"
);

Write-Host "Granting the following permissions...";
Write-Host "Create/Delete user objects (this object)";
[String]$cmd = "dsacls '$Target' /G '`"$Trustee`":CCDC;user;'";
Invoke-Expression $cmd |Out-Null;

Write-Host "Create/Delete user objects (child OUs)";
[String]$cmd = "dsacls '$Target' /I:S /G '`"$Trustee`":CCDC;user;organizationalUnit'";
Invoke-Expression $cmd |Out-Null;

Write-Host '"Reset Password" Control Access Right (CAS)';
[String]$cmd = "dsacls '$Target' /I:S /G '`"$Trustee`":CA;`"Reset Password`";user'";
Invoke-Expression $cmd |Out-Null;

foreach($attr in $attributes)
{
    Write-Host "Write Property (WP) $attr on descendent user objects";
    [String]$cmd = "dsacls '$Target' /I:S /G '`"$Trustee`":WP;$attr;user'";
    Invoke-Expression $cmd |Out-Null;
}

Write-Host "`nScript complete.";

Note

The script defines both “this object only” and the “all descendent organizationalUnit objects” inherited permissions for CC and DC, thus when applying the script to OU=People the resultant permissions are such that users can be created in OU=People or any OU underneath OU=People.

And here’s the command(s) that invoke the script.  In this example the script is called UserProvisioningPermissions.ps1 and I’m invoking it from the current directory.

.\UserProvisioningPermissions.ps1 `
    -Target "OU=people, DC=corp, DC=contoso, DC=com" `
    -Trustee "CORP\ADMAUserProvisioningPermissions";

Wrap up

The intention of this post is to provide you with enough information to define granular, precise permissions for the ADMA and not use some all encompassing group.  In addition to defining the minimum permissions for create, delete and move I also provided some additional attributes often used when provisioning and synchronising with an AD DS domain.  I provided an example script to automate the deployment of the permissions and an example of how to use it.

Given the script in the preceding section and the OUs listed in the section before that we would successfully deploy the permissions in this article by running the script three times –once against each of the following OUs.

  • OU=people, DC=corp, DC=contoso, DC=com
  • OU=vip,DC=corp, DC=contoso, DC=com
  • OU=deleted-users,DC=corp, DC=contoso, DC=com

The following is an example of again using PowerShell to make life easier.

[String[]]$containers = @(
    "OU=people, DC=corp, DC=contoso, DC=com",  
    "OU=vip,DC=corp, DC=contoso, DC=com", 
    "OU=deleted-users,DC=corp, DC=contoso, DC=com"
);

foreach($target in $containers)
{
    .\UserProvisioningPermissions.ps1 `
        -Target $target `
        -Trustee "CORP\ADMAUserProvisioningPermissions";
}

Posted in Active Directory, Active Directory Domain Services, ADMA, FIM, FIM 2010, FIM 2010 R2, Provisioning, Synchronization | Tagged , , , , , , , , , , , , , , , | 1 Comment

sp_executeSQL to the rescue

(a.k.a. my DEFAULT constraints aren’t working –NULL is being written to the DB.  Where are my default values?)

I encountered a bug in a stored procedure I’d written that took me a little longer to fix than it should have that led me to another aspect of SQL that I’d not used and thought was both cool and essential and worth a post.  Basically the stored procedure in question adds a row to the person table.  I fire the procedure from a PowerShell script and provide the variables as parameters.  The database is normalised (as best I could – I think I’ve got as far as 3NF) so I need to convert the string input into IDs which is where the stored procedure comes in.  Anyway to cut a long story short once I’ve done my validation I run the following:

BEGIN TRY
	INSERT INTO Person VALUES
	(
		@UniqueName,
		@AltUniqueName,
		@DisplayName,
		@teamID,
		@mgrID,
		@empID,
		@secID,
		@natID,
		@jobID
	);

	SET @rval = 1;
	RETURN;
END TRY

The problem is that some of those values can be NULL.  Or more specifically the user doesn’t input a value for job title (mapped to jobID) or employee type (mapped to empID) for example.  The bug was that the NULL was making it into the table.  This was initially unexpected as there are default constraints in place.  I assumed that supplying NULL via the table valued constructor would result in the value specified in the default constraint being used.  This isn’t the case however.  The ANSI standard behaviour is that a NOT NULL column with a DEFAULT constraint will not use the default if the column is specified in the INSERT statement.  That is, specifically providing NULL actually writes NULL into the tuple and does not utilise the DEFAULT constraint.

OK.  How do we fix this?  Dynamic SQL.  Specifically EXECUTE and sQLCommandString.  Here’s how I replaced the above to get the desired behaviour:

	DECLARE @sQLCommandString NVARCHAR(MAX) = N'INSERT INTO Person VALUES ( ' +
		'@UniqueName, @AltUniqueName, @DisplayName, @teamID, @mgrID, ' +
		CASE WHEN @empID IS NULL THEN 'DEFAULT' ELSE '@empID' END + ', ' +
		CASE WHEN @secID IS NULL THEN 'DEFAULT' ELSE '@secID' END + ', @natID, ' +
		CASE WHEN @jobID IS NULL THEN 'DEFAULT' ELSE '@jobID' END + ' );';

	DECLARE @paramDefinition NVARCHAR(MAX) = N'@UniqueName NVARCHAR(256), ' +
		'@AltUniqueName NVARCHAR(20), @DisplayName NVARCHAR(256), ' +
		'@teamID BIGINT, @mgrID BIGINT, @empID SMALLINT, @secID BIGINT, ' +
		'@natID BIGINT, @jobID BIGINT';

	BEGIN TRY
		EXECUTE sp_executeSQL @sQLCommandString, @paramDefinition,
			@UniqueName = @UniqueName,
			@AltUniqueName = @AltUniqueName,
			@DisplayName = @DisplayName,
			@teamID = @teamID,
			@mgrID = @mgrID,
			@empID = @empID,
			@secID = @secID,
			@natID = @natID,
			@jobID = @jobID;

		SET @rval = 1;
		RETURN;
	END TRY

I build a dynamic SQL statement and execute that.  What’s cool about sp_executesql is that it understands variables.  The above is a little lengthier than the original version but vastly better than a bunch of IF ELSE statements utilising IS NULL and IS NOT NULL to determine what values to input into the table valued constructor.

Posted in Off-topic, SQL Server, T-SQL, Troubleshooting | Tagged , , , , , , , , | Leave a comment