Understanding Password Expiration Notifications in FIM 2010

Understanding Password Expiration Notifications in FIM 2010

Given FIM's strengths it seems only natural to leverage the notification capabilities to inform your users of impending password expiration. However, in order to do that you will need several items to start:

  • Contribute pwdLastSet to the FIM Service (This attribute is replicated)
  • Extract the ADS_UF_DONT_EXPIRE_PASSWD bit from userAccountControl and contribute to the FIM Service
  • Setup a series of Set transitions to trigger our notifications (see Set Transition Patterns for Password Notification below)

Now, these days I try to avoid purpose-built code and tend to build reusable modules, so I'm providing two code fragments at the end of this wiki for converting File Time attributes in AD to the precise ISO 8601 format that the FIM Web Service needs as well as a general method of extracting a bit from the userAccountControl bitmask and turning it into a Boolean attribute.

I'm not going to cover how to wire up FIM Sync here to contribute these values as there are enough examples now on how to get data into the FIM MA. I'm also not going to cover here how to wire up a basic Set Transition policy or use the Notification Workflow, but these topics could be covered if there is demand. Once you've wired in the attributes to the FIM Service, you'll need to work on your Set Transitions. After the Set Transitions are modeled, then you can build your policy and workflows to suit.

Set Transition Patterns for Password Notification

There are numerous ways to approach this, but I broke this down into three general patterns and one base pattern. For the sake of argument, let's assume we're modeling three basic notification events: 14 days, 7 days and 1 day. Also, in my examples I'm using ADPasswordLastSet as the FIM Service attribute I've contributed pwdLastSet into and ADPasswordDoesNotExpire as the Boolean attribute I've contributed from the extracted ADS_UF_DONT_EXPIRE_PASSWD. 

The Base Pattern

These two clauses should form the foundation of all of your sets, and they will be AND'd together with one of the general patterns.



In the first clause you are filtering out anyone who is not an active person, as identified by membership in another set. While this makes it handy for building sets that are dependent upon it, care must be taken that you are not violating any of the set nesting rules. You can also substitute this clause for a direct evaluation (EmployeeStatus = 'A'), for example. The bottom line here is to filter out inactive people; if you are sending password expiration notices to terminated people then you have issues with your Deprovisioning logic.

The second clause filters out anyone who has a password that does not expire as we want to avoid nagging them when their password isn't actually expiring despite the fact that it technically falls into the date ranges we care about.

These two clauses form the base of your rule and the following patterns will dictate the remainder of the query. You will pick the pattern below that fits your scenario best and add it to the list of AND'd clauses in your Set definition.

The All-inclusive Pattern

The first pattern is where you will likely find yourself after simple grouping into Sets:



To determine each milestone we use the following equation:

Milestone = Max Password Age – (Notification Event)

In this pattern we can correctly identify people as they reach each milestone; however, people never transition out of the preceding set. In some cases you simply may not care but I generally like to avoid this type of situation so I don't have people in sets they don't need to be in. There is one situation where this pattern is an advantage, and that is if you really want the ability to resend all of the outstanding notifications by triggering a Run On Policy Update (ROPU). In this case, the above pattern would trigger your 14 day and 7 day notifications for a person 3 days from expiration; if this is what you are after then this is the pattern for you. Also, keep in mind that if you wire up a policy to a ROPU enabled Action Workflow, you will send all of the notifications immediately.

A Regret

The most regrettable aspect of this pattern, and all subsequent patterns, is that we are hardcoding the Max Password Age and have no means to discover this at execution time. This means that we have to know a lot of about the infrastructure and model several variations of this approach if you are using Fine Grained Password Policy in Windows Server 2008. On to our next pattern...

The Short Staggered Pattern



This pattern (assuming a 90 day max password age) keeps the person object in each set for only one day – long enough to trigger the notification and then they are removed. So, in the above example, days 13 to 8 and days 6 to 2 the person is not in any of the expiration sets. How long they remain in each set is determined by the difference between the two clauses. If you wanted them to remain in the set for two days then you'd widen the gap by extending the Transition In clause by two days instead of one. Let's look at this in equation form:

Transition In = Max Password Age – (Notification Event – Membership Period)

Transition Out = Max Password Age – (Notification Event)

So, in our first example using the 14 day Notification Event, a 90 day Max Password Age, and a 1 day period we'd get:

77 = 90 – (14 – 1)

76 = 90 – 14

The notification behavior in this pattern would be to notify as objects entered each set.

The Skip Pattern

For the object conservationists, we have this pattern, where we leverage the extended period approach to skip every other set – this is because we can use both the rising and the falling edge of the transition using a Transition-In policy on the rising edge and a Transition-Out policy on the falling edge:



In this pattern, the person is in each set for the entire period (6 days in the first example, transitioning on day 7), allowing you to use Run On Policy Update anytime during that period. It also allows you to reduce the number of Set objects you are constructing (you didn't build the 7 to 2 day Set) if you don't mind using Transition-Out policies for the next transition. Your policies would look like so:

  • Notify at 14 days from Expiration – Transition-In (14 to 8 Days Set)
  • Notify at 7 days from Expiration – Transition-Out (14 to 8 Days Set)

You trigger the second policy because you  transitioned out after the 7th day…just remember that when you are in the 7 to 2 day range you cannot use Run On Policy Update to trigger any notifications until you reach the next Set transition.

Code Fragments

ConvertFileTimeToISO8601

if (FlowRuleName.StartsWith("ConvertFileTimeToISO8601:"))
{
    // 1/6/10 bturner
    //
    // Reusable code to convert file time into string format
    // FlowRuleName will be passed as "ConvertFileTimeToISO8601:sourceAttribute,destinationAttribute"
    // Source value should look like 9223372036854775807
    // Should return time formatted as such: 2009-11-06T07:00:00.000
    //
    string strAttributeName, strSourceAttribute, strDestinationAttribute;
    string[] arrAttribs;
    // Replace the beginning of the flowrulename with nothing to find the attribute to be deleted
    strAttributeName = FlowRuleName.Replace("ConvertFileTimeToISO8601:", "");
    arrAttribs = strAttributeName.Split(',');
    strSourceAttribute = arrAttribs[0];
    strDestinationAttribute = arrAttribs[1];
    //none,accountExpires,number,9223372036854775807,9223372036854775807
    if (csentry[strSourceAttribute].IntegerValue == 0 || csentry[strSourceAttribute].IntegerValue == 9223372036854775807)
    {
        // This is a special condition, do not contribute and delete any current value
        mventry[strDestinationAttribute].Delete();
    }
    else
    {
        DateTime dtFileTime = DateTime.FromFileTime(csentry[strSourceAttribute].IntegerValue);
        // Convert to UTC, format string using custom format similiar to round trip "o" format
        // NOTE: SQL's precision for fractional time makes storage and confirmation of anything more than two digits problematic
        //      It's better to simply enforce .000 for fractional time here since it's not absolutely critical
        mventry[strDestinationAttribute].Value = dtFileTime.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.000'");
    }
}

GetUACBit

if (FlowRuleName.StartsWith("GetUACBit:"))
{
    // 12/7/10 - bturner
    //
    // I A F 
    // [csentry                   -> mventry                              ]
    // [user                      -> person                               ]
    // [userAccountControl        -> destinationAttribute (Boolean)       ] 
    //
    // Check the userAccountControlStatus for the enable/disable bit (bit 1)
    // FlowRuleName will be passed as "GetUACBit:destinationAttribute,intADS_USER_FLAG"
    // Where intADS_USER_FLAG is the integer form of the bit you are testing the mask for
    //
    // For a list of userAccountControl bits and their decimal equivalents:
    //
    // Example: 
    //      GetUACBit:ADPwdDoesNotExpire,65536
    //
    string strAttributeName, strDestinationAttribute;
    long intADS_USER_FLAG;
    string[] arrAttribs;
    // Replace the beginning of the flowrulename with nothing to find the attribute to be deleted
    strAttributeName = FlowRuleName.Replace("GetUACBit:", "");
    arrAttribs = strAttributeName.Split(',');
    strDestinationAttribute = arrAttribs[0];
    intADS_USER_FLAG = Convert.ToInt64(arrAttribs[1]);
    mventry[strDestinationAttribute].BooleanValue = ((csentry["userAccountControl"].IntegerValue & intADS_USER_FLAG) == intADS_USER_FLAG);
         
}
Sort by: Published Date | Most Recent | Most Useful
Comments
  • NOTE: accountExpirese uses the same dateTime format as pwdLastSet, so the same process and code can be used to send notifications of impending account expiration.

  • Thanks for nice article. I was looking for same. I have already Contribute pwdLastSet to the FIM Service & it is workign fine. I am not clear on second part  "Extract the ADS_UF_DONT_EXPIRE_PASSWD bit from userAccountControl and contribute to the FIM Service "

    How to do this? DO we need to create rule extension using this code provided or what to do.

    Pls bear with me if I am missing some thing in this article. IF you can pls simplyfy it will be of more help

    Thanks

    Anand K

  • @Anand - yes, you will need to contribute this value into the portal so you can filter out people whose passwords never expire. If you do not, then you will be sending them notifications based solely on the date their password was last set.  

    Use the GetUACBit code to populate ADPwdDoesNotExpire, which is used in your base query to apply the filter.

  • Hi Brad

    Thanks for you reply. I am still not clear on how to contribute password never expire bit to portal. How to use the code provided by you? do we need to create a rule extensions using VS?

    could you Pls explain again?

  • Hi Brad

    I m still struggling with code given by you . Not able to build dll out of it. I don't know what I am missing in this code. Created a project in VB & Copy/Paste this entire code under Public Sub MapAttributesForImport in ADPSWDNOEXPIRE.vb file. But it is giving me few errors like " String is class type & can not be used as Expression" "Long is a type & can not be used as Expression". '.'expected.

    I am not very much comfortable with coding, so if anybody pl explain or simplify it further will be of great help..

    will request  for another article for flowing such Rule extensions through FIM sync service to Portal.

  • @Anand: I think the following two links will be helpful, also the snippets are written in C#, not VB.NET:

    Introduction to Inbound Synchronizaton:

    technet.microsoft.com/.../ee534911(WS.10).aspx

    How to: Create Management Agent Rules Extensions:

    msdn.microsoft.com/.../ms695363.aspx

  • Hi Brad

    Some how I am manged to complie this code. But now facing one issue while doing AD MA sync it is giving error

    "Microsoft.MetadirectoryServices.EntryPointNotImplementedException: Error in the application.

      at Mms_ManagementAgent_ADPwdDoesNotExpire.MAExtensionObject.Microsoft.MetadirectoryServices.IMASynchronization.MapAttributesForImport(String FlowRuleName, CSEntry csentry, MVEntry mventry)"

    What will be the flow rule name which needs to be passed in import flow? right now I am giving it as GetUACBit:

    Pls reply..thanks in advance..

  • This is great for domains on Windows Server 2003 and below, but as Brad points out this breaks down on Windows 2008 domains if they use Fine Grained Password Policy.  Windows 2008 also adds a new computed attribute msDS-UserPasswordExpiryTimeComputed msdn.microsoft.com/.../ms677839(v=VS.85).aspx that can make this easier since it takes the Fine Grained Password Policies into account. However, it is a calculated attribute and hence it won't import into the AD MA (even though it shows up in the list of attributes in the MA Select Attributes screen).

    So we would need to load the data into FIM through some other means, an MA that makes a series of PowerShell calls to retrieve the data, or a PowerShell script that updates the data in the FIM Service. I believe that the "owerShell MA" approach would be more efficient. I found a blog post that offers up the germ of what could become that MA ahultgren.blogspot.com/.../powershell-active-directory-and.html.

    He even translates the date to a Number of Days left which can make defining a set very easy.

Page 1 of 1 (8 items)