Converting Contractors to Full-Time (identities conversing identity profiles)

Background:
It’s reasonable to expect most companies do not have a unified workforce management source. In the case for non-employees wether it be Contractors or Interns, there’s a general contract for conversions wether it be Contractor to Full-Time, and in rare cases the inverse. In these scenarios, different sources are used to aggregate worker types and thus requires multiple identity profiles to onboard each member into Sailpoint. The challenge with Sailpoint then becomes a problem with identity profile priorities and no recourse for transferring an identity from one identity profile to the other.

Problem:
In my case, ADP is our authoritative source for Full-Time Employees while Contingent workers are tracked in another source. Since Identity profiles have a 1:1 relationship to sources, the only recourse would be to invest significant engineering resources into building and maintaining a database that effectively aggregates all authoritative sources and internally draws correlation between workerTypes and their progression within the workforce to preserve a Sailpoint identity. This is not ideal.

As for the current state, the problem that we’re facing can be seen in the following:

Explaination:
John Doe starts as a Contractor and is onboarded into sailpoint with the userID: OID-A. At some point, he’s converted to a Full-Time employee (FTE), but because FTE’s are sourced from ADP, a new sailpoint user is created with userID: OID-B. This results in a duplicate identity in Sailpoint and often results with the other being disabled. While the duplication is unavoidable within this architecture, there’s no method to transfer the access history from one sailpoint user to another. Therefore the access history will be reset upon the new start date although it’s the same person.

Other concerns:
In this example, at some point the “stale” record would be marked as terminated since the previous work assignment has expired. This especially applies to the FTE > CON conversions where in ADP we have an FTE that is terminated (cannot delete from ADP), however he’s an active contractor. We can’t necessarily “filter” terminated identities from ADP so-as-to prevent them from deleting their user object in Sailpoint and to maintain the identity for historical purposes as “terminated” to follow-up and verify off-boarding tasks are completed. So in sailpoint, both records are present and when requesting access, users will see both despite not knowing which is stale.

Recommendations \ Feature Request(s)

  • Feature #1 - to promote conversions for correlated identity profile identities:

When an identity is correlated through multiple identity profiles, provide an option to transfer access history and components to the active record. It would be ideal to allow conversions from one identity profile to the other so-long as there’s a persistent identifier (i.e. employeeNumber or email) that permits the override to take place and whichever direction the core identity and access history is preserved. This may replace the OID of the new record with the old and would allow for the stale record to be deleted or be complemented by #2. The primary goal would be to provide a unified solution to customers for converting identities wether they have multiple sources or not. The latter is simply a huge undertaking that some costumers may not be able to afford.

  • Feature #2 (required) - to flag \ hide duplicate identities:

This would significantly improve QoL for both admins and users by enabling a feature to hide \ ignore identities to request on behalf of or to eliminate confusion for records that are duplicate and have both active \ terminated states. Could be accomplished by a simple “Tag” or allowing for an API to modify an attribute that hides the identity. This compliments Option 2.

Currently in planning (needs more urgency)
Hide terminated users when requesting access - Discussion and Questions - SailPoint Developer Community Forum
https://ideas.sailpoint.com/ideas/GOV-I-1864

Without these features, we’re left with the following options

  • Option 1 - Process all identities that support conversions from the same source

This requires that all Contractors, Interns, and Employees be maintained within the same source. In this case, PeopleOps would need to be responsible for tracking all workers from these workerTypes in ADP and transition workers internally. This is not a surefire solution but would be an ideal model, however requires a lot of pull, time, and effort to reprioritize the other organizations workstreams.

  • Option 2 - Continue with the existing design \ strategy

This requires that we accept the risk associated with conversions for the current design and compensate for the deficiencies by triggering a recertification of all access items for the work assignment. This does not however solve for the duplication \ presence of stale objects as they would remain visible to both Admins and Users.

  • Option 3 - Develop a unified source to aggregate identities from all other sources

Create a central database instead of aggregating Contractors, Interns, and Full-Time employees from separate sources. This would require extensive development to account for all worker transitions and to support operations, triggers, and workflows for existing worker management. Not ideal, and requires rigorous testing and maintenance to ensure the integrity of this consolidated authoritative source.

Synopsis
There’s little to be desired for the core feature limitations when it comes to converting identities, especially if they belong to separate authoritative sources. It would be extremely helpful to know if others have faced these design challenges and came up with a viable solution or support the need for the desired features mentioned above. I am otherwise relatively new to the product, however the community forums, professional services (third-party), and documentation have lead me to the above problem statement.

4 Likes

Hi @brian-short,

This is not entirely true - Identity Profiles have a priority order. This means if an Identity has accounts on 2 or more authoritative sources, the Identity Profile with the lower priority number is used to build the identity.

For example:
John Doe starts as a Contractor and is onboarded into the authoritative source DB Contractor. The Identity Profile for DB Contractor is configured to create the User ID “C-001”.

When the Identity is converted to an Employee, a new account is created for the identity on the source FTE Company. If the Identity Profile for FTE Company has a lower priority number - it will recreate the identity for John Doe and will calculate the values for User ID (e.g. “E-001”) and other identity attributes depending on the configuration for the respective Identity Profile.

I will also link the documentation here for further reading.

Hope this helps!

Hi Kartik,

While the identity profiles take priority into consideration, it only functions to take precedence for correlation (i.e. attributes and lifecycle state). This in theory would be fine if conversions only functioned in a linear path and the other authoritative source records were ephemeral (despite the risk of data loss). There are two limitations that I feel i need to clarify with examples.

1. Identity Profile Priority

Source Information

Name Priority Source Notes
CON01 10 JDBC Supports FTE > CON (requires removal from source to return to FTE)
FTE 20 ADP Records permenant
CON02 30 JDBC Supports CON > FTE
INT 40 JDBC Supports INT > CON and INT > FTE

Notes: ADP records cannot be deleted, so any member in CON01 will have a duplicate identity with the opposite lifecycle state (i.e. Active in CON01 and Terminated in FTE). Second, handling separate authoritative sources poses an operational hurdle to remove stale records post conversion to eliminate confusion within the product. ADP for example is one that cannot be mitigated, however the other sources pose the question as to when we should delete unless a feature is added to resolve these concerns.

2. Duplication and access history limitations
While the conversions take place and correlate the identities within a higher priority (identity profile) the duplicate still remains and despite the correlation, the access history is not conjoined between the two. While the new identity is added and entitlements correlated, the history of requests and changes are lost, especially if the CON01 > FTE scenario is invoked and the primary record is deleted from the source.

Here you can see the same user and their access history

Left = Priority 20 - FTE
Right = Priority 10 - CON01

And thus, it’s operationally not ideal from both an admin and user perspective.

Hi @brian-short,

Thank you for the detailed response. You have highlighted many of the deficiencies with the proposed implementation.

I had another thought - what if the same was done using a singular Identity Profile?

This would eliminate the need for handling the priority order and would theoretically also keep the historical access data even after conversions.

It would require creating multiple custom identity attribute mappings and using transforms to calculate values based off the source accounts (preferably using the First Valid transform).
Role membership criteria can also be configured based off identity attribute values.

Is it absolutely necessary to transfer an Identity? We create separate identities when someone moves from Contractor to FTE or the inverse because we consider them two completely separate entites.

It really depends on the business use-case. Given our architectural limitations, it’s justified as necessary. We’re actively working with HR to bring all identities subject to conversions within the same authoritative source (i.e. ADP or Workday), however either solution would require an extensive effort from all stakeholders to achieve. This inevitably leaves us with a gap until improvements can be made. Why we might not treat the identities as separate at this time is to preserve a users journey. Should some one leave and come back months \ years later, we’d treat them as entirely new.

What features I’m suggesting here is to take a more agnostic approach to adequately feature the product to support customers regardless of the use-case (i.e. optional if required).

The first thing that comes to mind with this is that Identity Profiles have a 1:1 relationship with Sources. So unless the source can aggregate all applicable identities, there isn’t much use.

Architecture Limitations (fraught with duplicate accounts \ operational overhead)

Name Priority Description Source Conversions? Notes
IDN (Admins) 10 Breakglass Accounts Delimted: IDN FALSE
LOA 20 Leave of absence (override) JDBC TRUE Temporarily Removes Access
CON-02 30 Contractors JDBC TRUE CON > FTE (requires deletion)
FTE 40 Full Time Employees ADP TRUE FTE > CON
CON-02 50 Contractors JDBC TRUE CON > FTE
INT 60 Interns JDBC TRUE INT > FTE
SRV 70 Service Accounts JDBC FALSE

Perfect World
This is where Option 3 would be the most ideal, but has hard engineering costs. Because of this, we’re forced to take the following Identity Profile hierarchy

Name Priority Description Source Conversions? Notes
IDN (Admins) 10 Breakglass Accounts Delimted: IDN FALSE
LOA 20 Leave of absence (override) JDBC TRUE Temporarily Removes Access
TURO 30 Interns, Contractors, Employees Unified Source TRUE
SRV 40 Service Accounts JDBC FALSE

Synopsis
This is where having the combined features suggested here would enable us to take a “ship and iterate approach” with the current design path and grant us time to resolve ANY deficiencies internally or to enlist other stakeholders to adapt and change their architecture and work-streams to meet the ours and the products requirements.

Gotcha. We actually have some middleware (Mulesoft) that combines the feed from our two personnel management systems (SAP Successfactors and FieldGlass) into a single feed that writes to a CSV source. That way we have a single authoritative source for personnel.

1 Like

Hello,

I will try to make it as concise as possible because the variables here are extensive :blush:

I would say you need to first define a unique identifier across the multiple sources you have, that could be calculated or using existing identifiers, the latter may trigger PII concerns so calculating is probably a better approach, you could use part of the data that would not be able to be used outside to identify someone i.e. DD + MM + last 3 SSN (you may want to bump to 4, in some situations you may find conflicts). But again, just ideas, there are orgs that use names, but that would conflict all the time and generates extra work for the IAM admin. Once you have defined that I would work with multiple sources + priority to define which one has precedence to create the ID or overwrite the attributes with new data, transforms should be used for the common scenarios which also require you to define if a flag will be used to determine Person A is terminated on Contractor source and Person A is active on Employee source, in the example below I am using endDate to determine that, so the transform for the attribute type is validating the two sources information about the end date, so the employee data if empty or less then the data on the Contractor source it will be considered “F” (FTE), the negative condition is “C” (Contractor).

You can even implement custom forms to request approval for conversion, if not approved a new identity would be created with similar data adjusting the conflicted attributes accordingly (accountName, displayName, etc).

hope that helps, in the attachment are some videos and transforms to get you going! and you can always ask for ES hours or advisory, they would definitely have something better and more sophisticated than this example.

attribute.Type transform used in the example:

{
    "name": "Determine identityType",
    "type": "dateCompare",
    "attributes": {
        "firstDate": {
            "type": "dateFormat",
            "attributes": {
                "inputFormat": "MM/DD/YYYY",
                "outputFormat": "ISO8601",
                "input": {
                    "attributes": {
                        "values": [
                            {
                                "attributes": {
                                    "attributeName": "endDate",
                                    "sourceName": "ADP"
                                },
                                "type": "accountAttribute"
                            },
                            {
                                "attributes": {
                                    "value": "01/01/2199"
                                },
                                "type": "static"
                            }
                        ]
                    },
                    "type": "firstValid"
                }
            }
        },
        "secondDate": {
            "type": "dateFormat",
            "attributes": {
                "inputFormat": "MM/DD/YYYY",
                "outputFormat": "ISO8601",
                "input": {
                    "attributes": {
                        "values": [
                            {
                                "attributes": {
                                    "attributeName": "endDate",
                                    "sourceName": "DB Contractor"
                                },
                                "type": "accountAttribute"
                            },
                            {
                                "attributes": {
                                    "value": "01/01/2199"
                                },
                                "type": "static"
                            }
                        ]
                    },
                    "type": "firstValid"
                }
            }
        },
        "operator": "lte",
        "positiveCondition": {
            "attributes": {
                "value": "F"
            },
            "type": "static"
        },
        "negativeCondition": {
            "attributes": {
                "value": "C"
            },
            "type": "static"
        }
    },
    "internal": false
}

Hi Predo,

We’ve been discussing this internally and should be able to scope a POC in the next few sprints. Will report back on wether we can mark this as the solution. Really appreciate the time and suggestion.

We have done something along the same lines. We have a user ID that is unique to an individual and it is used if they are a contractor or employee (new hire or rehire). Like @PVolu mentioned this was key for us to track access and has allowed ours to work fairly well. All contractors (Beeline) are fed into PeopleSoft and then into SailPoint via a DB view. The view doesn’t have all identities only active and those that have been inactive for x days. The view only allows the active record if there is a duplicate during conversion. Kind of like what @mcheek mentioned with Mulesoft. Initially ours was via csv but that had more issues than what the DB connection has.

We have an employee status field that contains either EMP or CWR. For CWR we added a field for projected enddate (contract end date is empty for employee) but both have an actual endDate. Currently they are in one authoritative source but considering splitting them into 2 different sources (same feed but filtering on empl_status) so that we can manage them in different Identity Profiles. This is all so we can use unique and less complex LifeCycle States (LCS). Wish you the best! Kelvin

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.