How to migrate groups from one directory to another in a bulk
Platform Notice: Data Center - This article applies to Atlassian products on the Data Center platform.
Note that this knowledge base article was created for the Data Center version of the product. Data Center knowledge base articles for non-Data Center-specific features may also work for Server versions of the product, however they have not been tested. Support for Server* products ended on February 15th 2024. If you are running a Server product, you can visit the Atlassian Server end of support announcement to review your migration options.
*Except Fisheye and Crucible
Before going through this KB article, please review Migrating Users between User Directories first.
This article intends to provide this solution "as is". Before making any changes in production, we strongly advise reviewing it, backing up Jira, and testing in a lower environment.
Summary
Jira manages group membership per directory. Hence, when adding new directories or moving users between directories, the administrator shall add the users' group to:
- The directory's external source (for example, when the LDAP provides the group);
- To Jira to update the directory's external source (for example, when the LDAP provides the group but no group was set in LDAP and Jira will update it - a READ-WRITE directory); or
- To the internal directory (if an external directory with local groups or an internal directory).
In a scenario where the administrator shall update the group membership in Jira to update the directories (items 2 and 3 above), a manual intervention might be difficult or a timely cost when the number of users is high. Below, we'd like to present a solution for bulk assigning those memberships.
The solution
The solution involves creating a CSV file with the users and assigned groups and bulk updating them through a direct curl command. The users and directories shall already be set in the environment.
Once again, please read all the steps carefully and always test them in a lower environment.
1. Create the CSV file with users and assigned groups
First, take a note of the directory id of the directory that you want to use as a source of users and groups with:
SELECT id, directory_name, description, directory_type FROM cwd_directory;
Then get the CSV of the users and groups of that specific directory (postgresql query, adjust for other RDBMS):
COPY (
SELECT cu.lower_user_name, cg.group_name
FROM cwd_user cu
JOIN cwd_membership cm
ON cu.id=cm.child_id
JOIN cwd_group cg
ON cm.parent_id=cg.id
WHERE cu.directory_id=<id-from-the-source-directory> AND cg.directory_id=<id-from-the-source-directory>
)
TO '/tmp/user_and_groups.csv' DELIMITER ';'
CSV;
Note if the users and groups are correctly set and with the correct format, as the example:
admin;ADM - Poweruser
admin;jira-users
customer-1;customer group
...
2. Double-check the directory order and change if necessary
Attention to this point: once the users are present in more than one directory, move the directory to have the groups added to the first (to the top) in the directory list (read this article for more details).
3. Prepare and run the automatic group membership setup script
With the users and groups correctly set and reviewed in the /tmp/user_and_groups.csv, run the script (Linux bash based) below to set the group membership to those users.
cat /tmp/user_and_groups.csv | while read LINE; do
username=`echo $LINE | cut -d';' -f1`
group=`echo $LINE | cut -d';' -f2 | sed 's/ /\%20/g'`
echo "\"$username:$group - <your-jira-base-url>/rest/api/2/group/user?groupname=$group\" --data \"{ \"name\": \"$username\" }\" "
curl -D- -u user:password -H "Content-Type: application/json" -X POST "<your-jira-base-url>/rest/api/2/group/user?groupname=$group" --data "{ \"name\": \"$username\" }"
echo; echo
done
The solution is using user and password ("-u user:password") in the curl command. If you can, we recommend using Personal Access Tokens instead.
If using windows or prefer Postman, as alternative for above script, see the How to send bulk API requests using Postman.
As an example of the output, we have from a lab (trimmed output):
% cat csv_file
admin;ADM - Poweruser
admin;jira-users
customer-1;customer group
% cat csv_file | while read LINE; do
username=`echo $LINE | cut -d';' -f1`
group=`echo $LINE | cut -d';' -f2 | sed 's/ /\%20/g'`
echo "\"$username:$group - https://linux-90706.prod.atl-cd.net/jira/rest/api/2/group/user?groupname=$group\" --data \"{ \"name\": \"$username\" }\" "
curl -D- -u admin:sphere -H "Content-Type: application/json" -X POST "https://linux-90706.prod.atl-cd.net/jira/rest/api/2/group/user?groupname=$group" --data "{ \"name\": \"$username\" }"
echo; echo
done
"admin:GLOB%20-%20Customers - https://linux-90706.prod.atl-cd.net/jira/rest/api/2/group/user?groupname=ADM%20-%20Poweruser" --data "{ "name": "admin" }"
HTTP/2 201
...
{"name":"ADM - Poweruser","self":"https://linux-90706.prod.atl-cd.net/jira/rest/api/2/group?groupname=ADM+-+Poweruser","users":{"size":5,"items":[],"max-results":50,"start-index":0,"end-index":0},"expand":"users"}
"admin:jira-users - https://linux-90706.prod.atl-cd.net/jira/rest/api/2/group/user?groupname=jira-users" --data "{ "name": "admin" }"
HTTP/2 201
...
{"name":"jira-administrators","self":"https://linux-90706.prod.atl-cd.net/jira/rest/api/2/group?groupname=jira-administrators","users":{"size":4,"items":[],"max-results":50,"start-index":0,"end-index":0},"expand":"users"}
"customer-1:customer%20-%20group - https://linux-90706.prod.atl-cd.net/jira/rest/api/2/group/user?groupname=customer%20-%20group" --data "{ "name": "customer-1" }"
HTTP/2 201
...
{"name":"customer - group","self":"https://linux-90706.prod.atl-cd.net/jira/rest/api/2/group?groupname=customer+-+group","users":{"size":6,"items":[],"max-results":50,"start-index":0,"end-index":0},"expand":"users"}
Note the http response 201 (meaning it was correctly set) and the json indicating the group was upgraded. If you face errors, check the response from each request, they will display the "errorMessages" at the bottom.
4. Post check
After updating the user membership, generate a new CSV file, as done in step 1, but now for the updated directory, and compare this file with the one generated for the source directory. If you see any discrepancies, correct them accordingly.
Important links
Here are important links cited in this article or that are also good to read:
- Move local group memberships between directories in Jira Data Center
- How to send bulk API requests using Postman