1

I am trying to migrate users from one org to another using SFDX. If I include ProfileId and UserRoleId, I receive errors Reference UserRole not found for UserRoleId. Skipping record undefined and

$ sfdx force:data:tree:export -q  "Select Username, FirstName, LastName, Email, Alias, Title, Department, UserRoleId, TimeZoneSidKey, LocaleSidKey, EmailEncodingKey, ProfileId, LanguageLocaleKey from user where CommunityNickname IN ('fsuzuki','ahartzler')" -u rr2 --outputdir data --plan
ERROR:  Reference UserRole not found for UserRoleId.  Skipping record undefined.
ERROR:  Reference Profile not found for ProfileId.  Skipping record undefined.
ERROR:  Reference UserRole not found for UserRoleId.  Skipping record undefined.
ERROR:  Reference Profile not found for ProfileId.  Skipping record undefined.
Wrote 2 records to data\Users.json
Wrote 0 records to data\User-plan.json

When I try to import, I receive an error that required fields are missing.

$ sfdx force:data:tree:import -u rr --plan data/User-plan.json
ERROR running force:data:tree:import:  {"hasErrors":true,"results":[{"referenceId":"UserRef1","errors":[{"statusCode":"REQUIRED_FIELD_MISSING","message":"Required fields are missing: [ProfileId]","fields":["ProfileId"]}]},{"referenceId":"UserRef2","errors":[{"statusCode":"REQUIRED_FIELD_MISSING","message":"Required fields are missing: [ProfileId]","fields":["ProfileId"]}]}]}

If I include Profile name and UserRole name in export, export doesn't bring any errors

$ sfdx force:data:tree:export -q  "Select Username, FirstName, LastName, Email, Alias, Title, Department, UserRole.Id, TimeZoneSidKey, LocaleSidKey, EmailEncodingKey, Profile.Id, LanguageLocaleKey, Profile.Name, UserRole.DeveloperName, UserRole.Name from user where CommunityNickname IN ('fsuzuki','ahartzler')" -u rr2 --outputdir data --plan
Wrote 2 records to data\Users.json
Wrote 0 records to data\User-plan.json

but then receive the errors Cannot reference a foreign key field UserRole and Cannot reference a foreign key field Profile.

$ sfdx force:data:tree:import -u rr --plan data/User-plan.json
ERROR running force:data:tree:import:  {"hasErrors":true,"results":
[{"reference Id":"UserRef1","errors":[{"statusCode":"INVALID_FIELD","message":"Cannot refere
nce a foreign key field Profile.","fields":[]},{"statusCode":"INVALID_FIELD","m
essage":"Cannot reference a foreign key field UserRole.","fields":[]}]},{"refer
enceId":"UserRef2","errors":[{"statusCode":"INVALID_FIELD","message":"Cannot re
ference a foreign key field Profile.","fields":[]},{"statusCode":"INVALID_FIELD
","message":"Cannot reference a foreign key field UserRole.","fields":[]}]}]}
3
  • I think the original export SOQL query needs to do a subquery for each child relationship as well, so that those records are available when trying to do the import. See the Parent to Child Query example Mar 18 '20 at 12:29
  • in this case, Profile and UserRole are not child relationships, but they are parent relationships
    – Patlatus
    Mar 18 '20 at 13:47
  • So Parent to child query example is not applicable here
    – Patlatus
    Mar 18 '20 at 13:48
2

Looks like it is not possible to achieve without apex. However, apex code can be called from sfdx.

So, we can write some file createUsers.apex and place it in scripts/apex folder of sfdx project.

String aron = 'Username=julfy@ah.rr,FirstName=Aaron,LastName=Hartzler,Email=julfy@i.ua,Alias=ahart,Title=Customer Support Rep,Department=Customer Support,TimeZoneSidKey=Europe/Dublin,LocaleSidKey=en_IE_EURO,EmailEncodingKey=ISO-8859-1,LanguageLocaleKey=en_US';
String fu = 'Username=julfy@fs.rr,FirstName=Fumiko,LastName=Suzuki,Email=julfy@i.ua,Alias=fsuzu,Title=Customer Support Rep,Department=Customer Support,TimeZoneSidKey=Asia/Tokyo,LocaleSidKey=ja_JP,EmailEncodingKey=ISO-8859-1,LanguageLocaleKey=ja';
Map<String, SObject> groupByName(List<SObject> records) {
    Map<String, SObject> m = new Map<String, SObject>();
    for (SObject r: records) {
        m.put(String.valueOf(r.get('Name')), r);
    }
    return m;
}
User createUser(String data, String role, String profile) {
    List<String> items = data.split(',');
    User u = new User();
    for (String item: items) {
        List<String> keyValue = item.split('=');
        u.put(keyValue[0], keyValue[1]);
    }
    u.ProfileId = profileMap.get(profile).Id;
    u.UserRoleId = roleMap.get(role).Id;
    u.IsActive = false;
    return u;
}
Map<String, SObject> profileMap = groupByName([SELECT Name FROM Profile]);
Map<String, SObject> roleMap = groupByName([SELECT Name FROM UserRole]);

insert new List<User>{
    createUser(aron, 'Customer Support, North America', 'Standard User'),
    createUser(fu, 'Customer Support, International', 'Standard Platform User')
};

and finally from command line the following command can be executed

sfdx force:apex:execute -f scripts/apex/createUsers.apex -u rr

The code can be adapted to use CSV or JSON or key-values pair format

0

I liked Patlatus answer above but it expects each user to be a separate variable constructed with all the required attributes. Instead make use of the sObject tree as is you get while exporting and read in the anonymous apex you write. I tried something like below

String usersJson = '{records:[the records array from sObject tree export goes here]}';

Map<String, Object> usersJsonObj = (Map<String, Object>) JSON.deserializeUntyped(usersJson);
Object obj = usersJsonObj.get('records');
List<Object> objList = (List<Object>)usersJsonObj.get('records');
List<User> usersList = new List<User>();
Profile sysAdminProfile = [SELECT Id FROM Profile WHERE Name = 'System Administrator' LIMIT 1];
for(Object userObj : objList) {
    System.debug(userObj);
    Map<String,Object> userFields = (Map<String,Object>)userObj;
    usersList.add(new User(Alias=userFields.get('Alias').toString(),
                Username=userFields.get('Username').toString(),
                Email=userFields.get('Email').toString(),
                FirstName=userFields.get('FirstName').toString(),
                LastName=userFields.get('LastName').toString(),
                LocaleSidKey=userFields.get('LocaleSidKey').toString(),
                LanguageLocaleKey=userFields.get('LanguageLocaleKey').toString(),
                EmailEncodingKey=userFields.get('EmailEncodingKey').toString(),
                TimeZoneSidKey=userFields.get('TimeZoneSidKey').toString(),
                ProfileId=sysAdminProfile.Id));
}
if(usersList.size() > 0) {
    try {
        insert usersList;
    }
    catch(Exception e) {
        System.debug('Exception message ' + e.getMessage());
    }
}

1
  • 1
    My version gives the flexibility to use different profile assignment and role assignment
    – Patlatus
    Jul 23 '21 at 7:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.