DEV Community

loading...
Cover image for Migrate principalNames for user accounts in Azure DevOps

Migrate principalNames for user accounts in Azure DevOps

Kai Walter
35+ years software and IT project veteran
・3 min read

Some while back I created a script that helps migrating user accounts in Azure DevOps and - if required - invited user accounts in the connected Azure Active Directory from one principal name to another.

Although it can be used for these cases ...

  • migrate from one domain to another e.g. userid@old-domain.com to userid@new-domain.com
  • migrate from one principal name to another e.g. old-userid@domain.com to new-userid@domain.com
  • migrate when last- and/or firstname had changed e.g. firstname.old-lastname@domain.com to firstname.new-lastname@domain.com
  • copy Azure Active Directory, Azure Resource Manager and/or Azure DevOps authorizationsfrom one user account to another e.g. userid@domain.com to some-other-userid@domain.com

... it was originally created for the case when principal names for invited accounts in an Azure Active Directory with connected Azure DevOps accounts change e.g. from userid@domain.com to firstname.lastname@domain.com.

abbreviations used in this post

term
AAD Azure Active Directory
ARM Azure Resource Manager
AzD Azure DevOps
UPN User Principal Name

processing flow of the script

The script migration.py has this main processes:

  • capture AAD (Azure Active Directory) user account group assignments and ARM (Azure Resource Manager) role assignments; capture AzD (Azure DevOps) entitlements and group assignments
  • delete accounts previously captured from AAD and AzD
  • rebuild accounts previously captured in AAD and AzD - inviting the AAD account, re-assigning group memberships, re-assigning ARM role assignments, re-creating in AzD, re-assigning group memberships and entitlements in AzD
  • transfer work items from one principal to another

Preparations

install dependencies

if you care - create and activate a Python virtual environment before

  • install Python 3.7+
  • install dependencies : pip install -r .\requirements.txt

configure Azure and Azure DevOps accounts

DISCLAIMER! The success of capture and rebuild hugely depends on the authorizations given to these accounts. Check the captured information thoroughly. I had cases where I had to assign IAM / Subscription Owner to the registered app.

For Azure an registered app is required with these application permissions:
Microsoft Graph -> Directory.ReadWrite.All + User.Invite.All + User.ReadWrite.All

For each Azure DevOps account to be processed a PAT with _Full access has to be created.

With that information create a configuration file .profile/accounts.json and set tentant, password and appId in aadAccount section. In section azdAccounts add a url and pat for each Azure DevOps account:

{
    "aadAccount": {
        "tenant": "***tenant***",
        "password": "***password***",
        "appId": "***appid***"
    },
    "azdAccounts": {
        "***accountkey1***": {
            "url": "https://dev.azure.com/***account1***",
            "pat": "***full access - personal access token***"
        },
        "***accountkey2***": {
            "url": "https://dev.azure.com/***account2***",
            "pat": "***full access - personal access token***"
        }
    }
}
value origin
***tenant*** Tenant Id (GUID format) of the Azure Active Directory to which the Azure DevOps accounts are linked to
***password*** Client secret / password for the AAD registered app
***appid*** Client Id / App Id for the AAD registered app
***accountkeyn*** A free short key identifying your Azure DevOps accounts in the migration process
***accountn*** Account URL suffix
***full access - personal access token*** Full access PAT to the particular account

Usage

listing users

List AAD users with their reference to the configured Azure DevOps accounts into a file userlist.txt.

This option expects a RegEx pattern to identify the user account in the AAD.

.\migration.py -l "^ab.*@domain.com$"

capture user account information

Users to be captured (analyzed) for a migration can be either specified in a text file - as a migrate from UPN to UPN pair - with a line for each user ...

userid@domain.com,firstname.lastname@domain.com
userid2@domain.com,firstname2.lastname2@domain.com
.\migration.py -c -f .\upnsbatch1.txt

... or directly as an argument

.\migration.py -c -u "userid@domain.com,firstname.lastname@domain.com"

Results of the capture can be verified in migration.json.

delete old user

Delete capture (old) user accounts:

.\migration.py -d

Delete only in Azure DevOps:

.\migration.py -d --azd

rebuild with new user

Rebuild user accounts from captured information:

.\migration.py -r

transfer work items

Transfer work items from captured information:

.\migration.py -w

Transfer work items directly (full diplayname is expected):

.\migration.py -w -u "John Doe <john.doe@old-domain.com>,John Doe <john.doe@new-domain.com>"

general parameters

parameter purpose
--aad only process Azure Active Directory (capture, delete, rebuild)
--azd only process Azure DevOps (capture, delete, rebuild)
--debug switch logging to DEBUG mode so that SDK logs API calls

Discussion (0)