Username Generation Rule not Iterating Suffix Beyond "a"

:bangbang: Please be sure you’ve read the docs and API specs before asking for help. Also, please be sure you’ve searched the forum for your answer before you create a new topic.

Hi,

I’ve implemented a custom AttributeGenerator rule to generate a uniquesAMAccountNamefor AD accounts.

Logic:

  • Username = first letter of preferredName + first letter of lastname + start date (MMyy) + suffix (az).
  • Example: John Doe, start date 2023-10-15 → base username = JD1023.
  • Expected: JD1023a, JD1023b, JD1023c, etc.

Issue:

  • First account generates JD1023a correctly.
  • On duplicate, it still returns JD1023a instead of incrementing to b, c, etc.

Rule:

    /* Rule to generate a unique username for Active Directory.
       The username is generated using the first letter of preferredName, first letter of lastname,
       the start date in mmyy format, and a suffix letter from 'a' to 'z' to ensure uniqueness.
       For example, for preferredName "John", lastname "Doe", and startDate "2023-10-15",
       the base username would be "JD1023". The rule will then try "JD1023a", "JD1023b", etc.,
       until it finds a unique username. */

    import sailpoint.tools.GeneralException;
    import sailpoint.object.Application;
    import sailpoint.object.Field;
    import sailpoint.object.Identity;
    import sailpoint.server.IdnRuleUtil;
    import sailpoint.api.*;

    import java.util.ArrayList;
    import java.util.List;
    import org.apache.commons.lang.StringUtils;
    import java.text.Normalizer;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.text.ParseException;
    import java.util.Calendar;
    import java.util.Locale;
    import java.util.TimeZone;
    import java.util.regex.Pattern;
    import java.util.regex.Matcher;
    import java.util.regex.PatternSyntaxException;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    

    public String generateUsername(String preferredName, String lastname, String startDate) throws GeneralException {
        preferredName = cleanAlphaNumeric(StringUtils.trimToNull(preferredName));
        lastname = cleanAlphaNumeric(StringUtils.trimToNull(lastname));
        startDate = StringUtils.trimToNull(startDate);

        if(preferredName == null || lastname == null || startDate == null) {
            throw new GeneralException("Invalid input: preferredName" + preferredName + ", lastname" + lastname + ", and startDate" + startDate + "cannot all be null.");
        }

        String preferredNamefirstLetter = preferredName.substring(0, 1);
        String lastnamefirstLetter = lastname.substring(0, 1);
        String dateFormatmmyy = extractDateFormat(startDate);
        String baseUsername = preferredNamefirstLetter + lastnamefirstLetter + dateFormatmmyy;  

        for (char suffix = 'a'; suffix <= 'z'; suffix++) {
            String username = baseUsername + suffix;
            if (isUnique(username)) {
                log.debug("GSB AD Unique sAMAccountName generated: " + username);
                return username;
            }
        }
        throw new GeneralException("Unable to generate a unique username after trying all suffixes."); 
    }

    public String cleanAlphaNumeric(String input) {
        if (input == null) {
            return null;
        }
        String cleanedInput = input.replaceAll("[^a-zA-Z0-9]", "");
        return cleanedInput.length() > 0 ? cleanedInput : null;
    }

    public String extractDateFormat(String date) throws GeneralException {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date parsedDate = sdf.parse(date);
            SimpleDateFormat outputFormat = new SimpleDateFormat("MMyy");
            return outputFormat.format(parsedDate);
        } catch (ParseException e) {
            throw new GeneralException("Invalid date format: " + date, e);
        }
    }

    public boolean isUnique(String username) throws GeneralException {
        List sourceIds = Collections.singletonList(application.getId());
        List searchValues = Collections.singletonList(username);
        int count = idn.attrSearchCountAccounts(sourceIds, "sAMAccountName", "Equals", searchValues);
        log.debug("GSB AD sAMAccountName uniqueness check for '" + username + "': found " + count + " existing accounts.");
        return count == 0;
    }

    return generateUsername(identity.getAttribute("preferredName"), identity.getAttribute("lastname"), identity.getAttribute("startDate"));

Has anyone else run into this? Am I using attrSearchCountAccounts incorrectly, or is there another recommended way to ensure suffix increments properly?

Hello @thiruppathit,

Please consider using the isUniqueLDAPValue method provided by idnRuleUtil to check the uniqueness of sAMAccountName.

You can find more details in the Java documentation here: Java docs | SailPoint Developer Community

Are you trying to provision those accounts at the same time? If so, this is an expected behavior

No, I am not trying at the same time.

Thanks, will try with this method and keep you posted.

Have you promoted sAMAccountName as searchable attribute?

Yes, I did. See the search-attribute-config response.

[

{

“name”: “sAMAccountName”,

“displayName”: “Searchable sAMAccountName”,

“applicationAttributes”: {

“3c1c9364e8514089bed10ddb78fbcf2c”: “sAMAccountName”

}

}

]

Thanks Ujwal. Using “isUniqueLDAPValue” solved the problem. I have replaced the logic with below code and its working fine now:


public boolean isUnique(String username) throws GeneralException {
        String applicationName = application.getName();
        String identityName = identity.getName();
        return idn.isUniqueLDAPValue(identityName, applicationName, "sAMAccountName", username);
    }
1 Like