Documentation menu

Users and Teams

Use Octopus docs with AI

List users

An example script to list all users in an Octopus instance.

This script will list all active users in an Octopus instance. In addition, there are a number of optional items you can include:

Usage

Provide values for:

  • Octopus URL
  • Octopus API Key
  • (Optional) whether or not to include user role details
  • (Optional) whether or not to include Active Directory details
  • (Optional) whether or not to include Azure Active Directory details
  • (Optional) whether or not to include disabled users
  • (Optional) path to export the results to a csv file

Script

PowerShell (REST API)
$ErrorActionPreference = "Stop";

# Define working variables
$octopusURL = "https://your-octopus-url"
$octopusAPIKey = "API-YOUR-KEY"
$header = @{ "X-Octopus-ApiKey" = $octopusAPIKey }

# Optional: include user role details?
$includeUserRoles = $False

# Optional: include non-active users in output
$includeNonActiveUsers = $False

# Optional: include AD details
$includeActiveDirectoryDetails = $False

# Optional: include AAD details
$includeAzureActiveDirectoryDetails = $False

# Optional: set a path to export to csv
$csvExportPath = ""

$users = @()
$usersList = @()
$response = $null
do {
    $uri = if ($response) { $octopusURL + $response.Links.'Page.Next' } else { "$octopusURL/api/users" }
    $response = Invoke-RestMethod -Method Get -Uri $uri -Headers $header
    $usersList += $response.Items
} while ($response.Links.'Page.Next')

# Filter non-active users
if($includeNonActiveUsers -eq $False) {
    Write-Host "Filtering users who aren't active from results"
    $usersList = $usersList | Where-Object {$_.IsActive -eq $True}
}

# If we are including user roles, need to get team details
if($includeUserRoles -eq $True) {
    $teams = @()
    $response = $null
    do {
        $uri = if ($response) { $octopusURL + $response.Links.'Page.Next' } else { "$octopusURL/api/teams" }
        $response = Invoke-RestMethod -Method Get -Uri $uri -Headers $header
        $teams += $response.Items
    } while ($response.Links.'Page.Next')

    foreach($team in $teams) {
        $scopedUserRoles = Invoke-RestMethod -Method Get -Uri ("$octopusURL/api/teams/$($team.Id)/scopeduserroles") -Headers $header
        $team | Add-Member -MemberType NoteProperty -Name "ScopedUserRoles" -Value $scopedUserRoles.Items
    }

    $allUserRoles = @()
    $response = $null
    do {
        $uri = if ($response) { $octopusURL + $response.Links.'Page.Next' } else { "$octopusURL/api/userroles" }
        $response = Invoke-RestMethod -Method Get -Uri $uri -Headers $header
        $allUserRoles += $response.Items
    } while ($response.Links.'Page.Next')

    $spaces = @()
    $response = $null
    do {
        $uri = if ($response) { $octopusURL + $response.Links.'Page.Next' } else { "$octopusURL/api/spaces" }
        $response = Invoke-RestMethod -Method Get -Uri $uri -Headers $header
        $spaces += $response.Items
    } while ($response.Links.'Page.Next')
}

foreach($userRecord in $usersList) {
    $usersRoles = @()

    $user = [PSCustomObject]@{
        Id = $userRecord.Id
        Username = $userRecord.Username
        DisplayName = $userRecord.DisplayName
        IsActive = $userRecord.IsActive
        IsService = $userRecord.IsService
        EmailAddress = $userRecord.EmailAddress
    }
    if($includeActiveDirectoryDetails -eq $True) 
    {
        $user | Add-Member -MemberType NoteProperty -Name "AD_Upn" -Value $null
        $user | Add-Member -MemberType NoteProperty -Name "AD_Sam" -Value $null
        $user | Add-Member -MemberType NoteProperty -Name "AD_Email" -Value $null
    }
    if($includeAzureActiveDirectoryDetails -eq $True) 
    {
        $user | Add-Member -MemberType NoteProperty -Name "AAD_DN" -Value $null
        $user | Add-Member -MemberType NoteProperty -Name "AAD_Email" -Value $null
    }

    if($includeUserRoles -eq $True) {
        $usersTeams = $teams | Where-Object {$_.MemberUserIds -icontains $user.Id}
        foreach($userTeam in $usersTeams) {
            $roles = $userTeam.ScopedUserRoles
            foreach($role in $roles) {
                $userRole = $allUserRoles | Where-Object {$_.Id -eq $role.UserRoleId} | Select-Object -First 1
                $roleName = "$($userRole.Name)"
                $roleSpace = $spaces | Where-Object {$_.Id -eq $role.SpaceId} | Select-Object -First 1
                if (![string]::IsNullOrWhiteSpace($roleSpace)) {
                    $roleName += " ($($roleSpace.Name))"
                }
                $usersRoles+= $roleName
            }
        }
        $user | Add-Member -MemberType NoteProperty -Name "ScopedUserRoles" -Value ($usersRoles -Join "|")
    }

    if($userRecord.Identities.Count -gt 0) {
        if($includeActiveDirectoryDetails -eq $True) 
        {
            $activeDirectoryIdentity = $userRecord.Identities | Where-Object {$_.IdentityProviderName -eq "Active Directory"} | Select-Object -ExpandProperty Claims
            if($null -ne $activeDirectoryIdentity) {               
                $user.AD_Upn = (($activeDirectoryIdentity | ForEach-Object {"$($_.upn.Value)"}) -Join "|")
                $user.AD_Sam = (($activeDirectoryIdentity | ForEach-Object {"$($_.sam.Value)"}) -Join "|")
                $user.AD_Email = (($activeDirectoryIdentity | ForEach-Object {"$($_.email.Value)"}) -Join "|")
            }
        }
        if($includeAzureActiveDirectoryDetails -eq $True) 
        {
            $azureAdIdentity = $userRecord.Identities | Where-Object {$_.IdentityProviderName -eq "Azure AD"} | Select-Object -ExpandProperty Claims
            if($null -ne $azureAdIdentity) {
                $user.AAD_Dn = (($azureAdIdentity | ForEach-Object {"$($_.dn.Value)"}) -Join "|")
                $user.AAD_Email = (($azureAdIdentity | ForEach-Object {"$($_.email.Value)"}) -Join "|")
            }
        }
    }
    $users+=$user
}

if (![string]::IsNullOrWhiteSpace($csvExportPath)) {
    Write-Host "Exporting results to CSV file: $csvExportPath"
    $users | Export-Csv -Path $csvExportPath -NoTypeInformation
}

$users | Format-Table
PowerShell (Octopus.Client)
$ErrorActionPreference = "Stop";

# Load assembly
Add-Type -Path 'path:\to\Octopus.Client.dll'
# Define working variables
$octopusURL = "https://your-octopus-url"
$octopusAPIKey = "API-YOUR-KEY"

# Optional: include user role details?
$includeUserRoles = $true

# Optional: include non-active users in output
$includeNonActiveUsers = $False

# Optional: include AD details
$includeActiveDirectoryDetails = $False

# Optional: include AAD details
$includeAzureActiveDirectoryDetails = $True

# Optional: set a path to export to csv
$csvExportPath = "path:\to\users.csv"

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

# Get users
$users = $repository.Users.GetAll()
$usersList = @()

# Check to see if we're filtering out inactive
if ($includeNonActiveUsers -eq $true)
{
    # Filter out inactive users
    Write-Host "Filtering users who aren't active from results"
    $users = $users | Where-Object {$_.IsActive -eq $True}
}


# Loop through users
foreach ($user in $users)
{
    # Populate user details
    $userDetails = [ordered]@{
        Id = $user.Id
        Username = $user.Username
        DisplayName = $user.DisplayName
        IsActive = $user.IsActive
        IsService = $user.IsService
        EmailAddress = $user.EmailAddress
    }


    # Check to see if we're including user roles
    if ($includeUserRoles -eq $true)
    {
        $userDetails.Add("ScopedUserRoles", "")
        # Get users teams
        $userTeamNames = $repository.UserTeams.Get($user)

        # Loop through the users teams
        foreach ($teamName in $userTeamNames)
        {
            # Get the team
            $team = $repository.Teams.Get($team.Id)
            
            foreach ($role in $repository.Teams.GetScopedUserRoles($team))
            {
                $userDetails["ScopedUserRoles"] += "$(($repository.UserRoles.Get($role.UserRoleId).Name)) ($(($repository.Spaces.Get($role.SpaceId)).Name))|"
            }
        }
    }

    if ($includeActiveDirectoryDetails -eq $true)
    {
        # Get the identity provider object
        $activeDirectoryIdentity = $user.Identities | Where-Object {$_.IdentityProviderName -eq "Active Directory"}
        if ($null -ne $activeDirectoryIdentity) 
        {
            $userDetails.Add("AD_Upn", (($activeDirectoryIdentity.Claims | ForEach-Object {"$($_.upn.Value)"}) -Join "|"))
            $userDetails.Add("AD_Sam", (($activeDirectoryIdentity.Claims | ForEach-Object {"$($_.sam.Value)"}) -Join "|"))
            $userDetails.Add("AD_Email", (($activeDirectoryIdentity.Claims | ForEach-Object {"$($_.email.Value)"}) -Join "|"))
        }
    }
    
    if ($includeAzureActiveDirectoryDetails -eq $true)
    {
        $azureAdIdentity = $user.Identities | Where-Object {$_.IdentityProviderName -eq "Azure AD"}
        if ($null -ne $azureAdIdentity)
        {
            $userDetails.Add("AAD_Dn", (($azureAdIdentity.Claims | ForEach-Object {"$($_.dn.Value)"}) -Join "|"))
            $userDetails.Add("AAD_Email", (($azureAdIdentity.Claims | ForEach-Object {"$($_.email.Value)"}) -Join "|"))
        }
    }

    
    $usersList += $userDetails    
}

# Write header
$header = $usersList.Keys | Select-Object -Unique
Set-Content -Path $csvExportPath -Value ($header -join ",")

foreach ($user in $usersList)
{
    Add-Content -Path $csvExportPath -Value ($user.Values -join ",")
}

$usersList | Format-Table
C#

#r "nuget: Octopus.Client"

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

class UserDetails
{
    // Define private variables

    public string Id
    {
        get;
        set;
    }

    public string Username
    {
        get; set;
    }

    public string DisplayName
    {
        get; set;
    }

    public bool IsActive
    {
        get; set;
    }

    public bool IsService
    {
        get; set;
    }

    public string EmailAddress
    {
        get;
        set;
    }

    public string ScopedUserRoles
    {
        get;set;
    }

    public string AD_Upn
    {
        get;
        set;
    }

    public string AD_Sam
    {
        get;
        set;
    }

    public string AD_Email
    {
        get;
        set;
    }

    public string AAD_Dn
    {
        get;
        set;
    }

    public string AAD_Email
    {
        get;
        set;
    }
}

// If using .net Core, be sure to add the NuGet package of System.Security.Permissions

var octopusURL = "https://your-octopus-url";
var octopusAPIKey = "API-YOUR-KEY";
string csvExportPath = "path:\\to\\users.csv";
bool includeUserRoles = true;
bool includeActiveDirectoryDetails = false;
bool includeAzureActiveDirectoryDetails = true;
bool includeInactiveUsers = false;

System.Collections.Generic.List<UserDetails> usersList = new System.Collections.Generic.List<UserDetails>();

// Create repository object
var endpoint = new OctopusServerEndpoint(octopusURL, octopusAPIKey);
var repository = new OctopusRepository(endpoint);
var client = new OctopusClient(endpoint);

// Get all users
var users = repository.Users.FindAll();

// Loop through users
if (!includeInactiveUsers)
    users = users.Where(u => u.IsActive == true).ToList();
foreach (var user in users)
{
    // Get basic details
    UserDetails userDetails = new UserDetails();
    userDetails.Id = user.Id;
    userDetails.Username = user.Username;
    userDetails.DisplayName = user.DisplayName;
    userDetails.IsActive = user.IsActive;
    userDetails.IsService = user.IsService;
    userDetails.EmailAddress = user.EmailAddress;

    // Check to see if userroles are included
    if (includeUserRoles)
    {
        var userTeamNames = repository.UserTeams.Get(user);

        foreach (var teamName in userTeamNames)
        {
            var team = repository.Teams.Get(teamName.Id);
            
            foreach (var role in repository.Teams.GetScopedUserRoles(team))
            {
                userDetails.ScopedUserRoles += string.Format("{0} ({1})|", (repository.UserRoles.Get(role.UserRoleId)).Name, (repository.Spaces.Get(role.SpaceId)));
            }
        }
    }

    if(includeActiveDirectoryDetails)
    {
        var activeDirectoryDetails = user.Identities.FirstOrDefault(i => i.IdentityProviderName == "Active Directory");
        if (null != activeDirectoryDetails)
        {
            userDetails.AD_Upn = activeDirectoryDetails.Claims["upn"].Value;
            userDetails.AD_Sam = activeDirectoryDetails.Claims["sam"].Value;
            userDetails.AD_Email = activeDirectoryDetails.Claims["email"].Value;
        }
    }

    if (includeAzureActiveDirectoryDetails)
    {
        var azureActiveDirectoryDetails = user.Identities.FirstOrDefault(i => i.IdentityProviderName == "Azure AD");
        if (null != azureActiveDirectoryDetails)
        {
            userDetails.AAD_Dn = azureActiveDirectoryDetails.Claims["dn"].Value;
            userDetails.AAD_Email = azureActiveDirectoryDetails.Claims["email"].Value;
        }
    }

    usersList.Add(userDetails);
}

Console.WriteLine(string.Format("Found {0} results", usersList.Count.ToString()));

if (usersList.Count > 0)
{
    foreach (var result in usersList)
    {
        System.Collections.Generic.List<string> row = new System.Collections.Generic.List<string>();
        System.Collections.Generic.List<string> header = new System.Collections.Generic.List<string>();
        
        var isFirstRow = variableTracking.IndexOf(result) == 0;
        var properties = result.GetType().GetProperties();

        foreach (var property in properties)
        {
            Console.WriteLine(string.Format("{0}: {1}", property.Name, property.GetValue(result)));
            if (isFirstRow)
            {
                header.Add(property.Name);
            }
            
            row.Add((property.GetValue(result) == null ? string.Empty : property.GetValue(result).ToString()));
        }

        if (!string.IsNullOrWhiteSpace(csvExportPath))
        {
            using (System.IO.StreamWriter csvFile = new System.IO.StreamWriter(csvExportPath, true))
            {
                if (isFirstRow)
                {
                    // Write header
                    csvFile.WriteLine(string.Join(",", header.ToArray()));
                }
                csvFile.WriteLine(string.Join(",", row.ToArray()));
            }
        }
    }
}
Python3
import json
import requests
from requests.api import get, head
import csv

def get_octopus_resource(uri, headers, skip_count = 0):
    items = []
    skip_querystring = ""

    if '?' in uri:
        skip_querystring = '&skip='
    else:
        skip_querystring = '?skip='

    response = requests.get((uri + skip_querystring + str(skip_count)), headers=headers)
    response.raise_for_status()

    # Get results of API call
    results = json.loads(response.content.decode('utf-8'))

    # Store results
    if hasattr(results, 'keys') and 'Items' in results.keys():
        items += results['Items']

        # Check to see if there are more results
        if (len(results['Items']) > 0) and (len(results['Items']) == results['ItemsPerPage']):
            skip_count += results['ItemsPerPage']
            items += get_octopus_resource(uri, headers, skip_count)

    else:
        return results

    
    # return results
    return items

octopus_server_uri = 'https://your-octopus-url'
octopus_api_key = 'API-YOUR-KEY'
headers = {'X-Octopus-ApiKey': octopus_api_key}
include_user_roles = True
include_non_active_users = False
include_active_directory_details = False
include_azure_active_directory = True
csv_export_path = "path:\\to\\users.csv"

# Get users
uri = '{0}/api/users'.format(octopus_server_uri)
users = get_octopus_resource(uri, headers)
users_list = []

# Loop through users
for user in users:
    if include_non_active_users != True and user['IsActive'] == False:
        continue

    user_details = {
        'Id': user['Id'],
        'Username': user['Username'],
        'DisplayName': user['DisplayName'],
        'IsActive': user['IsActive'],
        'IsService': user['IsService'],
        'EmailAddress': user['EmailAddress']
    }

    if include_user_roles:
        # Get users teams
        uri = '{0}/api/users/{1}/teams'.format(octopus_server_uri, user['Id'])
        user_team_names = get_octopus_resource(uri, headers)

        # Loop through teams
        for team_name in user_team_names:
            uri = '{0}/api/teams/{1}'.format(octopus_server_uri, team_name['Id'])
            team = get_octopus_resource(uri, headers)

            # Get scoped user roles
            uri = '{0}/api/teams/{1}/ScopedUserRoles'.format(octopus_server_uri, team['Id'])
            scoped_user_roles = get_octopus_resource(uri, headers)

            user_details['ScopedUserRoles'] = ''
            
            # Loop through roles
            for role in scoped_user_roles:
                if role['SpaceId'] == None:
                    role['SpaceId'] = 'Spaces-1'
                uri = '{0}/api/spaces/{1}'.format(octopus_server_uri, role['SpaceId'])
                space = get_octopus_resource(uri, headers)
                uri = '{0}/api/userroles/{1}'.format(octopus_server_uri, role['UserRoleId'])
                user_role = get_octopus_resource(uri, headers)
                user_details['ScopedUserRoles'] += '{0} ({1})|'.format(user_role['Name'], space['Name'])

    if include_active_directory_details:
        active_directory_identity = next((x for x in user['Identities'] if x['IdentityProviderName'] == 'Active Directory'), None)
        if active_directory_identity != None:
            user_details['AD_Upn'] = active_directory_identity['Claims']['upn']['Value']
            user_details['AD_Sam'] = active_directory_identity['Claims']['sam']['Value']
            user_details['AD_Email'] = active_directory_identity['Claims']['sam']['Value']

    if include_azure_active_directory:
        azure_ad_identity = next((x for x in user['Identities'] if x['IdentityProviderName'] == 'Azure AD'), None)
        if azure_ad_identity != None:
            user_details['AAD_Dn'] = azure_ad_identity['Claims']['dn']['Value']
            user_details['AAD_Email'] = azure_ad_identity['Claims']['email']['Value']

    print(user_details)
    users_list.append(user_details)

    if csv_export_path:
        with open(csv_export_path, mode='w') as csv_file:
            fieldnames = ['Id', 'Username', 'DisplayName', 'IsActive', 'IsService', 'EmailAddress', 'ScopedUserRoles', 'AD_Upn', 'AD_Sam', 'AD_Email', 'AAD_Dn', 'AAD_Email']
            writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
            writer.writeheader()
            for user in users_list:
                writer.writerow(user)
Go
package main

import (
	"bufio"
	"fmt"
	"log"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"

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

type UserDetails struct {
	Id              string
	Username        string
	DisplayName     string
	IsActive        string
	IsService       string
	EmailAddress    string
	ScopedUserRoles string
	AD_Upn          string
	AD_Sam          string
	AD_Email        string
	AAD_Dn          string
	AAD_Email       string
}

func main() {

	apiURL, err := url.Parse("https://your-octopus-url")
	if err != nil {
		log.Println(err)
	}
	APIKey := "API-YOUR-KEY"
	csvExportPath := "path:\\to\\users.csv"
	includeUserRoles := true
	includeActiveDirectoryDetails := false
	includeAzureActiveDirectoryDetails := true
	includeInactiveUsers := false

	usersList := []UserDetails{}

	// Create client object
	client := octopusAuth(apiURL, APIKey, "")

	// Get all users
	users, err := client.Users.GetAll()
	if err != nil {
		log.Println(err)
	}

	// Loop through users
	for _, user := range users {
		if !includeInactiveUsers && !user.IsActive {
			continue
		}

		// record user information
		userDetails := UserDetails{}
		userDetails.Id = user.ID
		userDetails.Username = user.Username
		userDetails.DisplayName = user.DisplayName
		userDetails.IsActive = strconv.FormatBool(user.IsActive)
		userDetails.IsService = strconv.FormatBool(user.IsService)
		userDetails.EmailAddress = user.EmailAddress

		if includeUserRoles {
			userTeamNames, err := client.Users.GetTeams(user)
			if err != nil {
				log.Println(err)
			}

			for _, userTeamName := range *userTeamNames {
				team, err := client.Teams.GetByID(userTeamName.ID)
				if err != nil {
					log.Println(err)
				}

				roles, err := client.Teams.GetScopedUserRoles(*team, octopusdeploy.SkipTakeQuery{Skip: 0, Take: 1000})

				for _, role := range roles.Items {
					if role.SpaceID == "" {
						role.SpaceID = "Spaces-1"
					}
					space := GetSpace(apiURL, APIKey, role.SpaceID)
					userRole, err := client.UserRoles.GetByID(role.UserRoleID)
					if err != nil {
						log.Println(err)
					}
					userDetails.ScopedUserRoles += userRole.Name + " (" + space.Name + ")|"
				}
			}
		}

		for _, provider := range user.Identities {
			if provider.IdentityProviderName == "Active Directory" && includeActiveDirectoryDetails {
				userDetails.AD_Upn += provider.Claims["upn"].Value
				userDetails.AD_Sam += provider.Claims["sam"].Value
				userDetails.AD_Email += provider.Claims["email"].Value
			}
			if provider.IdentityProviderName == "Azure AD" && includeAzureActiveDirectoryDetails {
				userDetails.AAD_Dn += provider.Claims["dn"].Value
				userDetails.AAD_Email += provider.Claims["email"].Value
			}
		}

		usersList = append(usersList, userDetails)
	}

	if len(usersList) > 0 {
		fmt.Printf("Found %[1]s results \n", strconv.Itoa(len(usersList)))

		for i := 0; i < len(usersList); i++ {
			row := []string{}
			header := []string{}
			isFirstRow := false
			if i == 0 {
				isFirstRow = true
			}

			e := reflect.ValueOf(&usersList[i]).Elem()
			for j := 0; j < e.NumField(); j++ {
				if isFirstRow {
					header = append(header, e.Type().Field(j).Name)
				}
				row = append(row, e.Field(j).Interface().(string))
			}

			if csvExportPath != "" {
				file, err := os.OpenFile(csvExportPath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
				if err != nil {
					log.Println(err)
				}

				dataWriter := bufio.NewWriter(file)
				if isFirstRow {
					dataWriter.WriteString(strings.Join(header, ",") + "\n")
				}
				dataWriter.WriteString(strings.Join(row, ",") + "\n")
				dataWriter.Flush()
				file.Close()
			}

		}
	}
}

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 GetSpace(octopusURL *url.URL, APIKey string, spaceId string) *octopusdeploy.Space {
	client := octopusAuth(octopusURL, APIKey, "")

	// Get specific space object
	space, err := client.Spaces.GetByID(spaceId)

	if err != nil {
		log.Println(err)
	} else {
		fmt.Println("Retrieved space " + space.Name)
	}

	return space
}