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