While migrating from Dynamics CE/CRM to another Dynamics CE/CRM, or any other Source system to CRM, in an ETL, records will need to be associated with users. And since the Source and Target Systems are different, it may not be guaranteed that some users would still exist in the organization. In such cases, such users become either unlicensed, are placeholders, or rather stub users. These types of users have restrictions when it comes to being associated with records, specifically for fields that look up to SystemUser and would require the user to be a valid licensed user (an example would be "ownerid"). Also, there are different types of users in Dynamics CRM. There are regular users, who are the regular synchronized users from Azure Active Directory (Azure AD). Then there are Application users and non-interactive users, the former being associated with an Application Id, and the latter being those users who are used in SDK/API-related processes. And there are Stub users, who are created as a placeholder, that is, as we had mentioned initially, they have records associated with them, but the user does not exist in Dynamics CE/CRM instance. And in this blog post, we will see how these can be handled during migration scenarios.
Premise
During a migration using KingswaySoft, assigning Systemuser-related fields, such as Ownerid, can be done in multiple ways, depending on whether you are performing a CRM to CRM migration, or from another system to CRM. If you have the GUIDs directly available, that is, if the SystemuserId for the user is available in the Source system, then you could map it directly on top of the Ownerid field in the metadata (columns) page. In the example shown below, an Account record needs to be Upserted/Created, and we directly mapped the GUID from the Source system to the CRM Destination component. The owneridtype field is mapped to specify whether it's a User or a Team.
Now, if the user is an unlicensed, non-interactive, or stub user who does not have the privilege to be assigned as an owner of the record, it would throw an error similar to the one below.
The user with SystemUserId=XXXXXXXXX in OrganizationContext=XXXXXXX is not licensed, and its SystemUserAccessMode=0 is not either of (NonInteractive=X, SetupUser=X)
The type of user is referred to by the "AccessMode" in the above error message, which can be found in Dynamics CRM entityreference.
Depending on your input, there are two ways to handle this.
Case 1:
Assuming that GUID is being directly passed as the input, you can choose to Redirect rows to error output which will prevent our CRM Destination component from failing in case of an error, but it will redirect the failed records to the component’s Error Output and return an ErrorMessage along with each input record data.
And then you can use a Conditional Split component to separate the records based on the ErrorMessage by searching for a part of the error message using the FINDSTRING function.
Once you have the specific error records in the conditional split component output, you could assign the default GUID value, and then retry the same using a second CRM Destination component, or log them in an error log or file, and not use the conditional split component.
This is applicable in case of any such errors, in which you would need to deal it by replacing the Source value with a default one.
Case 2:
Let's assume that the GUID is not available in the Source data, but the string value is, like the name of the user that is being used in a lookup to assign ownerids for the records. And we know for a fact that few of these users do not exist in the CRM instance anymore, and therefore, have not been migrated to the Target instance. Our KingswaySoft CRM Destination component has its own inbuilt Text Lookup Feature, that would be used for this purpose, and as seen below, a default value can be assigned in case of a lookup failure.
Note that the optional default value can be "NULL" (case-insensitive with quotes removed), in which case it will perform a lookup using the input value, then the string literal "NULL". If that still fails to find a match, the component will set the lookup field's value to the empty one (NULL).And in our recent releases (~5 years) the optional default value supports using SSIS variables. When doing so, the variable should be in the format such as @[User::VariableName] or @[System::VariableName].
Migrating Stub Users for other references
Even though the records are associated with a valid user during migration, you might still be required to migrate the Stub users to the target systems, for other SystemUser-related fields for a record. This can be a tricky process as the CRM systemuser has some special requirements that need to be satisfied in order for the records to be written successfully. Usually, you can create a stub systemuser in Dynamics 365 with domainname, firstname, lastname, internalemailaddress, and businessunitid fields mapped, and to ensure that the created systemuser is a stub user, you would also need to provide a value to the islicensed and issyncwithdirectory fields as follows:
- Islicensed = false
- Issyncwithdirectory = false
Now, more fields may need to be mapped, and this would depend on your business requirement.
Conclusion
By providing a retry logic, or by using the optional default value, unlicensed and other not valid users can be managed to avoid being assigned to fields requiring valid users during a migration. This implemented in the migration pattern can avoid such errors being thrown, and ensure that the flow completes successfully without any missing association.