Override Variable values during a Deploy using Rest API

Hi. I am using version v2018.5.3 of Octopus Deploy. I have a Project and I am attempting to initiate a deployment via the Rest API. What I am struggling with is attempting to provide values that OD should use for the project’s variables for the duration of the deployment being triggered.

At the moment, I am posting to /api/deployments. In the body of the post, I am passing the releaseId and the environmentId. That’s fine. When I attempt to pass a FormValues value as well, none of the values are being used when the deployment starts on Octopus Deploy.

I have seen this question which suggests that you can define the Variable values in the FormValues field in the post request, but that is not working for me.

It looks like Octo.exe has support for this (the --variable option), and from what I’ve read I should be able to do everything via the Rest API that I can through Octo.exe. But I seem to be struggling to find an example that works for me.

Any help would be appreciated.

Just an update. I have upgraded Octopus Deploy to version 2018.08.11, and it made no difference when using the FormValues attribute. Still no joy

Hello,

Thanks for getting in touch and sorry for the delay in the reply.

I had a look at that example and what the UI generates, it looks like you need to supply the id of the variable in the FormValues field.

In my testing the payload sent was :

{
  "ReleaseId": "Releases-4",
  "EnvironmentId": "Environments-1",
  "TenantId": null,
  "SkipActions": [],
  "QueueTime": null,
  "QueueTimeExpiry": null,
  "FormValues": {
    "885330af-83c0-4d6b-df65-be551644cf4b": "THE VALUE I'm SENDING"
  },
  "ForcePackageDownload": false,
  "UseGuidedFailure": false,
  "SpecificMachineIds": [],
  "ExcludedMachineIds": [],
  "ForcePackageRedeployment": false
}

Could you share your payload and specific API calling code so I can debug, if the above info doesn’t help.

Another approach to take is to open the network developer tools in the browser and perform the actions manually in the UI, capture the calls and JSON, and compare the those with the ones you’re creating in your code.

Let me know how you go.

Regards,
Nick

Thanks for that help, I’ll give that a go. Is there an easy way to find what those GUIDs are? Can I call another Rest API endpoint that will give me those? Is that from a DeploymentProcess?

Thanks again,
Ed

Hi Ed,

If you call the release preview API supplying the environment id you’re planning to deploy to, you’ll get a response that has the form value ids.

The API endpoint was:
api/releases/Releases-5/deployments/preview/Environments-1

In my sample project the response looks like, my prompt variable is called prompt1:

{
  "StepsToExecute": [
    {
      "ActionId": "69490439-2d01-427f-a02d-9e7085ec4ee1",
      "ActionName": "script action",
      "ActionNumber": "1",
      "Roles": [],
      "MachineNames": [],
      "Machines": [],
      "CanBeSkipped": true,
      "IsDisabled": false,
      "HasNoApplicableMachines": false,
      "UnavailableMachines": [],
      "ExcludedMachines": null
    }
  ],
  "Form": {
    "Values": {
      "885330af-83c0-4d6b-df65-be551644cf4b": null
    },
    "Elements": [
      {
        "Name": "885330af-83c0-4d6b-df65-be551644cf4b",
        "Control": {
          "Type": "VariableValue",
          "Name": "prompt1",
          "Label": "prompt1",
          "Description": "",
          "IsSecure": false
        },
        "IsValueRequired": true
      }
    ]
  },
  "UseGuidedFailureModeByDefault": false,
  "Links": {}
}

Let me know how you go with that approach.
Regards,
Nick

Thanks… you’ve just saved me a heap of time :wink: I’ll have a crack this avo.

I’ve tried calling that endpoint to give the GUID Mapping to the variable names, and my return value shows none of the data in your form element. What I see is something like this (excuse the horribly formatted text)

[Assembled: 2018 - 10 - 24T00: 57: 06.867 + 00: 00, ChannelId: Channels - 244, Id: Releases - 7777, LibraryVariableSetSnapshotIds: [], Links: [Artifacts: /api/artifacts ? regarding = Releases - 7777, Defects : /api/releases / Releases - 7777 / defects, DeploymentTemplate: /api/releases / Releases - 7777 / deployments / template, Deployments: /api/releases / Releases - 7777 / deployments {
			 ? skip,
			take
		}, Progression : /api/releases / Releases - 7777 / progression, Project: /api/projects / Projects - 381, ProjectDeploymentProcessSnapshot: /api/deploymentprocesses / deploymentprocess - Projects - 381 - s - 36 - GRJ8X, ProjectVariableSnapshot: /api/variables / variableset - Projects - 381 - s - 55 - L5THS, ReportDefect: /api/releases / Releases - 7777 / defects, ResolveDefect: /api/releases / Releases - 7777 / defects / resolve, Self: /api/releases / Releases - 7777 {
			 ? ignoreChannelRules
		}, SnapshotVariables : /api/releases / Releases - 7777 / snapshot - variables, Web: /app#/releases / Releases - 7777], ProjectDeploymentProcessSnapshotId: deploymentprocess - Projects - 381 - s - 36 - GRJ8X, ProjectId: Projects - 381, ProjectVariableSetSnapshotId: variableset - Projects - 381 - s - 55 - L5THS, ReleaseNotes: null, SelectedPackages: [[ActionName: Check PostgreSQL, PackageReferenceName: , StepName: Check PostgreSQL, Version: 1.1 - SNAPSHOT - BUILD - 48], [ActionName: Check Service Bus, PackageReferenceName: , StepName: Check Service Bus, Version: 1.1 - SNAPSHOT - BUILD - 48], [ActionName: Check Azure Application Gateway, PackageReferenceName: , StepName: Check Azure Application Gateway, Version: 1.1 - SNAPSHOT - BUILD - 48], [ActionName: Check Load Balancer, PackageReferenceName: , StepName: Check Load Balancer, Version: 1.1 - SNAPSHOT - BUILD - 48], [ActionName: Register Edge Into Cluster, PackageReferenceName: , StepName: Register Edge Into Cluster, Version: 1.1 - SNAPSHOT - BUILD - 48], [ActionName: Manage Service Fabric Application, PackageReferenceName: , StepName: Manage Service Fabric Application, Version: 1.1 - SNAPSHOT - BUILD - 48]], Version: 1.1 - SNAPSHOT - BUILD - 48]
[Form: [Elements: [], Values: [: ]], Links: [: ], StepsToExecute: [[ActionId: 8116103a - f9e1 - 4b3a - 8bf4 - 17d5316cc681, ActionName: Check PostgreSQL, ActionNumber: 1, CanBeSkipped: false, ExcludedMachines: null, HasNoApplicableMachines: false, IsDisabled: false, MachineNames: [], Machines: [], Roles: [], UnavailableMachines: []], [ActionId: 971a8e9f - c996 - 4026 - 84d2 - 902e1d707744, ActionName: Check Service Bus, ActionNumber: 2, CanBeSkipped: false, ExcludedMachines: null, HasNoApplicableMachines: false, IsDisabled: false, MachineNames: [], Machines: [], Roles: [], UnavailableMachines: []], [ActionId: c7569fa2 - 5edc - 482e-82a1 - 986db546b098, ActionName: Check Azure Application Gateway, ActionNumber: 3, CanBeSkipped: false, ExcludedMachines: null, HasNoApplicableMachines: false, IsDisabled: false, MachineNames: [], Machines: [], Roles: [], UnavailableMachines: []], [ActionId: d4042adf - 0520 - 435a - 9af7 - f401707375a3, ActionName: Check Load Balancer, ActionNumber: 4, CanBeSkipped: false, ExcludedMachines: null, HasNoApplicableMachines: false, IsDisabled: false, MachineNames: [], Machines: [], Roles: [], UnavailableMachines: []], [ActionId: 44ae4b0a - 0bb2 - 4665 - ba2c - 9b5255224679, ActionName: Register Edge Into Cluster, ActionNumber: 5, CanBeSkipped: true, ExcludedMachines: null, HasNoApplicableMachines: false, IsDisabled: false, MachineNames: [], Machines: [], Roles: [], UnavailableMachines: []], [ActionId: 19039b60 - 4630 - 4016 - 9faf - 2e75d6f1e528, ActionName: Manage Service Fabric Application, ActionNumber: 6, CanBeSkipped: false, ExcludedMachines: null, HasNoApplicableMachines: false, IsDisabled: false, MachineNames: [], Machines: [], Roles: [], UnavailableMachines: []]], UseGuidedFailureModeByDefault: false]

Can I get those from the ProjectVariableSnapshot? I tried that before, but it didn’t return json content.

And, FYI, this is the groovy script I’m doing this from. I’ve been able to use this script for a while now to initiate deployments, but this is the first time I am trying to provide values for Octopus Deploy Variables as part of the deployment

@Grab('org.codehaus.groovy.modules.http-builder:http-builder:0.7.1')
// This next line seems like a bit of a hack.  If I don't specify the jdk15, grapes
// cannot find the correct dependency (from the above Grab) in consecutive runs
@Grab('net.sf.json-lib:json-lib:2.3:jdk15')

import groovyx.net.http.RESTClient
import static groovyx.net.http.ContentType.*
import groovy.json.JsonOutput

def cli = new CliBuilder(usage: '-p <OD Project Slug> -e <Target Environment> [options]')
cli.h(longOpt: 'help', 'Print this message')
cli.p(longOpt: 'projectSlug', args: 1, argName: 'OD Project Slug', 'The slug of the Project in Octopus Deploy whose releases will be deployed')
cli.e(longOpt: 'targetEnv', args: 1, argName: 'Target Environment', 'The Environment to which the Release will be deployed')
cli.v(longOpt: 'projectVersion', args:1, argName: 'Release Version', 'The version of the Project to deploy.  If not defined, the latest Release will be used')
cli.u(longOpt: 'odUrl', args: 1, argName: 'OctopusDeploy URL', 'The URL of the Octopus Deploy instance being used')
cli.a(longOpt: 'apiKey', args: 1, argName: 'API Key', 'The API Key to use when interacting with Octopus Deploy')
cli.x(longOpt: 'noProxy', 'Skip proxy definition')
cli.ph(longOpt: 'httpProxyHost', args: 1, argName: 'Host Name', 'The host name of the machine hosting the http proxy')
cli.pp(longOpt: 'httpProxyPort', args: 1, argName: 'Port', 'The port of the machine hosting the http proxy')

def options = cli.parse(args)

if (!options || options.h || !options.p || !options.e || !options.u || !options.a) {
    cli.usage()
    return
}

if (!options.x) {
    def host = (options.ph ? options.ph : 'proxy.s.com')
    def port = (options.pp ? options.pp : '80')
    println 'Using proxy ' + host + ':' + port
    System.properties.putAll(["http.proxyHost":host, "http.proxyPort":port])
    System.properties.putAll(["https.proxyHost":host, "https.proxyPort":port])
}

def projectSlug = options.p
def environmentName = options.e

def http = new RESTClient(options.u)
http.defaultRequestHeaders.'X-Octopus-ApiKey' = options.a

def resp = http.get( path: '/api/projects/' + projectSlug)
assert resp.status == 200
def projectObj = resp.getData()

def releaseObj

if (options.v) {
	resp = http.get(path: '/api/projects/' + projectObj.Id + '/releases/' + options.v)
	assert resp.status == 200
	releaseObj = resp.getData()
} else {
	// Grab the latest release
	resp = http.get(path: '/api/projects/' + projectObj.Id + '/releases')
	assert resp.status == 200
	releaseObj = resp.getData().Items[0]
}

println releaseObj

resp = http.get(path: '/api/environments')
assert resp.status == 200

def envObj = resp.getData().Items.find { it.Name == environmentName}
if (envObj) {
	// Find the variables available to the release and environmentId
	resp = http.get(path: '/api/releases/' + releaseObj.Id + '/deployments/preview/' + envObj.Id)
	assert resp.status == 200
	def deploymentPreview = resp.getData();
	println deploymentPreview

//    println 'Deploying release ' + releaseObj.Version + ' of Project ' + projectObj.Name + ' to ' + envObj.Name
//	def formValuesMap = [Variable1: 'Value1-From-Script', Variable2: 'Value2-From-Script', Variable3: 'Value3-From-Script']
//	println formValuesMap
//	println JsonOutput.toJson(formValuesMap)
//    resp = http.post(
//        path: '/api/deployments',
//        body: [ releaseId: releaseObj.Id, environmentId: envObj.Id, formValues: formValuesMap ],
//        requestContentType: JSON)
//    assert resp.status == 201
//    def taskLink = resp.getData().Links."Task"
//    if (taskLink) {
//        println 'Waiting for Deployment Task to complete'
//        resp = http.get(path: taskLink);
//        assert resp.status == 200
//        while (!resp.getData().IsCompleted) {
//            sleep(5000)
//            resp = http.get(path: taskLink);
//            assert resp.status == 200
//        }
//        if (!resp.getData().FinishedSuccessfully) {
//			throw new Exception(resp.getData().ErrorMessage)
//        }
//        println 'Deployment completed: ' + resp.getData().Duration
//    }
//} else {
//    println 'No match'
}

OK. So, hopefully I have some more information. Your original advise was correct. I can see the variables in the form. However, for the variables to be accessible, I need to define the variable in the OD Project so that they prompt for a value. When I configure it this way, I can see the variable names in the Form attribute when I call the preview. I should be able to work with this.

If there’s ever a future enhancement to add a Variables element in the request that does this by name, though, I’d be happy to support it .:slight_smile:

Thanks,
Ed

Good to hear you’ve made some progress, yes this isn’t that user friendly via the API.

Let us know if you hit any other issues.

Regards,
Nick

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.