This script demonstrates how to programmatically create a new runbook snapshot and publish it for use by runbook consumers. If the runbook references any packages from the Octopus built-in repository, then the latest package versions will be included in the snapshot.
Usage
Provide values for the following:
- Octopus URL
- Octopus API Key
- Name of the space to work with
- Name of the project with the runbook
- Name of the runbook
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"
$projectName = "MyProject"
$runbookName = "MyRunbook"
# Get space
$spaces = Invoke-RestMethod -Uri "$octopusURL/api/spaces?partialName=$([uri]::EscapeDataString($spaceName))&skip=0&take=100" -Headers $header
$space = $spaces.Items | Where-Object { $_.Name -eq $spaceName }
# Get project
$projects = Invoke-RestMethod -Uri "$octopusURL/api/$($space.Id)/projects?partialName=$([uri]::EscapeDataString($projectName))&skip=0&take=100" -Headers $header
$project = $projects.Items | Where-Object { $_.Name -eq $projectName }
# Get runbook
$runbooks = Invoke-RestMethod -Uri "$octopusURL/api/$($space.Id)/projects/$($project.Id)/runbooks?partialName=$([uri]::EscapeDataString($runbookName))&skip=0&take=100" -Headers $header
$runbook = $runbooks.Items | Where-Object { $_.Name -eq $runbookName }
# Get a runbook snapshot template
$runbookSnapshotTemplate = Invoke-RestMethod -Uri "$octopusURL/api/$($space.Id)/runbookProcesses/$($runbook.RunbookProcessId)/runbookSnapshotTemplate" -Headers $header
# Create a runbook snapshot
$body = @{
ProjectId = $project.Id
RunbookId = $runbook.Id
Name = $runbookSnapshotTemplate.NextNameIncrement
Notes = $null
SelectedPackages = @()
}
# Include latest built-in feed packages
foreach($package in $runbookSnapshotTemplate.Packages)
{
# Get latest package version
$packages = Invoke-RestMethod -Uri "$octopusURL/api/$($space.Id)/feeds/$($package.FeedId)/packages/versions?packageId=$($package.PackageId)" -Headers $header
$latestPackage = $packages.Items | Select-Object -First 1
$package = @{
ActionName = $package.ActionName
Version = $latestPackage.Version
PackageReferenceName = $package.PackageReferenceName
}
$body.SelectedPackages += $package
}
$body = $body | ConvertTo-Json -Depth 10
$runbookPublishedSnapshot = Invoke-RestMethod -Method Post -Uri "$octopusURL/api/$($space.Id)/runbookSnapshots?publish=true" -Body $body -Headers $header
# Re-get runbook
$runbook = Invoke-RestMethod -Uri "$octopusURL/api/$($space.Id)/runbooks/$($runbook.Id)" -Headers $header
# Publish the snapshot
$runbook.PublishedRunbookSnapshotId = $runbookPublishedSnapshot.Id
Invoke-RestMethod -Method Put -Uri "$octopusURL/api/$($space.Id)/runbooks/$($runbook.Id)" -Body ($runbook | ConvertTo-Json -Depth 10) -Headers $header
Write-Host "Published runbook snapshot: $($runbookPublishedSnapshot.Id) ($($runbookPublishedSnapshot.Name))"
PowerShell (Octopus.Client)
# Load assembly
Add-Type -Path 'path:\to\Octopus.Client.dll'
$octopusURL = "https://your-octopus-url"
$octopusAPIKey = "API-YOUR-KEY"
$spaceName = "Default"
$projectName = "MyProject"
$runbook = "MyRunbook"
$endpoint = New-Object Octopus.Client.OctopusServerEndpoint($octopusURL, $octopusAPIKey)
$repository = New-Object Octopus.Client.OctopusRepository($endpoint)
$client = New-Object Octopus.Client.OctopusClient($endpoint)
# Get space
$space = $repository.Spaces.FindByName($spaceName)
$repositoryForSpace = $client.ForSpace($space)
# Get project
$project = $repositoryForSpace.Projects.FindByName($projectName)
# Get runbook
$runbook = $repositoryForSpace.Runbooks.FindByName($project, $runbook)
# Get runbook snapshot template
$runbookSnapshotTemplate = $repositoryForSpace.Runbooks.GetRunbookSnapshotTemplate($runbook)
# Create a runbook snapshot
$runbookSnapshot = New-Object Octopus.Client.Model.RunbookSnapshotResource
$runbookSnapshot.ProjectId = $project.Id
$runbookSnapshot.RunbookId = $runbook.Id
$runbookSnapshot.Name = $runbookSnapshotTemplate.NextNameIncrement
$runbookSnapshot.SpaceId = $space.Id
# Add packages
foreach ($package in $runbookSnapshotTemplate.Packages)
{
# Get the feed
$feed = $repositoryForSpace.Feeds.Get($package.FeedId)
$latestPackage = [Linq.Enumerable]::FirstOrDefault($repositoryForSpace.Feeds.GetVersions($feed, @($package.Id)))
# Create selected package object
$selectedPackage = New-Object Octopus.Client.Model.SelectedPackage -Property @{
ActionName = $package.ActionName
Version = $latestPackage.Version
PackageReferenceName = $package.PackageReferenceName
}
# Add to selected packages
$runbookSnapshot.SelectedPackages.Add($selectedPackage)
}
#Publish snapshot
$runbookSnapshot = $repositoryForSpace.RunbookSnapshots.Create($runbookSnapshot)
$runbook.PublishedRunbookSnapshotId = $runbookSnapshot.Id
$repositoryForSpace.Runbooks.Modify($runbook)
C#
#r "path\to\Octopus.Client.dll"
using Octopus.Client;
using Octopus.Client.Model;
using System;
using System.Linq;
// 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";
var spaceName = "Default";
var projectName = "MyProject";
var runbookName = "MyRunbook";
// Create repository object
var endpoint = new OctopusServerEndpoint(octopusURL, octopusAPIKey);
var repository = new OctopusRepository(endpoint);
var client = new OctopusClient(endpoint);
// Get space
var space = repository.Spaces.FindByName(spaceName);
var spaceRepository = client.ForSpace(space);
// Get project
var project = spaceRepository.Projects.FindByName(projectName);
// Get runbook
var runbook = spaceRepository.Runbooks.FindByName(project, runbookName);
// Get runbook snapshot template
var runbookSnapshotTemplate = spaceRepository.Runbooks.GetRunbookSnapshotTemplate(runbook);
// Create runbook snapshot
var runbookSnapshot = new Octopus.Client.Model.RunbookSnapshotResource(project.Id);
runbookSnapshot.RunbookId = runbook.Id;
runbookSnapshot.Name = runbookSnapshotTemplate.NextNameIncrement;
runbookSnapshot.SpaceId = space.Id;
// Add any referenced packages
foreach (var package in runbookSnapshotTemplate.Packages)
{
// Get the feed
var feed = spaceRepository.Feeds.Get(package.FeedId);
var latestPackage = spaceRepository.Feeds.GetVersions(feed, (new string[] { package.PackageId }));
// Create new selected package object
var selectedPackage = new Octopus.Client.Model.SelectedPackage(package.ActionName, package.PackageReferenceName, latestPackage[0].Version);
// Add to runbook snapshot
runbookSnapshot.SelectedPackages.Add(selectedPackage);
}
// Publish snapshot
runbookSnapshot = spaceRepository.RunbookSnapshots.Create(runbookSnapshot);
runbook.PublishedRunbookSnapshotId = runbookSnapshot.Id;
spaceRepository.Runbooks.Modify(runbook);
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
# Define Octopus server variables
octopus_server_uri = 'https://your-octopus-url'
octopus_api_key = 'API-YOUR-KEY'
headers = {'X-Octopus-ApiKey': octopus_api_key}
space_name = 'Default'
project_name = 'MyProject'
runbook_name = 'MyRunbook'
# 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)
# Get project
uri = '{0}/api/{1}/projects'.format(octopus_server_uri, space['Id'])
projects = get_octopus_resource(uri, headers)
project = next((x for x in projects if x['Name'] == project_name), None)
# Get project runbooks
uri = '{0}/api/{1}/projects/{2}/runbooks'.format(octopus_server_uri, space['Id'], project['Id'])
runbooks = get_octopus_resource(uri, headers)
runbook = next((x for x in runbooks if x['Name'] == runbook_name), None)
# Get runbook snapshot template
uri = '{0}/api/{1}/runbookprocesses/{2}/runbooksnapshottemplate'.format(octopus_server_uri, space['Id'], runbook['RunbookProcessId'])
runbook_snapshot_template = get_octopus_resource(uri, headers)
# Create runbook snapshot
runbookSnapshotJson = {
'ProjectId': project['Id'],
'RunbookId': runbook['Id'],
'Name': runbook_snapshot_template['NextNameIncrement'],
'SelectedPackages': []
}
# Include any referenced packages
for package in runbook_snapshot_template['Packages']:
uri = '{0}/api/{1}/feeds/{2}/packages/versions?packageId={3}'.format(octopus_server_uri, space['Id'], package['FeedId'], package['PackageId'])
packages = get_octopus_resource(uri, headers)
latestPackage = packages[0] # get the latest one
selectedPackage = {
'ActionName': package['ActionName'],
'Version': latestPackage['Version'],
'PackageReferenceName': package['PackageReferenceName']
}
runbookSnapshotJson['SelectedPackages'].append(selectedPackage)
# Create snapshot
uri = '{0}/api/{1}/runbooksnapshots'.format(octopus_server_uri, space['Id'])
response = requests.post(uri, headers=headers, json=runbookSnapshotJson)
response.raise_for_status()
# Get results of API call
runbookSnapshot = json.loads(response.content.decode('utf-8'))
# Update the runbook object
runbook['PublishedRunbookSnapshotId'] = runbookSnapshot['Id']
uri = '{0}/api/{1}/runbooks/{2}'.format(octopus_server_uri, space['Id'], runbook['Id'])
response = requests.put(uri, headers=headers, json=runbook)
response.raise_for_status()
Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"io/ioutil"
"github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy"
)
type runbooksnapshot struct {
ProjectID string
RunbookID string
Name string
Notes string
SelectedPackages []octopusdeploy.SelectedPackage
}
func main() {
apiURL, err := url.Parse("https://your-octopus-url")
if err != nil {
log.Println(err)
}
APIKey := "API-YourAPI"
spaceName := "Default"
projectName := "MyProject"
runbookName := "MyRunbook"
// Get reference to space
space := GetSpace(apiURL, APIKey, spaceName)
// Create client object
client := octopusAuth(apiURL, APIKey, space.ID)
// Get project
project := GetProject(apiURL, APIKey, space, projectName)
// Get runbook
runbook := GetRunbook(client, project, runbookName)
// Get runbook snapshot template
runbookSnapshotTemplate := GetRunbookSnapshotTemplate(apiURL, APIKey, space, runbook)
// Create runbook snapshot object
runbookSnapshot := runbooksnapshot{
ProjectID: project.ID,
RunbookID: runbook.ID,
Name: runbookSnapshotTemplate["NextNameIncrement"].(string),
}
runbookPackages := runbookSnapshotTemplate["Packages"].([]interface{})
for i := 0; i < len(runbookPackages); i++ {
runbookPackage := runbookPackages[i].(map[string]interface{})
version := GetPackageVersion(apiURL, APIKey, space, runbookPackage["FeedId"].(string), runbookPackage["PackageId"].(string))
selectedPackage := octopusdeploy.SelectedPackage{
ActionName: runbookPackage["ActionName"].(string),
Version: version,
PackageReferenceName: runbookPackage["PackageReferenceName"].(string),
}
runbookSnapshot.SelectedPackages = append(runbookSnapshot.SelectedPackages, selectedPackage)
}
// Create new snapshot
runbookSnapshotId := CreateRunbookSnapshot(apiURL, APIKey, space, runbookSnapshot)
// Publish snapshot
runbook.PublishedRunbookSnapshotID = runbookSnapshotId
client.Runbooks.Update(runbook)
}
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 GetProject(octopusURL *url.URL, APIKey string, space *octopusdeploy.Space, projectName string) *octopusdeploy.Project {
// Create client
client := octopusAuth(octopusURL, APIKey, space.ID)
projectsQuery := octopusdeploy.ProjectsQuery {
Name: projectName,
}
// Get specific project object
projects, err := client.Projects.Get(projectsQuery)
if err != nil {
log.Println(err)
}
for _, project := range projects.Items {
if project.Name == projectName {
return project
}
}
return nil
}
func GetRunbook(client *octopusdeploy.Client, project *octopusdeploy.Project, runbookName string) *octopusdeploy.Runbook {
// Get runbook
runbooks, err := client.Runbooks.GetAll()
if err != nil {
log.Println(err)
}
for i := 0; i < len(runbooks); i++ {
if runbooks[i].ProjectID == project.ID && runbooks[i].Name == runbookName {
return runbooks[i]
}
}
return nil
}
func GetRunbookSnapshotTemplate(octopusURL *url.URL, APIKey string, space *octopusdeploy.Space, runbook *octopusdeploy.Runbook) map[string]interface{} {
// Query api for tasks
templateApi := octopusURL.String() + "/api/" + space.ID + "/runbookprocesses/" + runbook.RunbookProcessID + "/runbooksnapshottemplate"
// Create http client
httpClient := &http.Client{}
// perform request
request, _ := http.NewRequest("GET", templateApi, nil)
request.Header.Set("X-Octopus-ApiKey", APIKey)
response, err := httpClient.Do(request)
if err != nil {
log.Println(err)
}
responseData, err := ioutil.ReadAll(response.Body)
var f interface{}
jsonErr := json.Unmarshal(responseData, &f)
if jsonErr != nil {
log.Println(err)
}
template := f.(map[string]interface{})
// return the template
return template
}
func GetPackageVersion(octopusURL *url.URL, APIKey string, space *octopusdeploy.Space, feedId string, packageId string) string {
packageApi := octopusURL.String() + "/api/" + space.ID + "/feeds/" + feedId + "/packages/versions?packageId=" + packageId
// Create http client
httpClient := &http.Client{}
// perform request
request, _ := http.NewRequest("GET", packageApi, nil)
request.Header.Set("X-Octopus-ApiKey", APIKey)
response, err := httpClient.Do(request)
if err != nil {
log.Println(err)
}
responseData, err := ioutil.ReadAll(response.Body)
var f interface{}
jsonErr := json.Unmarshal(responseData, &f)
if jsonErr != nil {
log.Println(err)
}
// Map the returned data
packageItems := f.(map[string]interface{})
// Returns the list of items, translate it to a map
returnedItems := packageItems["Items"].([]interface{})
// We only need the most recent version
mostRecentPackageVersion := returnedItems[0].(map[string]interface{})
return mostRecentPackageVersion["Version"].(string)
}
func CreateRunbookSnapshot(octopusURL *url.URL, APIKey string, space *octopusdeploy.Space, snapshot runbooksnapshot) string {
snapshotApi := octopusURL.String() + "/api/" + space.ID + "/runbooksnapshots"
// Create http client
httpClient := &http.Client{}
// Marshall the snapshot object
snapshotJson, err := json.Marshal(snapshot)
if err != nil {
log.Println(err)
}
// Make post request
request, err := http.NewRequest("POST", snapshotApi, bytes.NewBuffer(snapshotJson))
request.Header.Set("X-Octopus-ApiKey", APIKey)
request.Header.Set("Content-Type", "application/json")
// Execute post and get response
response, err := httpClient.Do(request)
responseData, err := ioutil.ReadAll(response.Body)
var f interface{}
jsonErr := json.Unmarshal(responseData, &f)
if jsonErr != nil {
log.Println(err)
}
runbookMap := f.(map[string]interface{})
return runbookMap["Id"].(string)
}
Help us continuously improve
Please let us know if you have any feedback about this page.
Page updated on Sunday, January 1, 2023