{"id":400,"date":"2020-05-19T22:56:46","date_gmt":"2020-05-20T05:56:46","guid":{"rendered":"https:\/\/raju.guide\/?p=400"},"modified":"2020-07-04T17:55:46","modified_gmt":"2020-07-05T00:55:46","slug":"update-all-jira-usernames","status":"publish","type":"post","link":"https:\/\/raju.guide\/index.php\/2020\/05\/19\/update-all-jira-usernames\/","title":{"rendered":"Update All Jira Usernames"},"content":{"rendered":"\n<p>Recently I&#8217;d been asked to integrate Jira with Identity Provider (IdP) so that we can have Single Sign On (SSO) in place. We&#8217;re using out of box integration available in Jira, but it was not working as expected!<\/p>\n\n\n\n<p>It turns out that IdP was providing email as SAML userid but Jira was expecting plain username. And since this both are different in Jira world, end result was Jira couldn&#8217;t able to log in given user.<\/p>\n\n\n\n<p>To fix this problem, Atlassian suggested we need to update all usernames as emails. This in turn forced me to write a script using Jira REST APIs so that I could all usernames to their emails.<\/p>\n\n\n\n<p>Also I was able to use <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/tqdm\/tqdm\/wiki\/How-to-make-a-great-Progress-Bar\" target=\"_blank\">TQDM<\/a> library to show progress of username retrieval, something I learned new along the way.<\/p>\n\n\n\n<p>Here is the link to <a href=\"https:\/\/github.com\/rkadam\/jira\">source code<\/a> if you like to give a try. Let me know what do you think<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import logging\nimport requests\nfrom dotenv import load_dotenv\nimport os\nimport sys\nfrom pprint import pformat\nimport json\nfrom datetime import datetime\n\n# https:\/\/towardsdatascience.com\/progress-bars-in-python-4b44e8a4c482?gi=6a0158a5a16e\nfrom tqdm.auto import tqdm\n\nlogging_level_dict = {\n    'DEBUG': logging.DEBUG,\n    'INFO': logging.INFO,\n    'ERROR': logging.ERROR,\n    'WARNING': logging.WARNING,\n    'CRITICAL': logging.CRITICAL\n}\n\ndef get_jira_users(jira_logger, jira_base_url, auth, group_name, include_inactive_users, start_at):\n\n    users = &#91;]\n    user_count = 0\n    pbar = tqdm(total=100)\n\n    resp = requests.get(f'{jira_base_url}rest\/api\/2\/group\/member?groupname={group_name}&amp;includeInactiveUsers={include_inactive_users}&amp;startAt={start_at}', auth=auth)    \n    if resp.status_code == 200:\n        total = resp.json()&#91;'total']\n        start_at = resp.json()&#91;'startAt']\n        current_result_count = len(resp.json()&#91;'values'])\n\n        while (current_result_count > 0):\n            jira_logger.debug(f'Total - {total}, Starts At - {start_at}, Current Result Count - {current_result_count}')\n            current_user_set = resp.json()&#91;'values']\n            for i in range(current_result_count):\n                jira_logger.debug(f\"{current_user_set&#91;i]&#91;'name']},{current_user_set&#91;i]&#91;'emailAddress']},{current_user_set&#91;i]&#91;'displayName']},{current_user_set&#91;i]&#91;'active']}\")\n                users.append(f\"{current_user_set&#91;i]&#91;'name']},{current_user_set&#91;i]&#91;'emailAddress']},{current_user_set&#91;i]&#91;'displayName']},{current_user_set&#91;i]&#91;'active']}\")\n                user_count += 1\n            \n            percent = (current_result_count\/total)*100\n            pbar.update(percent)\n\n            # Get next set of results if available.\n            start_at = start_at + current_result_count            \n            resp = requests.get(f'{jira_base_url}rest\/api\/2\/group\/member?groupname={group_name}&amp;includeInactiveUsers={include_inactive_users}&amp;startAt={start_at}', auth=auth)    \n            current_result_count = len(resp.json()&#91;'values'])\n    else:\n        jira_logger.error(f\"Response Code: {resp.status_code}, Response Message: {resp.text}\")\n\n    jira_logger.info(f\"Total {user_count} users found in group - '{group_name}'\")    \n    pbar.close()\n\n    return users\n\ndef update_jira_username(jira_logger, jira_base_url, auth, username, new_username_value):\n\n    is_update_successful = True\n    update_status_code = 200\n    update_status_message = \"\"\n    \n    headers = {'Content-type': 'application\/json'}\n    \n    # name is username attribute in Jira Internal Directory\n    json_body = {\n        'name' : new_username_value\n    }\n    \n    resp = requests.put(f'{jira_base_url}rest\/api\/2\/user?username={username}', data=json.dumps(json_body), auth=auth, headers=headers)\n    if resp.status_code != 200:\n        is_update_successful = False\n        update_status_code = resp.status_code\n        update_status_message = resp.json()&#91;\"errors\"]&#91;\"active\"]\n        jira_logger.error(f\"Response Code: {update_status_code}, Response Message: {update_status_message}\")\n    \n    jira_logger.info(f\"{username} - update status: {is_update_successful}\")\n    return is_update_successful, update_status_code, update_status_message\n\n# update user name with value from emailAddress\ndef update_jira_usernames(jira_logger, jira_base_url, auth, group_name, user_dict):\n    update_operation_status_list = &#91;]\n    update_operation_status_list.append(\"username,Is Update Successful?, Error Details\")\n\n    with open(f\"{group_name}.group_users_update_execution.csv\", \"a\") as output_csvfile:\n\n        output_csvfile.writelines(\"\\n\\n\")\n        output_csvfile.writelines(datetime.now().strftime(\"%d\/%b\/%Y %H:%M:%S\") + \"\\n\\n\")\n        \n        for username in user_dict:\n            #if username is already updated to email, skip this user!\n            if username != user_dict&#91;username]:\n                jira_logger.info(f\"Update username from {username} to {user_dict&#91;username]}\")\n                update_status_info_tuple = update_jira_username(jira_logger, jira_base_url, auth, username, user_dict&#91;username])\n                jira_logger.debug(f\"{username} - {update_status_info_tuple&#91;0]}\")\n                update_operation_status_list.append(username + \",\" + str(update_status_info_tuple&#91;0]) + \",\" + update_status_info_tuple&#91;2])\n                output_csvfile.writelines(username + \",\" + str(update_status_info_tuple&#91;0]) + \",\" + update_status_info_tuple&#91;2]+ \"\\n\")\n            \ndef main():\n\n    load_dotenv(override=True)\n\n    jira_base_url = os.getenv('JIRA_ENV_BASE_URL')\n    jira_env = os.getenv('JIRA_ENV')\n\n    jira_logger = logging.getLogger(__name__)\n    # Following check is necessary otherwise everytime we run this in Jupyter lab Cell, new handler is getting added resulting in duplicate logs printed!\n    if not jira_logger.handlers:\n        jira_logger.setLevel(logging_level_dict&#91;os.getenv('LOG_LEVEL')])\n\n        file_handler = logging.FileHandler(os.getenv('LOG_FILE'))\n        file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - line %(lineno)d - %(message)s'))\n\n        out_hdlr = logging.StreamHandler(sys.stdout)\n        out_hdlr.setFormatter(logging.Formatter('line %(lineno)d - %(message)s'))\n\n        jira_logger.addHandler(out_hdlr)\n        jira_logger.addHandler(file_handler)\n\n    auth = (os.getenv('USERID'), os.getenv('PASSWORD'))\n\n    group_name = 'jira-software-users'\n    include_inactive_users = 'true'\n    start_at = 0\n    \n    jira_logger.info(\"Reteriving current usernames to store in backup file!\")\n    users = get_jira_users(jira_logger, jira_base_url, auth, group_name, include_inactive_users, start_at)\n    \n    if users:\n        jira_logger.info(\"Backup complete!\")\n        # First write current usernames into backup file in case we need them.\n        with open(f\"{group_name}.{jira_env}.txt\", 'w') as filehandle:\n            filehandle.writelines(\"username,email,display_name,is_user_active\\n\")\n            filehandle.writelines(\"%s\\n\" % user for user in users)\n\n        username_email_dict = {}\n        for user in users:\n            #Also we will skip username as which we will be running this script, so that subsequent calls will not fail!\n            username = user.split(',')&#91;0].strip()\n            email = user.split(',')&#91;1].strip()\n            # We don't want to update logged in User's username otherwise script will fail.\n            # we will need to update username to email to make it working agian!\n            if username not in &#91;os.getenv('USERID')]:\n                username_email_dict&#91;username] = email\n\n        jira_logger.info(\"Updting usernames now...\")\n        update_jira_usernames(jira_logger, jira_base_url, auth, group_name, username_email_dict)\n        jira_logger.info(\"Update complete!\")\n\nif __name__ == '__main__':\n    main()<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Recently I&#8217;d been asked to integrate Jira with Identity Provider (IdP) so that we can have Single Sign On (SSO) in place. We&#8217;re using out of box integration available in Jira, but it was not working as expected! It turns out that IdP was providing email as SAML userid but Jira was expecting plain username. &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[4,5],"tags":[],"class_list":{"0":"post-400","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"hentry","6":"category-atlassian","7":"category-jira","9":"without-featured-image"},"jetpack_sharing_enabled":true,"featured_image_src":null,"featured_image_src_square":null,"author_info":{"display_name":"adminraju","author_link":"https:\/\/raju.guide\/index.php\/author\/adminraju\/"},"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/raju.guide\/index.php\/wp-json\/wp\/v2\/posts\/400"}],"collection":[{"href":"https:\/\/raju.guide\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/raju.guide\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/raju.guide\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/raju.guide\/index.php\/wp-json\/wp\/v2\/comments?post=400"}],"version-history":[{"count":1,"href":"https:\/\/raju.guide\/index.php\/wp-json\/wp\/v2\/posts\/400\/revisions"}],"predecessor-version":[{"id":401,"href":"https:\/\/raju.guide\/index.php\/wp-json\/wp\/v2\/posts\/400\/revisions\/401"}],"wp:attachment":[{"href":"https:\/\/raju.guide\/index.php\/wp-json\/wp\/v2\/media?parent=400"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/raju.guide\/index.php\/wp-json\/wp\/v2\/categories?post=400"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/raju.guide\/index.php\/wp-json\/wp\/v2\/tags?post=400"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}