Swap AD group with LDAP group

This script demonstrates how to programmatically swap any Active Directory external group for a matching LDAP external group in each Octopus team. This can be useful when you are migrating from the Active Directory authentication provider to the LDAP provider.

We also have a script that will swap Active Directory login records with matching LDAP ones for Octopus users.

Note: Please note there are some things to consider before using this script:

  • Both the Active Directory and LDAP providers must be enabled for this script to work as it queries both providers.
  • Always ensure you test the script on a non-production server first, and have a production database backup.

Usage

Provide values for:

  • Octopus URL
  • Octopus API Key
  • Name of the Active Directory domain to use to look up the groups to swap
  • WhatIf - A boolean value to toggle whether or not to perform the actual updates to teams in Octopus.
  • Remove old teams - A boolean value to toggle whether or not to remove the existing Active Directory groups from each team.

Script

PowerShell (REST API)
$ErrorActionPreference = "Stop"

$octopusURL = "https://your-octopus-url" # Replace with your instance URL
$octopusAPIKey = "API-YOUR-KEY" # Replace with a service account API Key
$header = @{ "X-Octopus-ApiKey" = $octopusAPIKey }

# Script options

# Provide the domain. This is needed to look up the group to ensure it's a valid AD Group we're working on.
$AD_Domain = "YOUR_DOMAIN"
# Set this to $False if you want the Script to perform the update on Octopus Teams.
$WhatIf = $True
# Set this to $True if you want the Script to remove old Active Directory teams once the LDAP group has been found and added.
$RemoveOldTeams = $False

# Limit how may teams are retrieved/updated. 
# Use these two variables to work through if you have hundreds of teams.
$skipIndex = 0
$recordsToBringBack = 30

# Get teams
Write-Host "Pulling teams starting at index $skipIndex and getting a max of $recordsToBringBack records back"
$teamList = Invoke-RestMethod -Method GET -Uri "$OctopusUrl/api/teams?skip=$skipIndex&take=$recordsToBringBack" -Headers $header
$teams = $teamList.Items

$ldapRecordsToAdd = @()
$activeDirectoryRecordsToRemove = @()
$recordsUpdated = 0

foreach ($team in $teams) {
    try {
        Write-Host "Working on team: '$($team.Name)'$(if (![string]::IsNullOrWhiteSpace($team.SpaceId)) {" from Space '$($team.SpaceId)'"})" 
    
        $teamExternalGroups = $team.ExternalSecurityGroups

        if ($teamExternalGroups.Count -eq 0) {
            Write-Verbose "Team: '$($team.Name)' doesn't have any external groups, skipping"
            continue 
        }
        else {
            foreach ($externalSecurityGroup in $team.ExternalSecurityGroups) {
                $externalName = $externalSecurityGroup.DisplayName            
                if ($null -eq $externalName) {
                    continue
                }
                else {
                    # Check if this external group is an AD group
                    $ad_TeamNameToFind = "$AD_Domain\$externalName"
                    $directoryServicesResults = Invoke-RestMethod -Method GET -Uri "$octopusURL/api/externalgroups/directoryServices?partialName=$([System.Web.HTTPUtility]::UrlEncode($ad_TeamNameToFind))" -Headers $header
                    $matchFound = $False
                    foreach ($adResult in $directoryServicesResults) {
                        if ($adResult.DisplayName -eq $externalName -and $adResult.Id -eq $externalSecurityGroup.Id) {
                            Write-Host "Found a matching team name in AD for '$($team.Name)' that matches the SID $($externalSecurityGroup.Id)." -ForegroundColor Green
                            $matchFound = $true
                            break;
                        }
                    }

                    # Next, check to see if to find a matching group in LDAP
                    if ($matchFound -eq $True) {
                        $ldapTeamNameToFind = "$externalName"
                
                        $ldapResults = Invoke-RestMethod -Method GET -Uri "$octopusURL/api/externalgroups/ldap?partialName=$([System.Web.HTTPUtility]::UrlEncode($ldapTeamNameToFind))" -Headers $header
                        foreach ($ldapResult in $ldapResults) {
                            if ($ldapResult.DisplayName -eq $externalName) {
                                Write-Host "Found a matching team name in LDAP for '$($team.Name)'." -ForegroundColor Green
                                $ldapMatchFound = $true
                                break;
                            }
                        }
                        $foundExistingMatch = $False
                        if ($ldapMatchFound -eq $True) {
                            # Does the Octopus team already have this LDAP Group?
                            foreach ($group in $team.ExternalSecurityGroups) {                        
                                if ($group.Id -eq $ldapResult.Id) {
                                    $foundExistingMatch = $true
                                    break
                                }
                            }

                            if ($foundExistingMatch -eq $false) {
                                $ldapRecordsToAdd += $ldapResult
                            }
                            else {
                                Write-Host "The LDAP group already existed on team '$($team.Name)'."
                            }
                            
                            if ($RemoveOldTeams -eq $True) {
                                Write-Host "Existing AD Group with SID $($externalSecurityGroup.Id) in team '$($team.Name)' will be marked to be removed"
                                $activeDirectoryRecordsToRemove += $adResult.Id
                            }
                        }
                    }
                }
            }

            if ($ldapRecordsToAdd.Length -gt 0) {
                foreach ($teamToAdd in $ldapRecordsToAdd) {
                    $team.ExternalSecurityGroups += $teamToAdd
                }
            }

            if ($RemoveOldTeams -eq $True -and $activeDirectoryRecordsToRemove.Length -gt 0) {
                $externalGroups = @()
                foreach ($group in $team.ExternalSecurityGroups) {
                    if ($activeDirectoryRecordsToRemove -contains $group.Id) {
                        Write-Verbose "Removing AD group with SID $($group.Id)"
                        continue
                    }
                    else {
                        $externalGroups += $group
                    }
                }
                Write-Host "Filtered external groups from $($team.ExternalSecurityGroups.Length) to $($externalGroups.Length)"
                $team.ExternalSecurityGroups = $externalGroups
            }
             
            if ($ldapRecordsToAdd.Length -gt 0 -or ($RemoveOldTeams -eq $True -and $activeDirectoryRecordsToRemove.Length -gt 0)) {
                $TeamUpdateUri = "$OctopusUrl/api/teams/$($team.Id)"
                $TeamBody = $($team | ConvertTo-Json -Depth 10 -Compress)

                if ($WhatIf -eq $True) {
                    Write-Host "WhatIf = True. Update for team '$($Team.Name)' would have been:" 
                    Write-Host "$($TeamBody)"
                }
                else {
                    Write-Host "Updating team '$($Team.Name)' in Octopus Deploy"
                    Invoke-RestMethod -Method PUT -Uri $TeamUpdateUri -Headers $header -Body $teamBody | Out-Null
                }
                
                $recordsUpdated += 1
            }
        }
    }
    catch {
        Write-Error "An error occurred with Team: $($team.Name) - $($_.Exception.ToString())"
    }
}

Write-Host "Updated $recordsUpdated team(s)."

Help us continuously improve

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

Send feedback

Page updated on Sunday, January 1, 2023

Use Octopus docs with AI