Create a tag set

This script demonstrates how to programmatically create a tag set in Octopus Deploy.

From Octopus Cloud version 2025.4.3897, Type and Scopes parameters can be included to configure the type of and scoping of a tag set when created via the API. This functionality is behind the Extended Tag Sets feature toggle, to request this functionality early, please contact support.

The API will ignore the Type and Scopes parameters if the Extended Tag Sets feature toggle is disabled.

Usage

Provide values for:

  • Octopus URL
  • Octopus API Key
  • Name of the space to use
  • Name of the tag set to create
  • Type for the tag set (MultiSelect, SingleSelect, or FreeText)
  • Scopes for the tag set (Tenant, Environment, Project, or any combination)
  • Optional description for the tag set
  • Optional tags to add to the new tag set

Script

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

# Define working variables
$octopusURL = "https://your-octopus-url"
$octopusAPIKey = "API-YOUR-KEY"
$header = @{ "X-Octopus-ApiKey" = $octopusAPIKey }
$spaceName = "Default"
$tagsetName = "Upgrade Ring"
$tagsetDescription = "Describes which upgrade ring the tenant belongs to"
$tagsetType = "MultiSelect" # Options: MultiSelect, SingleSelect, FreeText
$tagsetScopes = @("Tenant") # Options: Tenant, Environment, Project (can specify multiple)

# Optional Tags to add in the format "Tag name", "Tag Color"
$optionalTags = @{}
$optionalTags.Add("Early Adopter", "#ECAD3F")
$optionalTags.Add("Stable", "#36A766")

# Get space
$space = (Invoke-RestMethod -Method Get -Uri "$octopusURL/api/spaces/all" -Headers $header) | Where-Object {$_.Name -eq $spaceName}

# See if tagset already exists
$tagsetResults = (Invoke-RestMethod -Method Get "$octopusURL/api/$($space.Id)/tagsets?partialName=$tagsetName" -Headers $header) 
if( $tagsetResults.TotalResults -gt 0) {
    throw "Existing tagset results found matching '$($tagsetName)'!"
}

$tags = @()
if($optionalTags.Count -gt 0)
{
    foreach ($tagName in $optionalTags.Keys) {
        $tag = @{
            Id = $null
            Name = $tagName
            Color = $optionalTags.Item($tagName)
            Description = ""
            CanonicalTagName = $null
        }
        $tags += $tag
    }
}
# Create tagset json payload
$jsonPayload = @{
    Name = $tagsetName
    Description = $tagsetDescription
    Type = $tagsetType
    Scopes = $tagsetScopes
    Tags = $tags
}

# Create tagset
Invoke-RestMethod -Method Post -Uri "$octopusURL/api/$($space.Id)/tagsets" -Body ($jsonPayload | ConvertTo-Json -Depth 10) -Headers $header
PowerShell (Octopus.Client)
# Load octopus.client assembly
Add-Type -Path "path\to\Octopus.Client.dll"

# Define working variables
$octopusURL = "https://your-octopus-url"
$octopusAPIKey = "API-YOUR-KEY"
$spaceName = "Default"
$tagsetName = "Upgrade Ring"
$tagsetDescription = "Describes which upgrade ring the tenant belongs to"
$tagsetType = "MultiSelect" # Options: MultiSelect, SingleSelect, FreeText
$tagsetScopes = @("Tenant") # Options: Tenant, Environment, Project (can specify multiple)

# Optional Tags to add in the format "Tag name", "Tag Color"
$optionalTags = @{}
$optionalTags.Add("Early Adopter", "#ECAD3F")
$optionalTags.Add("Stable", "#36A766")

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

try
{
    # Get space
    $space = $repository.Spaces.FindByName($spaceName)
    $repositoryForSpace = $client.ForSpace($space)

    # Create or modify tagset
    $tagsetEditor = $repositoryForSpace.TagSets.CreateOrModify($tagsetName, $tagsetDescription)
    
    # Add optional tags
    if($optionalTags.Count -gt 0)
    {
        foreach ($tagName in $optionalTags.Keys) {
            $tagsetEditor.AddOrUpdateTag($tagName, "", $optionalTags.Item($tagName))
        }
    }
    $tagsetEditor.Save()
}
catch
{
    Write-Host $_.Exception.Message
}
C#
// If using .net Core, be sure to add the NuGet package of System.Security.Permissions
#r "nuget: Octopus.Client"

using Octopus.Client;
using Octopus.Client.Model;

// Declare working variables
var octopusURL = "https://your-octopus-url";
var octopusAPIKey = "API-YOUR-KEY";
var spaceName = "Default";
var tagsetName = "Upgrade Ring";
var tagsetDescription = "Describes which upgrade ring the tenant belongs to";
var tagsetType = "MultiSelect"; // Options: MultiSelect, SingleSelect, FreeText
var tagsetScopes = new[] { "Tenant" }; // Options: Tenant, Environment, Project (can specify multiple)

// Optional Tags to add in the format "Tag name", "Tag Color"
var optionalTags = new Dictionary<string, string>();
optionalTags.Add("Early Adopter", "#ECAD3F");
optionalTags.Add("Stable", "#36A766");

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

try
{
    // Get space
    var space = repository.Spaces.FindByName(spaceName);
    var repositoryForSpace = client.ForSpace(space);

    // Create or modify tagset
    var tagsetEditor = repositoryForSpace.TagSets.CreateOrModify(tagsetName, tagsetDescription);

    // Add optional tags
    foreach (var tag in optionalTags)
    {
        tagsetEditor.AddOrUpdateTag(tag.Key, "", tag.Value);
    }
    tagsetEditor.Save();
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    return;
}
Python3
import json
import requests
from requests.api import get, head

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 '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}
space_name = "Default"
tagset_name = "MyTagset"
tagset_description = "My description"
tagset_type = "MultiSelect"  # Options: MultiSelect, SingleSelect, FreeText
tagset_scopes = ["Tenant"]  # Options: Tenant, Environment, Project (can specify multiple)
tags = []
tag = {
    'Id': None,
    'Name': 'Tag1',
    'Color': '#ECAD3F',
    'Description': 'Tag Description',
    'CanonicalTagName': None
}

tags.append(tag)

# Get space
uri = '{0}/api/spaces'.format(octopus_server_uri)
spaces = get_octopus_resource(uri, headers)
space = next((x for x in spaces if x['Name'] == space_name), None)

# Check to see if tagset exists
uri = '{0}/api/{1}/tagsets?partialName={2}'.format(octopus_server_uri, space['Id'], tagset_name)
tagsets = get_octopus_resource(uri, headers)

if len(tagsets) == 0:
    tagset = {
        'Name': tagset_name,
        'Description': tagset_description,
        'Type': tagset_type,
        'Scopes': tagset_scopes,
        'Tags': tags
    }

    uri = '{0}/api/{1}/tagsets'.format(octopus_server_uri, space['Id'])
    response = requests.post(uri, headers=headers, json=tagset)
    response.raise_for_status()    
else:
    print ('{0} already exists!'.format(tagset_name))
Go
package main

import (
	"fmt"
	"log"

	"net/url"

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

func main() {

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

	// Get reference to space
	space := GetSpace(apiURL, APIKey, spaceName)

	// Create client object
	client := octopusAuth(apiURL, APIKey, space.ID)

	// Get tagset
	tagset := GetTagSet(apiURL, APIKey, space, tagsetName, 0)

	if tagset == nil {
		// Create new tagset
		tagset = octopusdeploy.NewTagSet(tagsetName)

		// Create new tag
		tag := octopusdeploy.Tag{
			Name:        "MyTag",
			Color:       "#ECAD3F",
			Description: "My tag description",
		}

		// Add to set
		tagset.Tags = append(tagset.Tags, tag)

		// Add to server
		client.TagSets.Add(tagset)
	} else {
		fmt.Println("Tagset " + tagsetName + " already exists!")
	}
}

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

	spaceQuery := octopusdeploy.SpacesQuery{
		Name: spaceName,
	}

	// Get specific space object
	spaces, err := client.Spaces.Get(spaceQuery)

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

	for _, space := range spaces.Items {
		if space.Name == spaceName {
			return space
		}
	}

	return nil
}

func GetTagSet(octopusURL *url.URL, APIKey string, space *octopusdeploy.Space, tagsetName string, skip int) *octopusdeploy.TagSet {
	// Get client for space
	client := octopusAuth(octopusURL, APIKey, space.ID)

	// Get tagsets
	tagsetQuery := octopusdeploy.TagSetsQuery {
		PartialName: tagsetName,
	}

	tagsets, err := client.TagSets.Get(tagsetQuery)
	if err != nil {
		log.Println(err)
	}

	if len(tagsets.Items) == tagsets.ItemsPerPage {
		// call again
		tagset := GetTagSet(octopusURL, APIKey, space, tagsetName, (skip + len(tagsets.Items)))

		if tagset != nil {
			return tagset
		}
	} else {
		// Loop through returned items
		for _, tagset := range tagsets.Items {
			if tagset.Name == tagsetName {
				return tagset
			}
		}
	}

	return nil
}
Java

import com.octopus.sdk.Repository;
import com.octopus.sdk.domain.Space;
import com.octopus.sdk.domain.TagSet;
import com.octopus.sdk.http.ConnectData;
import com.octopus.sdk.http.OctopusClient;
import com.octopus.sdk.http.OctopusClientFactory;
import com.octopus.sdk.model.tag.TagResource;
import com.octopus.sdk.model.tagset.TagSetResource;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.Optional;

public class CreateTagSet {


  static final String octopusServerUrl = "http://localhost:8065";
  // as read from your profile in your Octopus Deploy server
  static final String apiKey = System.getenv("OCTOPUS_SERVER_API_KEY");

  public static void main(final String... args) throws IOException {
    final OctopusClient client = createClient();
    final Repository repo = new Repository(client);
    final Optional<Space> space = repo.spaces().getByName("TheSpaceName");
    if (!space.isPresent()) {
      System.out.println("No space named 'TheSpaceName' exists on server");
      return;
    }

    final TagSetResource newTagSet = new TagSetResource("TheTagSet");
    newTagSet.addTagsItem(new TagResource("FirstTag", "#ECAD3F"));
    final TagSet tagSet = space.get().tagSets().create(newTagSet);
  }

  // Create an authenticated connection to your Octopus Deploy Server
  private static OctopusClient createClient() throws MalformedURLException {
    final Duration connectTimeout = Duration.ofSeconds(10L);
    final ConnectData connectData =
        new ConnectData(new URL(octopusServerUrl), apiKey, connectTimeout);
    final OctopusClient client = OctopusClientFactory.createClient(connectData);

    return client;
  }
}

Help us continuously improve

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

Send feedback

Page updated on Wednesday, October 8, 2025