Managing AD boolean-in-integer flagsets with FIM

Active Directory, for reasons best known to itself, stores many boolean attributes (such as userAccountControl or msExchELCMailboxFlags) together in an aggregate integer rather than separating them into individual attributes.

This presents a problem when manipulating only part of that data while preserving the rest.

For example, to disable a user’s account, you must set the userAccountControl attribute to 0x0202 (0x002 + 0x0200). Which, in decimal, is 514 (2 + 512).

Doing this sort of arithmetic is fine if you know what all the flags should be. If, however, you only want to flip one of them (for example to activate an Archive Mailbox on a user while preserving the Litigation Hold flag), you need to be a bit more subtle.

I’m going to start with a little helper function which we use in FIM to manage the msExchELCMailboxFlags attribute:

Private Function BitwiseSet(flagset As Integer, pos As Integer, value As Boolean) As Integer
   pos = 2 ^ (pos - 1)
   If value Then
      Return flagset Or pos
   Else
      Return flagset And Not pos
   End If
End Function

This function takes an existing integer to work on, an offset for the flag (least significant bit is 1 rather than 0) and a boolean (true/false) of what that bit should be set to. For example, to set ElcV2 (2) and ValidArchiveDatabase and (32) both to true, enabling the archive mailbox) we’d call:

    BitwiseSet(msExchELCMailboxFlags, 2, True)
    BitwiseSet(msExchELCMailboxFlags, 6, True)

Tying that in to FIM’s export attribute flow API (MapAttributesForExport), this ends up looking like:

Public Sub MapAttributesForExport _
   (ByVal FlowRuleName As String, ByVal mventry As MVEntry, ByVal csentry As CSEntry) _
   Implements IMASynchronization.MapAttributesForExport
 Select Case FlowRuleName
 Case "export:msExchELCMailboxFlags"
    'Const ExpirationSuspended As Integer = 1
    Const ElcV2 As Integer = 2
    'Const DisableCalendarLogging As Integer = 3
    'Const LitigationHold As Integer = 4
    'Const SingleItemRecovert As Integer = 5
    Const ValidArchiveDatabase As Integer = 6

    Dim msExchELCMailboxFlags As Integer
    If csentry("msExchELCMailboxFlags").IsPresent Then
       msExchELCMailboxFlags = csObject._msExchELCMailboxFlags.Value
    Else
       msExchELCMailboxFlags = 0 ' Nothing
    End If

    If isMailboxUser(mvObject) Then
       msExchELCMailboxFlags = _
           BitwiseSet(msExchELCMailboxFlags, ElcV2, True)
       msExchELCMailboxFlags = _
           BitwiseSet(msExchELCMailboxFlags, ValidArchiveDatabase, True)
    Else
       msExchELCMailboxFlags = _
           BitwiseSet(msExchELCMailboxFlags, ElcV2, False)
       msExchELCMailboxFlags = _
           BitwiseSet(msExchELCMailboxFlags, ValidArchiveDatabase, False)
    End If

    If msExchELCMailboxFlags = 0 Then
       If csentry("msExchELCMailboxFlags").IsPresent Then
          csentry("msExchELCMailboxFlags").Delete()
       End If
    Else
       csentry("msExchELCMailboxFlags").Value = msExchELCMailboxFlags
    End If
 Case Else
    Throw New UnexpectedDataException(String.Format("MapAttributesForExport: Unknown flow rule '{0}'", FlowRuleName))
 End Select
End Sub

Leave a Reply