Add Microsoft Entra ID login to users

Octopus supports a number of external authentication providers, including Microsoft Entra ID Authentication. If you want to use Microsoft Entra ID to authenticate but re-use existing Octopus user accounts, the easiest way is to add an Azure AD login:

Add an Microsoft Entra ID login to an Octopus user

This script will add Microsoft Entra ID login details to Octopus user accounts.

Usage

Provide values for:

  • Octopus URL
  • Octopus API Key
  • A list of users, supplied from either:
    • The path to a CSV file containing user records
    • The Octopus Username, Azure email address and (optionally) Azure display name
  • (Optional) whether or not to update the Octopus user’s email address
  • (Optional) whether or not to update the Octopus user’s display name
  • (Optional) whether or not to continue to the next user if an error occurs
  • (Optional) whether or not to force an update of the Azure AD identity if one already exists
  • (Optional) whether or not to perform a dry run (What If?) and not perform any updates
  • (Optional) whether or not to toggle debug (Verbose) logging

Add Microsoft Entra ID identities to single user

AddAzureADLogins -OctopusURL "https://your-octopus-url/" -OctopusAPIKey "API-YOUR-KEY" -OctopusUsername "OctoUser" -AzureEmailAddress "octouser@exampledomain.com" -AzureDisplayName "Octo User" -ContinueOnError $False -Force $False -WhatIf $False -DebugLogging $False

Add Microsoft Entra ID identities for multiple users from CSV file

AddAzureADLogins -OctopusURL "https://your-octopus-url/" -OctopusAPIKey "API-YOUR-KEY" -Path "/path/to/user_azure_ad_logins.csv" -ContinueOnError $False -Force $False -WhatIf $False -DebugLogging $False

Example CSV file

An example of the expected CSV file format is shown below:

OctopusUsername, AzureEmailAddress, AzureDisplayName
OctoUser, octouser@exampledomain.com, Octo User 

The first row should be the header row containing the following columns:

  • OctopusUsername
  • AzureEmailAddress
  • AzureDisplayName

Script

PowerShell (REST API)
function AddAzureADLogins(
    [Parameter(Mandatory=$True)]
    [String]$OctopusURL,
    [Parameter(Mandatory=$True)]
    [String]$OctopusAPIKey,
    [String]$Path,
    [String]$OctopusUsername,
    [String]$AzureEmailAddress,
    [String]$AzureDisplayName = $null,
    [Boolean]$UpdateOctopusEmailAddress = $False,
    [Boolean]$UpdateOctopusDisplayName = $False,
    [Boolean]$ContinueOnError = $False,
    [Boolean]$Force = $False,
    [Boolean]$WhatIf = $True,
    [Boolean]$DebugLogging = $False
)
{
    Write-Host "OctopusURL: $OctopusURL"
    Write-Host "OctopusAPIKey: ********"
    Write-Host "Path: $Path"
    Write-Host "OctopusUsername: $OctopusUsername"
    Write-Host "AzureEmailAddress: $AzureEmailAddress"
    Write-Host "AzureDisplayName: $AzureDisplayName"
    Write-Host "UpdateOctopusEmailAddress: $UpdateOctopusEmailAddress"
    Write-Host "UpdateOctopusDisplayName: $UpdateOctopusDisplayName"
    Write-Host "ContinueOnError: $ContinueOnError"
    Write-Host "Force: $Force"
    Write-Host "WhatIf: $WhatIf"
    Write-Host "DebugLogging: $DebugLogging"
    Write-Host $("=" * 60)
    Write-Host

    if (-not [string]::IsNullOrWhiteSpace($OctopusURL)) {
        $OctopusURL = $OctopusURL.TrimEnd('/')
    }

    if($DebugLogging -eq $True) {
        $DebugPreference = "Continue"
    }

    $header = @{ "X-Octopus-ApiKey" = $octopusAPIKey }
    $usersToUpdate = @()
    $recordsUpdated = 0
    # Validate we have minimum required details.
    if ([string]::IsNullOrWhiteSpace($Path) -eq $true) {
        if([string]::IsNullOrWhiteSpace($OctopusUsername) -eq $true -or [string]::IsNullOrWhiteSpace($AzureEmailAddress) -eq $true) {
            Write-Warning "Path not supplied. OctopusUsername or AzureEmailAddress are either null, or an empty string."
            return
        }
        $usersToUpdate += [PSCustomObject]@{
            OctopusUsername = $OctopusUsername
            AzureEmailAddress = $AzureEmailAddress
            AzureDisplayName = $AzureDisplayName
        }
    }
    else {
        # Validate path 
        if(-not (Test-Path $Path)) {
            Write-Warning "Path '$Path' not found. Does a file exist at that location?"
            return
        }

        $usersToUpdate = Import-Csv -Path $Path -Delimiter ","
    }

    # Check if we have any users. If we do, get existing octopus users
    if($usersToUpdate.Count -gt 0) {
        Write-Host "Users to update: $($usersToUpdate.Count)"
        $ExistingOctopusUsers = @()
        $response = $null
        do {
            $uri = if ($response) { $octopusURL + $response.Links.'Page.Next' } else { "$OctopusURL/api/users" }
            $response = Invoke-RestMethod -Method Get -Uri $uri -Headers $header
            $ExistingOctopusUsers += $response.Items
        } while ($response.Links.'Page.Next')

        Write-Debug "Found $($ExistingOctopusUsers.Count) existing Octopus users"
    }
    else {
        Write-Host "No users to update, exiting."
        return
    }
    
    if($ExistingOctopusUsers.Count -le 0) {
        Write-Warning "No users found in Octopus, exiting."
        return
    }

    foreach($user in $usersToUpdate)
    {
        Write-Host "Working on user $($User.OctopusUsername)"
        try {
            $existingOctopusUser = $ExistingOctopusUsers | Where-Object {$_.Username -eq $user.OctopusUsername} | Select-Object -First 1
            if($null -ne $ExistingOctopusUser) {
                Write-Debug "Found matching octopus user for $($user.OctopusUsername)"
                # Check if its a service account
                if($user.IsService -eq $True) {
                    Write-Debug "User $($user.OctopusUsername) is a Service account. This user won't be updated..."
                    continue
                }
                # Check if its an active account
                if($user.IsActive -eq $False) {
                    Write-Debug "User $($user.OctopusUsername) is an inactive account. This user won't be updated..."
                    continue
                }

                # Check for existing Microsoft Entra ID Identity first.
                $azureAdIdentity = $existingOctopusUser.Identities | Where-Object {$_.IdentityProviderName -eq "Azure AD"} | Select-Object -First 1
                if($null -ne $azureAdIdentity) {
                    Write-Debug "Found existing Microsoft Entra ID login for user $($user.OctopusUsername)"
                    if($Force -eq $True) {
                        Write-Debug "Force set to true. Replacing existing Microsoft Entra ID Claims for Display Name and Email for user $($user.OctopusUsername)"
                        $azureAdIdentity.Claims.email.Value = $User.AzureEmailAddress
                        $azureAdIdentity.Claims.dn.Value = $User.AzureDisplayName
                    }
                    else {
                        Write-Debug "Force set to false. Skipping replacing existing Microsoft Entra ID Claims for Display Name and Email for user $($user.OctopusUsername)"
                    }
                }
                else {
                    Write-Debug "No existing Microsoft Entra ID login found for user $($user.OctopusUsername), creating new"
                    $newAzureADIdentity = @{
                        IdentityProviderName = "Azure AD"
                        Claims = @{
                            email = @{
                                Value = $User.AzureEmailAddress
                                IsIdentifyingClaim = $True
                            }
                            dn = @{
                                Value = $User.AzureDisplayName
                                IsIdentifyingClaim = $False
                            }
                        }
                    }
                    $existingOctopusUser.Identities += $newAzureADIdentity
                }

                # Update user's email address if set AND the value isn't empty.
                if($UpdateOctopusEmailAddress -eq $True -and -not([string]::IsNullOrWhiteSpace($User.AzureEmailAddress) -eq $true)) {
                    Write-Debug "Setting Octopus email address to: $($User.AzureEmailAddress)"
                    $existingOctopusUser.EmailAddress = $User.AzureEmailAddress
                }

                 # Update user's display name if set AND the value isn't empty.
                 if($UpdateOctopusDisplayName -eq $True -and -not([string]::IsNullOrWhiteSpace($User.AzureDisplayName) -eq $true)) {
                    Write-Debug "Setting Octopus display name to: $($User.AzureDisplayName)"
                    $existingOctopusUser.DisplayName = $User.AzureDisplayName
                }

                $userJsonPayload = $($existingOctopusUser | ConvertTo-Json -Depth 10)

                if($WhatIf -eq $True) {
                    Write-Host "What If set to true, skipping update for user $($User.OctopusUsername). For details of the payload, set DebugLogging to True"
                    Write-Debug "Would have done a POST to $OctopusUrl/api/users/$($existingOctopusUser.Id) with body:"
                    Write-Debug $userJsonPayload
                } 
                else {
                    Write-Host "Updating the user $($User.OctopusUsername) in Octopus Deploy"
                    Invoke-RestMethod -Method PUT -Uri "$OctopusUrl/api/users/$($existingOctopusUser.Id)" -Headers $header -Body $userJsonPayload | Out-Null
                    $recordsUpdated += 1
                }
            }
            else {
                Write-Warning "No match found for an existing octopus user with Username: $($User.OctopusUsername)"
            }
        }
        catch {
            If($ContinueOnError -eq $true) {
                Write-Warning "Error encountered updating $($User.OctopusUsername): $($_.Exception.Message), continuing..."
                continue
            }
            else {
                throw
            }
        }
    }
    Write-Host "Updated $($recordsUpdated) user records."
}
PowerShell (Octopus.Client)
# Load assembly
Add-Type -Path 'path:\to\Octopus.Client.dll'

function AddAzureLogins
(
    [Parameter(Mandatory=$True)]
    [String]$OctopusURL,
    [Parameter(Mandatory=$True)]
    [String]$OctopusAPIKey,
    [String]$Path,
    [String]$OctopusUsername,
    [String]$AzureEmailAddress,
    [String]$AzureDisplayName = $null,
    [Boolean]$UpdateOctopusEmailAddress = $False,
    [Boolean]$UpdateOctopusDisplayName = $False,
    [Boolean]$ContinueOnError = $False,
    [Boolean]$Force = $False,
    [Boolean]$WhatIf = $True,
    [Boolean]$DebugLogging = $False
)
{
    Write-Host "OctopusURL: $OctopusURL"
    Write-Host "OctopusAPIKey: ********"
    Write-Host "Path: $Path"
    Write-Host "OctopusUsername: $OctopusUsername"
    Write-Host "AzureEmailAddress: $AzureEmailAddress"
    Write-Host "AzureDisplayName: $AzureDisplayName"
    Write-Host "UpdateOctopusEmailAddress: $UpdateOctopusEmailAddress"
    Write-Host "UpdateOctopusDisplayName: $UpdateOctopusDisplayName"
    Write-Host "ContinueOnError: $ContinueOnError"
    Write-Host "Force: $Force"
    Write-Host "WhatIf: $WhatIf"
    Write-Host "DebugLogging: $DebugLogging"
    Write-Host $("=" * 60)
    Write-Host

    if (-not [string]::IsNullOrWhiteSpace($OctopusURL)) {
        $OctopusURL = $OctopusURL.TrimEnd('/')
    }

    if($DebugLogging -eq $True) {
        $DebugPreference = "Continue"
    }

    
    $endpoint = New-Object Octopus.Client.OctopusServerEndpoint($OctopusURL, $OctopusAPIKey)
    $repository = New-Object Octopus.Client.OctopusRepository($endpoint)
    $client = New-Object Octopus.Client.OctopusClient($endpoint)

    $usersToUpdate = @()
    $recordsUpdated = 0
    # Validate we have minimum required details.
    if ([string]::IsNullOrWhiteSpace($Path) -eq $true) {
        if([string]::IsNullOrWhiteSpace($OctopusUsername) -eq $true -or [string]::IsNullOrWhiteSpace($AzureEmailAddress) -eq $true) {
            Write-Warning "Path not supplied. OctopusUsername or AzureEmailAddress are either null, or an empty string."
            return
        }
        $usersToUpdate += [PSCustomObject]@{
            OctopusUsername = $OctopusUsername
            AzureEmailAddress = $AzureEmailAddress
            AzureDisplayName = $AzureDisplayName
        }
    }
    else {
        # Validate path 
        if(-not (Test-Path $Path)) {
            Write-Warning "Path '$Path' not found. Does a file exist at that location?"
            return
        }

        $usersToUpdate = Import-Csv -Path $Path -Delimiter ","
    }
    
    # Check if we have any users. If we do, get existing octopus users
    if($usersToUpdate.Count -gt 0) {
        Write-Host "Users to update: $($usersToUpdate.Count)"
        $ExistingOctopusUsers = @()

        # Loop through users
        foreach ($user in $usersToUpdate)
        {
            # Retrieve user account from Octopus
            Write-Host "Searching Octopus users for $($user.OctopusUsername) ..."
            $existingOctopusUser = $client.Repository.Users.FindByUsername($user.OctopusUsername)
            
            # Check for null
            if ($null -ne $existingOctopusUser)
            {
                # Check user types
                if ($existingOctopusUser.IsService)
                {
                    # This is a service account and will not be updated
                    Write-Warning "$($user.OctopusUsername) is a service account, skipping ..."

                    continue
                }

                if ($existingOctopusUser.IsActive -eq $False)
                {
                    # Inactive user skipping
                    Write-Warning "$($user.OctopusUsername) is an inactive account, skipping ..."

                    continue
                }

                # Check to see if there's already an Microsoft Entra ID identity
                $azureAdIdentity = $existingOctopusUser.Identities | Where-Object {$_.IdentityProviderName -eq "Azure AD"}
                if($null -ne $azureAdIdentity) 
                {
                    Write-Debug "Found existing Microsoft Entra ID login for user $($user.OctopusUsername)"
                    if($Force -eq $True) 
                    {
                        Write-Debug "Force set to true. Replacing existing Microsoft Entra ID Claims for Display Name and Email for user $($user.OctopusUsername)"
                        $azureAdIdentity.Claims.email.Value = $User.AzureEmailAddress
                        $azureAdIdentity.Claims.dn.Value = $User.AzureDisplayName
                    }
                    else 
                    {
                        Write-Warning "Force set to false. Skipping replacing existing Microsoft Entra ID Claims for Display Name and Email for user $($user.OctopusUsername)"
                    }
                }
                else 
                {
                    Write-Debug "No existing Microsoft Entra ID login found for user $($user.OctopusUsername), creating new"
                    $newAzureADIdentity = New-Object Octopus.Client.Model.IdentityResource
                    $newAzureADIdentity.IdentityProviderName = "Azure AD"
                    
                    $newEmailClaim = New-Object Octopus.Client.Model.IdentityClaimResource
                    $newEmailClaim.IsIdentifyingClaim = $True
                    $newEmailClaim.Value = $user.AzureEmailAddress

                    $newAzureADIdentity.Claims.Add("email", $newEmailClaim) # Claims is a Dictionary object

                    $newDisplayClaim = New-Object Octopus.Client.Model.IdentityClaimResource
                    $newDisplayClaim.IsIdentifyingClaim = $False
                    $newDisplayClaim.Value = $user.AzureDisplayName

                    $newAzureADIdentity.Claims.Add("dn", $newDisplayClaim)

                    $existingOctopusUser.Identities += $newAzureADIdentity # Identities is an array
                }

                # Update user's email address if set AND the value isn't empty.
                if($UpdateOctopusEmailAddress -eq $True -and -not([string]::IsNullOrWhiteSpace($User.AzureEmailAddress) -eq $true)) 
                {
                    Write-Debug "Setting Octopus email address to: $($User.AzureEmailAddress)"
                    $existingOctopusUser.EmailAddress = $User.AzureEmailAddress
                }

                # Update user's display name if set AND the value isn't empty.
                if($UpdateOctopusDisplayName -eq $True -and -not([string]::IsNullOrWhiteSpace($User.AzureDisplayName) -eq $true)) 
                {
                    Write-Debug "Setting Octopus display name to: $($User.AzureDisplayName)"
                    $existingOctopusUser.DisplayName = $User.AzureDisplayName
                }

                if($WhatIf -eq $True) 
                {
                    Write-Host "What If set to true, skipping update for user $($User.OctopusUsername). For details of the payload, set DebugLogging to True"
                    Write-Debug "Would have done a POST to $OctopusUrl/api/users/$($existingOctopusUser.Id) with body:"
                    Write-Debug $userJsonPayload
                } 
                else 
                {
                    Write-Host "Updating the user $($User.OctopusUsername) in Octopus Deploy"
                    $client.Repository.Users.Modify($existingOctopusUser)
                    $recordsUpdated += 1
                }
            }
            else
            {
                # User not found
                Write-Warning "$($user.OctopusUsername) not found!"
            }
        }
        Write-Debug "Found $($ExistingOctopusUsers.Count) existing Octopus users"
    }
    else {
        Write-Host "No users to update, exiting."
        return
    }
}
C#
#r "path\to\Octopus.Client.dll"

using Octopus.Client;
using Octopus.Client.Model;
using System.Linq;

public class UserToUpdate
{
    public string OctopusUserName
    {
        get;
        set;
    }

    public string AzureEmailAddress
    {
        get;
        set;
    }

    public string AzureDisplayName
    {
        get;
        set;
    }
}

public static void AddAzureLogins(string OctopusUrl, string ApiKey, string Path = "", string OctopusUserName = "", string AzureEmailAddress = "", string AzureDisplayName = "", bool UpdateOctopusEmail = false, bool UpdateOctopusDisplayName = false, bool Force = false, bool WhatIf = false)
{
    // Display passed in information
    Console.WriteLine(string.Format("OctopusURL: {0}", OctopusUrl));
    Console.WriteLine("OctopusAPIKey: ****");
    Console.WriteLine(string.Format("OctopusUsername: {0}", OctopusUserName));
    Console.WriteLine(string.Format("AzureEmailAddress: {0}", AzureEmailAddress));
    Console.WriteLine(string.Format("AzureDisplayName: {0}", AzureDisplayName));
    Console.WriteLine(string.Format("UpdateOctopusEmailAddress: {0}", UpdateOctopusEmail.ToString()));
    Console.WriteLine(string.Format("UpdateOctopusDisplayName: {0}", UpdateOctopusDisplayName.ToString()));
    Console.WriteLine(string.Format("Force: {0}", Force.ToString()));
    Console.WriteLine(string.Format("WhatIf: {0}", WhatIf.ToString()));

    // Check to see url is empty
    if (!string.IsNullOrWhiteSpace(OctopusUrl))
    {
        // Remove trailing /
        OctopusUrl = OctopusUrl.TrimEnd('/');
    }

    // Create Octopus.Client objects
    var endpoint = new Octopus.Client.OctopusServerEndpoint(OctopusUrl, ApiKey);
    var repository = new Octopus.Client.OctopusRepository(endpoint);
    var client = new Octopus.Client.OctopusClient(endpoint);

    // Declare collection of users to update
    var usersToUpdate = new System.Collections.Generic.List<UserToUpdate>();

    // Test to see if path was provided
    if (string.IsNullOrWhiteSpace(Path))
    {
        if (!string.IsNullOrWhiteSpace(OctopusUserName) || !string.IsNullOrWhiteSpace(AzureEmailAddress))
        {
            // Create new user to update object
            var userToUpdate = new UserToUpdate();
            userToUpdate.AzureDisplayName = AzureDisplayName;
            userToUpdate.AzureEmailAddress = AzureEmailAddress;
            userToUpdate.OctopusUserName = OctopusUserName;

            // Add to collection
            usersToUpdate.Add(userToUpdate);
        }
    }
    else
    {
        // Read from csv
        using (var reader = new System.IO.StreamReader(Path))
        {
            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine();
                var columns = line.Split(',');

                // Create new user to update object
                var userToUpdate = new UserToUpdate();
                userToUpdate.AzureDisplayName = columns[0];
                userToUpdate.AzureEmailAddress = columns[1];
                userToUpdate.OctopusUserName = columns[2];

                // Add to collection
                usersToUpdate.Add(userToUpdate);
            }
        }
    }

    // Check to see if we have anything to update
    if (usersToUpdate.Count > 0)
    {
        Console.WriteLine(string.Format("Users to update: {0}", usersToUpdate.Count));

        // Loop through collection
        foreach (var userToUpdate in usersToUpdate)
        {
            Console.WriteLine(string.Format("Searching for user {0}", userToUpdate.OctopusUserName));
            var existingOctopusUser = client.Repository.Users.FindByUsername(userToUpdate.OctopusUserName);

            // Check to see if something was returned
            if (null != existingOctopusUser)
            {
                // Check to see if it is a service account
                if (existingOctopusUser.IsService)
                {
                    Console.WriteLine(string.Format("{0} is a service account, skipping ...", userToUpdate.OctopusUserName));
                    continue;
                }

                // Check to see if user is active
                if (!existingOctopusUser.IsActive)
                {
                    Console.WriteLine(string.Format("{0} is not an active account, skipping ...", userToUpdate.OctopusUserName));
                }

                // Get existing Microsoft Entra ID identity, if exists
                var azureAdIdentity = existingOctopusUser.Identities.FirstOrDefault(i => i.IdentityProviderName == "Azure AD");

                // Check to see if something was returned
                if(null != azureAdIdentity)
                {
                    // Check to see if force update was set
                    if (Force)
                    {
                        Console.WriteLine(string.Format("Force set to true, replacing existing entries for {0}", userToUpdate.OctopusUserName));
                        azureAdIdentity.Claims["email"].Value = userToUpdate.AzureEmailAddress;
                        azureAdIdentity.Claims["dn"].Value = userToUpdate.AzureDisplayName;
                    }
                }
                else
                {
                    Console.WriteLine(string.Format("No existing AzureAD login found for user {0}", userToUpdate.OctopusUserName));

                    // Create new octopus objects
                    var newAzureIdentity = new Octopus.Client.Model.IdentityResource();
                    newAzureIdentity.IdentityProviderName = "Azure AD";

                    var newEmailClaim = new Octopus.Client.Model.IdentityClaimResource();
                    newEmailClaim.IsIdentifyingClaim = true;
                    newEmailClaim.Value = userToUpdate.AzureEmailAddress;

                    newAzureIdentity.Claims.Add("email", newEmailClaim);

                    var newDisplayNameClaim = new Octopus.Client.Model.IdentityClaimResource();
                    newDisplayNameClaim.IsIdentifyingClaim = false;
                    newDisplayNameClaim.Value = userToUpdate.AzureDisplayName;

                    newAzureIdentity.Claims.Add("dn", newDisplayNameClaim);

                    // Add identity object to user
                    var identityCollection = new System.Collections.Generic.List<Octopus.Client.Model.IdentityResource>(existingOctopusUser.Identities);
                    identityCollection.Add(newAzureIdentity);
                    existingOctopusUser.Identities = identityCollection.ToArray();
                }

                if (UpdateOctopusDisplayName && !string.IsNullOrWhiteSpace(userToUpdate.AzureDisplayName))
                {
                    Console.WriteLine(string.Format("Setting Octopus Display Name to: {0}", userToUpdate.AzureDisplayName));
                    existingOctopusUser.DisplayName = userToUpdate.AzureDisplayName;
                }

                if (UpdateOctopusEmail && !string.IsNullOrWhiteSpace(userToUpdate.AzureEmailAddress))
                {
                    Console.WriteLine(string.Format("Setting Octopus Email Address to: {0}", userToUpdate.AzureEmailAddress));
                    existingOctopusUser.EmailAddress = userToUpdate.AzureEmailAddress;
                }

                if (WhatIf)
                {
                    Console.WriteLine(string.Format("WhatIf is set to true, skipping update of user: {0}", userToUpdate.OctopusUserName));
                    Console.WriteLine(existingOctopusUser);
                }
                else
                {
                    // Update account
                    Console.WriteLine(string.Format("Updating: {0}", userToUpdate.OctopusUserName));
                    client.Repository.Users.Modify(existingOctopusUser);
                }
            }
        }
    }
}
Python3
import json
import requests
import csv

# Define class
class userToUpdate:
    OctopusUsername = ''
    AzureEmailAddress = ''
    AzureDisplayName = ''

# Define Octopus server variables
octopus_server_uri = 'https://your-octopus-url'
octopus_api_key = 'API-YOUR-KEY'


# Create function
def AddAzureLogins(OctopusUrl, 
OctopusAPIKey, Path='', 
OctopusUsername='', 
AzureEmailAddress='', 
AzureDisplayName='',
UpdateOctopusEmailAddress=False, 
UpdateOctopusDisplayName=False, 
Force=False, 
WhatIf=False):
    # Display values passed into function
    print ("OctopusURL: ", OctopusUrl)
    print ("OctopusAPIKey: ", "*******")
    print ("Path: ", Path)
    print ("OctopusUsername: ", OctopusUsername)
    print ("AzureEmailAddress: ", AzureEmailAddress)
    print ("AzureDisplayName: ", AzureDisplayName)
    print ("UpdateOctopusEmailAddress", UpdateOctopusEmailAddress)
    print ("UpdateOctopusDisplayName: ", UpdateOctopusDisplayName)  

    headers = {'X-Octopus-ApiKey': OctopusAPIKey}
    usersToUpdate = []

    if Path:
        # Write something to do extraction
        with open(Path) as csv_file:
            csv_reader = csv.reader(csv_file, delimiter=',')
            for row in csv_reader:
                updateUser = userToUpdate()
                updateUser.AzureDisplayName = row[0]
                updateUser.AzureEmailAddress = row[1]
                updateUser.OctopusUsername = row[3]

                usersToUpdate.append(updateUser)
    else:
        updateUser = userToUpdate()
        updateUser.AzureDisplayName = AzureDisplayName
        updateUser.AzureEmailAddress = AzureEmailAddress
        updateUser.OctopusUsername = OctopusUsername

        usersToUpdate.append(updateUser)

    # Gather users from instance
    existingUsers = []
    uri = '{0}/users'.format(OctopusUrl)
    response = requests.get(uri, headers=headers)
    response.raise_for_status()

    # Decode content
    results = json.loads(response.content.decode('utf-8'))
    existingUsers += results["Items"]

    # Loop through remaining results
    while ("Page.Next" in results["Links"]):
            response = requests.get(uri, headers=headers)
            response.raise_for_status()

            # Decode content
            results = json.loads(response.content.decode('utf-8'))
            existingUsers += results["Items"]
    for user in usersToUpdate:
        # Search for user
        existingUser = next((u for u in existingUsers if u["Username"] == user.OctopusUsername), None)
        
        if (existingUser != None):
            print(existingUser)
            # Check to see if user is a service account
            if (existingUser["IsService"] == True):
                print (f"User {user.OctopusUsername} is a service account, skipping ...")
                continue

            if (existingUser["IsActive"] == False):
                print (f"User {user.OctopusUsername} is inactive, skipping...")
                continue

            if (existingUser["Identities"] != None):
                azureAdIdentity = next((u for u in existingUser["Identities"] if u["IdentityProviderName"] == "Azure AD"), None)
                
                if (azureAdIdentity != None):
                    print (f"Found existing Microsoft Entra ID identity for {user.OctopusUsername} ...")
                    
                    if(Force):
                        print("Force is set to true, overwriting values")
                        azureAdIdentity["Claims"]["email"]["Value"] = user.AzureEmailAddress
                        azureAdIdentity["Claims"]["dn"]["Value"] = user.AzureDisplayName
                    else:
                        print("Force is set to false, skipping...")
                        continue
                else:
                    # Create new Identity
                    newIdentity = {
                        'IdentityProviderName': 'Azure AD',
                        'Claims': {
                            'email': {
                                'Value': user.AzureEmailAddress,
                                'IsIdentifyingClaim': True
                            },
                            'dn':{
                                'Value': user.AzureDisplayName,
                                'IsIdentifyingClaim': False
                            }
                        }
                    }
                    existingUser["Identities"].append(newIdentity)

            if (UpdateOctopusEmailAddress):
                existingUser["EmailAddress"] = user.AzureEmailAddress

            if (UpdateOctopusDisplayName):
                existingUser["DisplayName"] = user.AzureDisplayName

            # Update the user account
            uri = '{0}/users/{1}'.format(OctopusUrl, existingUser['Id'])
            response = requests.put(uri, headers=headers, json=existingUser)
            response.raise_for_status()
    return


AddAzureLogins(octopus_server_uri, octopus_api_key, OctopusUsername='some.email@microsoft.com', AzureDisplayName='DisplayName', AzureEmailAddress='some.email@microsoft.com', Force=True )
Go
package main

import (
	"fmt"
	"log"
	"os"

	"net/url"

	"github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy"

	"encoding/csv"
	"io"
)

type User struct {
	OctopusUsername   string
	AzureEmailAddress string
	AzureDisplayName  string
}

func main() {

	apiURL, err := url.Parse("https://your-octopus-url")
	if err != nil {
		log.Println(err)
	}
	APIKey := "API-YOUR-KEY"

	Path := ""
	Users := []User{}
	OctopusUsername := ""
	AzureEmailAddress := ""
	AzureDisplayName := ""
	OverwriteEmailAddress := false
	OverwriteDisplayName := false

	if Path != "" {
		Users = GetCSVData(Path)
	} else {
		u := User{OctopusUsername: OctopusUsername, AzureEmailAddress: AzureEmailAddress, AzureDisplayName: AzureDisplayName}
		Users = append(Users, u)
	}

	for i := 0; i < len(Users); i++ {
		// Get existing user account
		existingUser := GetUser(apiURL, APIKey, Users[i].OctopusUsername)

		// Check to see if something was returned
		if existingUser != nil {
			fmt.Println("Found " + existingUser.Username)

			// Check to see if it has an identity
			if existingUser.Identities != nil {
				identityIndex := -1
				// Loop through Identities collection
				for j := 0; j < len(existingUser.Identities); j++ {
					if existingUser.Identities[i].IdentityProviderName == "Azure AD" {
						fmt.Println("User has existing Microsoft Entra ID identity")
						identityIndex = j
						break
					}
				}

				if identityIndex > -1 {
					if OverwriteDisplayName {
						existingUser.DisplayName = Users[i].AzureDisplayName
					}

					if OverwriteEmailAddress {
						existingUser.EmailAddress = Users[i].AzureEmailAddress
					}

				} else {
					// Create new identity object
					claimsCollection := make(map[string]octopusdeploy.IdentityClaim)
					emailClaim := octopusdeploy.IdentityClaim{Value: Users[i].AzureEmailAddress, IsIdentifyingClaim: true}
					displayNameClaim := octopusdeploy.IdentityClaim{Value: Users[i].AzureDisplayName, IsIdentifyingClaim: false}
					claimsCollection["email"] = emailClaim
					claimsCollection["dn"] = displayNameClaim
					octopusIdentity := octopusdeploy.Identity{IdentityProviderName: "Azure AD", Claims: claimsCollection}

					// Add new identity
					existingUser.Identities = append(existingUser.Identities, octopusIdentity)
				}

				// Update user account
				client := octopusAuth(apiURL, APIKey, "")
				existingUser, err = client.Users.Update(existingUser)

				if err != nil {
					log.Println(err)
				}
			}
		}
	}
}

func octopusAuth(octopusURL *url.URL, APIKey, space string) *octopusdeploy.Client {
	client, err := octopusdeploy.NewClient(nil, octopusURL, APIKey, space)
	if err != nil {
		log.Println(err)
	}

	return client
}

func GetUser(octopusURL *url.URL, APIKey string, OctopusUserName string) *octopusdeploy.User {
	// Get client
	client := octopusAuth(octopusURL, APIKey, "")

	// Get user account
	userQuery := octopusdeploy.UsersQuery{
		Filter: OctopusUserName,
	}

	userAccounts, err := client.Users.Get(userQuery)

	if err != nil {
		log.Println(err)
	}

	for i := 0; i < len(userAccounts.Items); i++ {
		// Check to see if it's a match
		if userAccounts.Items[i].Username == OctopusUserName {
			return userAccounts.Items[i]
		}
	}

	return nil
}

func GetCSVData(Path string) []User {
	recordFile, err := os.Open(Path)

	if err != nil {
		log.Println(err)
	}

	Users := []User{}

	reader := csv.NewReader(recordFile)
	reader.Comma = ','

	for i := 0; ; i++ {
		record, err := reader.Read()

		if err == io.EOF {
			break
		}

		userAccount := User{OctopusUsername: record[0], AzureEmailAddress: record[1], AzureDisplayName: record[2]}

		Users = append(Users, userAccount)
	}

	return Users
}

Help us continuously improve

Please let us know if you have any feedback about this page.

Send feedback

Page updated on Tuesday, June 25, 2024