Blog - Title

May, 2013

  • Back to the Loopback: Troubleshooting Group Policy loopback processing, Part 2

    Welcome back!  Kim Nichols here once again with the much anticipated Part 2 to Circle Back to Loopback.  Thanks for all the comments and feedback on Part 1.  For those of you joining us a little late in the game, you'll want to check out Part 1: Circle Back to Loopback before reading further.

    In my first post, the goal was to keep it simple.  Now, we're going to go into a little more detail to help you identify and troubleshoot Group Policy issues related to loopback processing.  If you follow these steps, you should be able to apply what you've learned to any loopback scenario that you may run into (assuming that the environment is healthy and there are no other policy infrastructure issues).

    To troubleshoot loopback processing you need to know and understand:

    1. The status of the loopback configuration.  Is it enabled, and if so, in which mode?
    2. The desired state configuration vs. the actual state configuration of applied policy
    3. Which settings from which GPOs are "supposed" to be applied?
    4. To whom should the settings apply or not apply?
      1. The security filtering requirements when using loopback
      2. Is the loopback setting configured in the same GPO or a separate GPO from the user settings?
      3. Are the user settings configured in a GPO with computer settings?

    What you need to know:

    Know if loopback is enabled and in which mode

    The first step in troubleshooting loopback is to know that it is enabled.  It seems pretty obvious, I know, but often loopback is enabled by one administrator in one GPO without understanding that the setting will impact all computers that apply the GPO.  This gets back to Part 1 of this blog . . . loopback processing is a computer configuration setting. 

    Take a deep cleansing breath and say it again . . . Loopback processing is a computer configuration setting.  :-)

    Everyone feels better now, right?  The loopback setting configures a registry value on the computer to which it applies.  The Group Policy engine reads this value and changes how it builds the list of applicable user policies based on the selected loopback mode.

    The easiest way to know if loopback might be causing troubles with your policy processing is to collect a GPResult /h from the computer.  Since loopback is a computer configuration setting, you will need to run GPResult from an administrative command prompt.

     

     

    The good news is that the GPResult output will show you the winning GPO with loopback enabled.  Unfortunately, it does not list all GPOs with loopback configured, just the one with the highest precedence. 

    If your OU structure separates users from computers, the GPResult output can also help you find GPOs containing user settings that are linked to computer OUs.  Look for GPOs linked to computer OUs under the Applied GPOs section of the User Details of the GPResult output. 

    Below is an example of the output of the GPResult /h command from a Windows Server 2012 member server.  The layout of the report has changed slightly going from Windows Server 2008 to Windows Server 2012, so your results may look different, but the same information is provided by previous versions of the tool.  Notice that the link location includes the Computers OU, but we are in the User Details section of the report.  This is a good indication that we have loopback enabled in a GPO linked in the path of the computer account. 

     

       
    Understand the desired state vs. the actual state

    This one also sounds obvious, but in order to troubleshoot you have to know and understand exactly which settings you are expecting to apply to the user.  This is harder than it sounds.  In a lab environment where you control everything, it's pretty easy to keep track of desired configuration.  However, in a production environment with potentially multiple delegated GPO admins, this is much more difficult. 

    GPResult gives us the actual state, but if you don't know the desired state at the setting level, then you can't reasonably determine if loopback is configured correctly (meaning you have WMI filters and/or security filtering set properly to achieve your desired configuration). 

         
    Review security filtering on GPOs

    Once you determine which GPOs or which settings are not applying as expected, then you have a place to start your investigation. 

    In our experience here in support, loopback processing issues usually come down to incorrect security filtering, so rule that out first.

    This is where things get tricky . . . If you are configuring custom security filtering on your GPOs, loopback can get confusing quickly.  As a general rule, you should try to keep your WMI and security filtering as simple as possible - but ESPECIALLY when loopback is involved.  You may want to consider temporarily unlinking any WMI filters for troubleshooting purposes.  The goal is to ensure the policies you are expecting to apply are actually applying.  Once you determine this, then you can add your WMI filters back into the equation.  A test environment is the best place to do this type of investigation.

    Setting up security filtering correctly depends on how you architect your policies:

    1. Did you enable loopback in its own GPO or in a GPO with other computer or user settings?
    2. Are you combining user settings and computer settings into the same GPO(s) linked to the computer’sOU?

    The thing to keep in mind is that if you have what I would call "mixed use" GPOs, then your security filtering has to accommodate all of those uses.  This is only a problem if you remove Authenticated Users from the security filter on the GPO containing the user settings.  If you remove Authenticated Users from the security filter, then you have to think through which settings you are configuring, in which GPOs, to be applied to which computers and users, in which loopback mode....

    Ouch.  That's LOTS of thinking!

    So, unless that sounds like loads of fun to you, it’s best to keep WMI and security filtering as simple as possible.  I know that you can’t always leave Authenticated Users in place, but try to think of alternative solutions before removing it when loopback is involved. 

    Now to the part that everyone always asks about once they realize their current filter is wrong – How the heck should I configure the security filter?!

     

    Security filtering requirements:

    1. The computer account must have READ and APPLY permissions to the GPO that contains the loopback configuration setting.
    2. If you are configuring user settings in the same GPO as computer settings, then the user and computer accounts will both need READ and APPLY permissions to the GPO since there are portions of the GPO that are applicable to both.
    3. If the user settings are in a separate GPO from the loopback configuration setting (#1 above) and any other computer settings (#2 above), then the GPO containing the user settings requires the following permissions:  

     

    Merge mode requirements (Vista+):

    User account:

    READ and APPLY (these are the default
      permissions that are applied when you add users to the Security Filtering
      section of the GPO  on the Scope tab in
      GPMC)

    Computer account:

    Minimum of READ permission

     

    Replace mode requirements:

    User account:

    READ and APPLY (these are the default
      permissions that are applied when you add users to the Security Filtering
      section of the GPO  on the Scope tab in
      GPMC)

    Computer account:

    No permissions are required

      

     

    Tools for Troubleshooting

    The number one tool for troubleshooting loopback processing is your GPRESULT output and a solid understanding of the security filtering requirements for loopback processing in your GPO architecture (see above).

    The GPRESULT will tell you which GPOs applied to the user.  If a specific GPO failed to apply, then you need to review the security filtering on that GPO and verify:

    • The user has READ and APPLY permissions
    • Depending on your GPO architecture, the computer may need READ or it may need READ and APPLY if you combined computer and user settings in the same GPO.

    The same strategy applies if you have mysterious policy settings applying after configuring loopback and you are not sure know why.  Use your GPRESULT output to identify which GPO(s) the policy settings are coming from and then review the security filtering of those GPOs. 

    The Group Policy Operational logs from the computer will also tell you which GPOs were discovered and applied, but this is the same information that you will get
    from the GPRESULT.

    Recommendations for using loopback

    After working my fair share of loopback-related cases, I've collected a list of recommendations for using loopback.  This isn’t an official list of "best practices", but rather just some personal recommendations that may make your life easier.  ENJOY!

    I'll start with what is fast becoming my mantra: Keep it Simple.  Pretty much all of my recommendations can come back to this point.

     

    1. Don't use loopback  :-) 

    OK, I know, not realistic.  How about this . . . Don't use loopback unless you absolutely have to. 

    • I say this not because there is something evil about loopback, but rather because loopback complicates how you think about Group Policy processing.  Loopback tends to be configured and then forgotten about until you start seeing unexpected results. 

    2. Use a separate GPO for the loopback setting; ONLY include the loopback setting in this GPO, and do not include the user settings.  Name it Loopback-Merge or Loopback-Replace depending on the mode.

    • This makes loopback very easy to identify in both the GPMC and in your GPRESULT output.  In the GPMC, you will be able to see where the GPO is linked and the mode without needing to view the settings or details of any GPOS.  Your GPRESULT output will clearly list the loopback policy in the list of applied policies and you will also know the loopback mode, without digging into the report. Using a separate policy also allows you to manage the security of the loopback GPO separately from the security on the GPOs containing the user settings.

    3. Avoid custom security filtering if you can help it. 

    • Loopback works without a hitch if you leave Authenticated Users in the security filtering of the GPO.  Removing Authenticated Users results in a lot more work for you in the long run and makes troubleshooting undesired behaviors much more complicated.

    4. Don't enable loopback in a GPO linked at the domain level!

    • This will impact your Domain Controllers.  I wouldn't be including this warning, if I hadn't worked several cases where loopback had been inadvertently applied to Domain Controllers.  Again, there isn’t anything inherently wrong with applying loopback on Domain Controllers.  It is bad, however, when loopback unexpectedly applies to Domain Controllers.
    • If you absolutely MUST enable loopback in a GPO linked at the domain level, then block inheritance on your Domain Controllers OU.  If you do this, you will need to link the Default Domain Policy back to the Domain Controllers OU making sure to have the precedence of the Default Domain Controllers policy higher (lower number) than the Domain Policy.
    • In general, be careful with all policies linked at the at the domain level.  Yes, it may be "simpler" to manage most policy at the domain level, but it can lead
      to lazy administration practices and make it very easy to forget about the impact of seemingly minor policy changes on your DCs.
    • Even if you are editing the security filtering to specific computers, it is still dangerous to have the loopback setting in a GPO linked at the domain level.  What if someone mistakenly modifies the security filtering to "fix" some other issue.
      • TEST, TEST, TEST!!!  It’s even more important to test when you are modifying GPOs that impact domain controllers.  Making a change at the domain level that negatively impacts a domain controller can be career altering.  Even if you have to set up a test domain in virtual machines on your own workstation, find a way to test.

    5. Always test in a representative environment prior to deploying loopback in production.

    • Try to duplicate your production GPOs as closely as possible.  Export/Import is a great way to do this.
    • Enabling loopback almost always surfaces some settings that you weren't aware of.  Unless you are diligent about disabling unused portions of GPOs and you perform periodic audits of actual configuration versus documented desired state configuration, there will typically be a few settings that are outside of your desired configuration. 
    • Duplicating your production policies in a test environment means you will find these anomalies before you make the changes in production.

     

    That’s all folks!  You are now ready to go forth and conquer all of those loopback policies!

     

    Kim “1.21 Gigawatts!!” Nichols

  • We're back. Did you miss us?

    Hey all, David here.  Now that we’ve broken the silence, we here on the DS team felt that we owed you, dear readers, an explanation of some sort.  Plus, we wanted to talk about the blog itself, some changes happening for us, and what you should hopefully be able to expect moving forward.

     

    So, what had happened was….

    As most of you know, a few months ago our editor-in-chief and the butt of many jokes here on the DS support team moved to a new position.  We have it on good authority that he is thoroughly terrorizing many of our developers in Redmond with scary words like “documentation”, “supportability”, and other Chicago-style aphorisms which are best not repeated in print.

    Unfortunately for us and for this blog, that left us with a little bit of a hole in the editing team!  The folks left behind might have been superheroes, but the problem with being a superhero is that you get called on to go save the world (or a customer with a crisis) all the time, and that doesn’t leave much time for picking up your cape from the dry cleaners, let alone keeping up with editing blog submissions, doing mail sacks, and generally keeping the blog going.

    At the same time, we had a bit of a reorganization internally.  Where we were formerly one team within support, we are now two teams – DS (Directory Services) and ID (Identity).  Why the distinction?  Well, you may have heard about this Cloud thing…. But that’s a story best told by technical blog posts, really.  For now, let’s just say the scope of some of what we do expanded last year from “a lot of people use it” to “internet scale”.  Pretty scary when you think about it.

    Just to make things even more confusing, about a month ago we were officially reunited with our long-lost (and slightly insane, but in a good way) brethren in the field engineering organization.  That’s right, our two orgs have been glommed[1] together into one giant concentration of support engineering superpower.  While it’s opening up some really cool stuff that we have always wanted to do but couldn’t before, it’s still the equivalent of waking up one day and finding out that all of those cousins you see every few years at family reunions are coming to live with you.  In your house.  Oh, and they’re bringing their dog.

    Either way, the net effect of all this massive change was that we sort of got quiet for a few months.  It wasn’t you, honest.  It was us.

     

    What to Expect

    It’s important to us that we keep this blog current with detailed, pertinent technical info that helps you resolve issues that you might encounter, or even just helps you understand how our parts of Windows work a bit better.  So, we’re picking that torch back up and we’ll be trying to get several good technical posts up each month for you.  You may also see some shorter posts moving forward.  The idea is to break up the giant articles and try to get some smaller, useful-to-know things out there every so often.  Internally, we’re calling the little posts “DS Quickies” but no promises on whether we’ll actually give you that as a category to search on.  Yes, we’re cruel like that.  You’ll also see the return of the mail sack at some point in the near future, and most importantly you’re going to see some new names showing up as writers.  We’ve put out the call, and we’re planning to bring you blog posts written not just by our folks in the Americas, but also in Europe and Asia.  You can probably also expect some guest posts from our kin in the PFE organization, when they have something specific to what we do that they want to talk about.

    At the same time, we’re keen on keeping the stuff that makes our blog useful and fun.  So you can continue to expect technical depth, detailed analysis, plain-English explanations, and occasional irreverent, snarky humor.  We’re not here to tell you why you should buy Windows clients or servers (or phones, or tablets) – we have plenty of marketing websites that do that better than we ever could.  Instead, we’re here to help you understand how Windows works and how to fix problems when they occur.  Although we do reserve the right to post blatant wackiness or fun things every so often too.  Look, we don’t get out much, ok?  This is our outlet.  Just go with us on this.

    Finally, you’re going to see me personally posting a bit more, since I’ve taken over as the primary editor for the site.  I know - I tried to warn them what would happen, but they still gave me the job all the same.  Jokes aside, I feel like it’s important that our blog isn’t just an encyclopedia of awesome technical troubleshooting, but also that it showcases the fact that we’re real people doing our best to make the IT world a better place, as sappy as that sounds. (Except for David Fisher– I’m convinced he’s really a robot).  I have a different writing style than Ned and Jonathan, and a different sense of humor, but I promise to contain myself as much as possible.  :-)

    Sound good?  We hope so.  We’re going to go off and write some more technical stuff now – in fact:  On deck for next week:  A followup to Kim’s blog on Loopback Policy Processing.

    We wanted to leave you with a funny video that’s safe for work to help kick off the weekend, but alas our bing fu was weak today.  Got a good one to share?  Feel free to link it for us in the comments!


    [1]
    “Glom” is a technical term, by the way, not a managerial one.  Needless to say, hijinks are continuing to
    ensue.

     

    -- David "Capes are cool" Beach

  • AD FS 2.0 Claims Rule Language Part 2

    Hello, Joji Oshima here to dive deeper into the Claims Rule Language for AD FS. A while back I wrote a getting started post on the claims rule language in AD FS 2.0. If you haven't seen it, I would start with that article first as I'm going to build on the claims rule language syntax discussed in that earlier post. In this post, I'm going to cover more complex claim rules using Regular Expressions (RegEx) and how to use them to solve real world issues.

    An Introduction to Regex

    The use of RegEx allows us to search or manipulate data in many ways in order to get a desired result. Without RegEx, when we do comparisons or replacements we must look for an exact match. Most of the time this is sufficient but what if you need to search or replace based on a pattern? Say you want to search for strings that simply start with a particular word. RegEx uses pattern matching to look at a string with more precision. We can use this to control which claims are passed through, and even manipulate the data inside the claims.

    Using RegEx in searches

    Using RegEx to pattern match is accomplished by changing the standard double equals "==" to "=~" and by using special metacharacters in the condition statement. I'll outline the more commonly used ones, but there are good resources available online that go into more detail. For those of you unfamiliar with RegEx, let's first look at some common RegEx metacharacters used to build pattern templates and what the result would be when using them.

    Symbol

    Operation

    Example rule

    ^

    Match the beginning of a string

    c:[type == "http://contoso.com/role", Value =~ "^director"]

    => issue (claim = c);

     

    Pass through any role claims that start with "director"

    $

    Match the end of a string

    c:[type == "http://contoso.com/email", Value =~ "contoso.com$"]

    => issue (claim = c);

     

    Pass through any email claims that end with "contoso.com"

    |

    OR

    c:[type == "http://contoso.com/role", Value =~ "^director|^manager"]

    => issue (claim = c);

     

    Pass through any role claims that start with "director" or "manager"

    (?i)

    Not case sensitive

    c:[type == "http://contoso.com/role", Value =~ "(?i)^director"]

    => issue (claim = c);

     

    Pass through any role claims that start with "director" regardless of case

    x.*y

    "x" followed by "y"

    c:[type == "http://contoso.com/role", Value =~ "(?i)Seattle.*Manager"]

    => issue (claim = c);

     

    Pass through any role claims that contain "Seattle" followed by "Manager" regardless of case.

    +

    Match preceding character

    c:[type == "http://contoso.com/employeeId", Value =~ "^0+"]

    => issue (claim = c);

     

    Pass through any employeeId claims that contain start with at least one "0"

    *

    Match preceding character zero or more times

    Similar to above, more useful in RegExReplace() scenarios.

     

    Using RegEx in string manipulation

    RegEx pattern matching can also be used in replacement scenarios. It is similar to a "find and replace", but using pattern matching instead of exact values. To use this in a claim rule, we use the RegExReplace() function in the value section of the issuance statement.

    The RegExReplace() function accepts three parameters.

    1. The first is the string in which we are searching.
      1. We will typically want to search the value of the incoming claim (c.Value), but this could be a combination of values (c1.Value + c2.Value).
    2. The second is the RegEx pattern we are searching for in the first parameter.
    3. The third is the string value that will replace any matches found.

    Example:

    c:[type == "http://contoso.com/role"]
    => issue (Type = "http://contoso.com/role", Value = RegExReplace(c.Value, "(?i)director", "Manager");

     

    Pass through any role claims. If any of the claims contain the word "Director", RegExReplace() will change it to "Manager". For example, "Director of Finance" would pass through as "Manager of Finance".

     

    Real World Examples

    Let's look at some real world examples of regular expressions in claims rules.

    Problem 1:

    We want to add claims for all group memberships, including distribution groups.

    Solution:

    Typically, group membership is added using the wizard and selecting Token-Groups Unqualified Names and map it to the Group or Role claim. This will only pull security groups, not distribution groups, and will not contain Domain Local groups.

    We can pull from memberOf, but that will give us the entire distinguished name, which is not what we want. One way to solve this problem is to use three separate claim rules and use RegExReplace() to remove unwanted data.

    Phase 1: Pull memberOf, add to working set "phase 1"

     

    c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"]
    => add(store = "Active Directory", types = ("http://test.com/phase1"), query = ";memberOf;{0}", param = c.Value);

    Example: "CN=Group1,OU=Users,DC=contoso,DC=com" is put into a phase 1 claim.

     

    Phase 2: Drop everything after the first comma, add to working set "phase 2"

     

    c:[Type == "http://test.com/phase1"]
    => add(Type = "http://test.com/phase2", Value = RegExReplace(c.Value, ",[^\n]*", ""));

    Example: We process the value in the phase 1 claim and put "CN=Group1" into a phase 2 claim.

     

    Digging Deeper: RegExReplace(c.Value, ",[^\n]*", "")

    • c.Value is the value of the phase 1 claim. This is what we are searching in.
    • ",[^\n]*" is the RegEx syntax used to find the first comma, plus everything after it
    • "" is the replacement value. Since there is no string, it effectively removes any matches.

     

    Phase 3: Drop CN= at the beginning, add to outgoing claim set as the standard role claim

     

    c:[Type == "http://test.com/phase2"]

    => issue(Type = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role", Value = RegExReplace(c.Value, "^CN=", ""));

    Example: We process the value in phase 2 claim and put "Group1" into the role claim

    Digging Deeper: RegExReplace(c.Value, "^CN=", "")

    • c.Value is the value of the phase 1 claim. This is what we are searching in.
    • "^CN=" is the RegEx syntax used to find "CN=" at the beginning of the string.
    • "" is the replacement value. Since there is no string, it effectively removes any matches.

     

    Problem 2:

    We need to compare the values in two different claims and only allow access to the relying party if they match.

    Solution:

    In this case we can use RegExReplace(). This is not the typical use of this function, but it works in this scenario. The function will attempt to match the pattern in the first data set with the second data set. If they match, it will issue a new claim with the value of "Yes". This new claim can then be used to grant access to the relying party. That way, if these values do not match, the user will not have this claim with the value of "Yes".

     

    c1:[Type == "http://adatum.com/data1"] &&

    c2:[Type == "http://adatum.com/data2"]

    => issue(Type = "http://adatum.com/UserAuthorized", Value = RegExReplace(c1.Value, c2.Value, "Yes"));

     

    Example: If there is a data1 claim with the value of "contoso" and a data2 claim with a value of "contoso", it will issue a UserAuthorized claim with the value of "Yes". However, if data1 is "adatum" and data2 is "fabrikam", it will issue a UserAuthorized claim with the value of "adatum".

     

    Digging Deeper: RegExReplace(c1.Value, c2.Value, "Yes")

    • c1.Value is the value of the data1 claim. This is what we are searching in.
    • c2.Value is the value of the data2 claim. This is what we are searching for.
    • "Yes" is the replacement value. Only if c1.Value & c2.Value match will there be a pattern match and the string will be replaced with "Yes". Otherwise the claim will be issued with the value of the data1 claim.

     

    Problem 3:

    Let's take a second look at potential issue with our solution to problem 2. Since we are using the value of one of the claims as the RegEx syntax, we must be careful to check for certain RegEx metacharacters that would make the comparison mean something different. The backslash is used in some RegEx metacharacters so any backslashes in the values will throw off the comparison and it will always fail, even if the values match.

    Solution:

    In order to ensure that our matching claim rule works, we must sanitize the input values by removing any backslashes before doing the comparison. We can do this by taking the data that would go into the initial claims, put it in a holding attribute, and then use RegEx to strip out the backslash. The example below only shows the sanitization of data1, but it would be similar for data2.

    Phase 1: Pull attribute1, add to holding attribute "http://adatum.com/data1holder"

     

    c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"]

    => add(store = "Active Directory", types = ("http://adatum.com/data1holder"), query = ";attribute1;{0}", param = c.Value);

    Example: The value in attribute 1 is "Contoso\John" which is placed in the data1holder claim.

     

    Phase 2: Strip the backslash from the holding claim and issue the new data1 claim

     

    c:[Type == "http://adatum.com/data1holder", Issuer == "AD AUTHORITY"]

    => issue(type = "http://adatum.com/data1", Value = RegExReplace(c.Value,"\\","");

    Example: We process the value in the data1holder claim and put "ContosoJohn" in a data1 claim

    Digging Deeper: RegExReplace(c.Value,"\\","")

    • c.Value is the value of the data1 claim. This is what we are searching in.
    • "\\" is considered a single backslash. In RegEx, using a backslash in front of a character makes it a literal backslash.
    • "" is the replacement value. Since there is no string, it effectively removes any matches.

     

    An alternate solution would be to pad each backslash in the data2 value with a second backslash. That way each backslash would be represented as a literal backslash. We could accomplish this by using RegExReplace(c.Value,"\\","\\") against a data2 input value.

     

    Problem 4:

    Employee numbers vary in length, but we need to have exactly 9 characters in the claim value. Employee numbers that are shorter than 9 characters should be padded in the front with leading zeros.

    Solution:

    In this case we can create a buffer claim, join that with the employee number claim, and then use RegEx to use the right most 9 characters of the combined string.

    Phase 1: Create a buffer claim to create the zero-padding

     

    => add(Type = "Buffer", Value = "000000000");

     

    Phase 2: Pull the employeeNumber attribute from Active Directory, place it in a holding claim

     

    c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"]

    => add(store = "Active Directory", types = ("ENHolder"), query = ";employeeNumber;{0}", param = c.Value);

     

    Phase 3: Combine the two values, then use RegEx to remove all but the 9 right most characters.

     

    c1:[Type == "Buffer"]

    && c2:[Type == "ENHolder"]

    => issue(Type = "http://adatum.com/employeeNumber", Value = RegExReplace(c1.Value + c2.Value, ".*(?=.{9}$)", ""));

    Digging Deeper: RegExReplace(c1.Value + c2.Value, ".*(?=.{9}$)", "")

    • c1.Value + c2.Value is the employee number padded with nine zeros. This is what we are searching in.
    • ".*(?=.{9}$)" represents the last nine characters of a string. This is what we are searching for. We could replace the 9 with any number and have it represent the last "X" number of characters.
    • "" is the replacement value. Since there is no string, it effectively removes any matches.

     

    Problem 5:

    Employee numbers contain leading zeros but we need to remove those before sending them to the relying party.

    Solution:

    In this case we can pull employee number from Active Directory and place it in a holding claim, then use RegEx to use the strip out any leading zeros.

    Phase 1: Pull the employeeNumber attribute from Active Directory, place it in a holding claim

     

    c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"]

    => add(store = "Active Directory", types = ("ENHolder"), query = ";employeeNumber;{0}", param = c.Value);

     

    Phase 2: Take the value in ENHolder and remove any leading zeros.

     

    c:[Type == "ENHolder"]

    => issue(Type = "http://adatum.com/employeeNumber", Value = RegExReplace(c.Value, "^0*", ""));

    Digging Deeper: RegExReplace(c.Value, "^0*", "")

    • c1.Value is the employee number. This is what we are searching in.
    • "^0*" finds any leading zeros. This is what we are searching for. If we only had ^0 it would only match a single leading zero. If we had 0* it would find any zeros in the string.
    • "" is the replacement value. Since there is no string, it effectively removes any matches.

     

    Conclusion

    As you can see, RegEx adds powerful functionality to the claims rule language. It has a high initial learning curve, but once you master it you will find that there are few scenarios that RegEx can't solve. I would highly recommend searching for an online RegEx syntax tester as it will make learning and testing much easier. I'll continue to expand the TechNet wiki article so I would check there for more details on the claims rule language.

    Understanding Claim Rule Language in AD FS 2.0

    AD FS 2.0: Using RegEx in the Claims Rule Language

    Regular Expression Syntax

    AD FS 2.0 Claims Rule Language Primer

    Until next time,

    Joji "Claim Jumper" Oshima