Harness StartExecution API Deep Dive
tags: training
api
graphql
Introduction
- With the StartExecution API, users can now start executions via the Harness API
- The use can start workflows and pipelines
- The API access is tied to the API Key created in the Access Management section in Continuous Security
- Links to the Harness docs: Trigger Workflows or Pipelines Using GraphQL API - Harness.io Docs
- Harness Blog Post: Deployment API is live!
Customer Use Cases
- Customers will incorporate the API call in their Jenkins or another system, in order to fire off the deployment (System to System interaction)
- A Harness CLI tool, that enables developers to deploy via an in house cli that triggers deployment (i.e.
devkit harness deploy
) - The User uses the Harness API Explorer and starts an execution via GraphQL query in a user-login session.
Security
Most enterprise customers will ask about the security around the Harness API, specifically the Write capabilities and the Execution API capabilities.
Common Questions
- How do we know who triggered the deployment?
- What access does that user have?
- Is the triggered deployment logged?
- Is the triggered deployment viewable in our Audit Trail?
Authentication
- Our APIs do support Authentication, there are two ways of doing so today
- The User Logged In Session, So as your user you can navigate to the API Explorer and execute queries based on your specific user permissions
- Via the API Key, the key is scoped to a user group so Harness can confirm who the user executing the API command is via the User Group they are a part of.
Authorization
- The authorization with the API comes from the API Token.
- The API Token is scoped to a Harness User Group or it can be scoped to multiple
- For list apis, the data being returned also depends on the permissions, you are only allowed to see what your User Group is authorized to read and have access to.
Example:
We add an API Key scoped to two different permission groups
Below, the user will see the key with the “Permissions Inherited From” field
The Permissions those groups have will be tied to the token. The user or system that uses the token will only be authorized to perform the specific actions on their behalf.
Since neither of these user groups have permission to execute a production pipeline, when I try to deploy the pipeline with the key:
The API throws an error stating that the user is not authorized to deploy
NOTE:
- The Authorization and Authentication of the API doesn’t apply to specific portions, the entire API follows this model
- The API Key, organizations may be worried about the key getting stale, its on the Admins to rotate the key and provide it to the team
- How do I rotate the key?
- Delete the old API Key and Create a new one
- How do I rotate the key?
Examples
Getting Piplines By Name
Sample Query to get Pipeline By Name and the variables associated with it
{
pipelineByName(applicationId: "iIfYmHDqTj6Gsygtwt4VRQ", pipelineName: "Guestbook Pipeline") {
id
name
pipelineVariables {
name
type
required
}
}
}
Sample Response:
{
"data": {
"pipelineByName": {
"id": "YdK1ONo9TnyzIK5-plsnAg",
"name": "Guestbook Pipeline",
"pipelineVariables": [
{
"name": "skipCV",
"type": "Text",
"required": false
},
{
"name": "hotfix",
"type": "Text",
"required": false
},
{
"name": "branch",
"type": "Text",
"required": false
}
]
}
}
}
Getting Workflow By Name
Sample Query to get Workflow By Name and the variables associated with it
{
workflowByName(applicationId: "iIfYmHDqTj6Gsygtwt4VRQ", workflowName: "K8s Rolling Deployment") {
id
name
workflowVariables {
name
type
required
}
}
}
Sample Response:
{
"data": {
"workflowByName": {
"id": "VzViT24gTluKWNFpdrL95A",
"name": "K8s Rolling Deployment",
"workflowVariables": [
{
"name": "Environment",
"type": "Environment",
"required": true
},
{
"name": "Service",
"type": "Service",
"required": true
},
{
"name": "InfraDefinition_KUBERNETES",
"type": "Infrastructure definition",
"required": true
},
{
"name": "hotfix",
"type": "Text",
"required": false
},
{
"name": "branch",
"type": "Text",
"required": false
},
{
"name": "skipCV",
"type": "Text",
"required": false
}
]
}
}
}
Sample Queries to Deploy Fargate Prod Pipeline
This example has no workflow variables, just a plain deployment
So I first queried the by Application Name and Returned Application ID and PipelineID
query {
applicationByName(name:"Harness Demo"){
id
name
pipelines(limit:4){
nodes{
id
description
}
}
}
}
After getting the correct information, I created the startExecution Mutation to execute the deployment.
I need to pass the Application ID and Pipeline ID as inputs. Based on my mutation, I want to return notes, execution id, and application id.
startExecution (input:$startExecution){
clientMutationId
execution {
notes
id
application{
id
}
}
}
}
So for the Parameters to execute the query, I passed the information below:
{
"startExecution": {
"applicationId": "<YOUR APPLICATION ID>",
"notes": "Demo",
"executionType": "PIPELINE",
"entityId": "<YOUR PIPELINE ID>",
"serviceInputs": {
"name": "fargate-nginx",
"artifactValueInput": {
"buildNumber": {
"buildNumber": "1.10.1",
"artifactSourceName": "library_nginx"
},
"valueType": "BUILD_NUMBER"
}
}
}
}
Once, passing in these parameters, I received a return payload and the pipeline executed!
{
"data": {
"startExecution": {
"clientMutationId": null,
"execution": {
"notes": "Demo",
"id": "<YOUR EXECUTION ID>",
"application": {
"id": "<YOUR APPLICATION ID>"
}
}
}
}
}
and I also see in the Continuous Deployment Page this beautiful deployment!
Deploying a Pipeline w/ Variables
My Query:
mutation($startExecution: StartExecutionInput!){
startExecution (input:$startExecution){
clientMutationId
execution {
notes
status
startedAt
id
application{
id
}
}
}
}
My Query Variables Input:
{
"startExecution": {
"applicationId": "iIfYmHDqTj6Gsygtwt4VRQ",
"notes": "Demo",
"executionType": "PIPELINE",
"entityId": "YdK1ONo9TnyzIK5-plsnAg",
"serviceInputs": [
{
"name": "guestbook",
"artifactValueInput": {
"buildNumber": {
"buildNumber": "v6",
"artifactSourceName": "guestbook"
},
"valueType": "BUILD_NUMBER"
}
}
],
"variableInputs": [
{
"name": "skipCV",
"variableValue": {
"type": "NAME",
"value": "true"
}
},
{
"name": "branch",
"variableValue": {
"value": "master",
"type": "NAME"
}
},
{
"name": "hotfix",
"variableValue": {
"value": "false",
"type": "NAME"
}
}
]
}
}
Proof that the workflow variables were passed in:
"branch"
"hotfix"
Since it was set to false all stages were deployed
"skipCV"
The end result:
Deploying a Workflow w/ Variables
My Query for the Execution:
mutation($startExecution: StartExecutionInput!){
startExecution (input:$startExecution){
clientMutationId
execution {
notes
status
startedAt
id
application{
id
}
}
}
}
My Query Variables:
{
"startExecution": {
"applicationId": "iIfYmHDqTj6Gsygtwt4VRQ",
"notes": "Demo",
"executionType": "WORKFLOW",
"entityId": "VzViT24gTluKWNFpdrL95A",
"serviceInputs": {
"name": "guestbook",
"artifactValueInput": {
"buildNumber": {
"buildNumber": "v6",
"artifactSourceName": "guestbook"
},
"valueType": "BUILD_NUMBER"
}
},
"variableInputs": [
{
"name": "Environment",
"variableValue": {
"type": "NAME",
"value": "dev"
}
},
{
"name": "InfraDefinition_KUBERNETES",
"variableValue": {
"type": "NAME",
"value": "k8s-dev"
}
},
{
"name":"Service",
"variableValue": {
"type": "NAME",
"value": "guestbook"
}
},
{
"name": "hotfix",
"variableValue": {
"type": "NAME",
"value": "false"
},
"name": "branch",
"variableValue": {
"type": "NAME",
"value": "master"
},
"name": "skipCV",
"variableValue": {
"type": "NAME",
"value": "true"
}
}
]
}
}
Proof of Execution
The value was “master” for “branch”. The “branch” was successfully passed:
and the Prometheus step was skipped due to the condition. “skipCV” was set to “true”.
Checking the Status of Deployments via API
For Pipeline Execution
query{
execution(executionId: "QzhqUddfQiKMxJdEzZC3bg"){
id
... on PipelineExecution {
id
status
}
}
}
For Workflow Execution
{
execution(executionId:"_Mffj1GZQd-VgNN_XWA7NQ"){
id
status
... on WorkflowExecution {
id
outcomes{
nodes{
execution {
id
endedAt
startedAt
}
}
}
}
}
}
Scripts for CLI tools or System to System Interaction
For our Scripters, here is a sample script with the startExecution API call:
#ARGUMENTS
APPLICATIONID="<YOUR APPLICATION ID>"
PIPELINEID="<YOUR PIPELINE ID>"
BUILDNO="<YOUR BUILD NO>"
SERVICENAME="<YOUR SERVICE NAME>"
ARTIFACTSOURCENAME="<YOUR ARTIFACT SOURCE>"
# Execute Pipeline
func_execute_pipeline(){
curl --request POST \
--url 'https://app.harness.io/gateway/api/graphql?accountId='$HARNESS_ACCOUNT_ID'' \
--header 'content-type: application/json' \
--header 'x-api-key:'$HARNESS_KEY' \
--data '{"query":"\nmutation($startExecution: StartExecutionInput!){\n startExecution (input:$startExecution){\n clientMutationId\n execution {\n notes\n id\n application{\n id\n }\n }\n }\n}","variables":{"startExecution":{"applicationId":"'$APPLICATIONID'","notes":"Demo","executionType":"PIPELINE","entityId":"'$PIPELINEID'","serviceInputs":{"name":"'$SERVICENAME'","artifactValueInput":{"buildNumber":{"buildNumber":"'$BUILDNO'","artifactSourceName":"'$ARTIFACTSOURCENAME'"},"valueType":"BUILD_NUMBER"}}}}}'
}
also for the Get Application by Name Script Sample:
# Get Application By Name
APPNAME="<YOUR APP NAME>"
func_getAppByName(){
curl --request POST \
--url 'https://app.harness.io/gateway/api/graphql?accountId='$HARNESS_ACCOUNT_ID'' \
--header 'content-type: application/json' \
--header 'x-api-key:'$HARNESS_KEY' \
--data '{"query":"query {\n applicationByName(name:\"'$APPNAME'\"){\n id\n name\n pipelines(limit:4){\n nodes{\n id\n description\n }\n }\n }\n}\n\n"}'
}
You as a user can quickly generate these curl commands, using tools like Insomnia or Postman to generate these calls.
To Download Insomnia: Download - Insomnia
To Download Postman: https://www.postman.com/downloads/
I used insomnia for most of my work, here is how a user would create these curl commands:
Click on the Arrow key next to your command and select from the drop down:
Select “Copy as Curl” and its automatically copied to your clipboard.
I pasted mine here:
curl --request POST \
--url 'https://app.harness.io/gateway/api/graphql?accountId=<ACOUNTID> \
--header 'content-type: application/json' \
--header 'x-api-key: <API_KEY>' \
--data '{"query":"\nmutation($startExecution: StartExecutionInput!){\n startExecution (input:$startExecution){\n clientMutationId\n execution {\n notes\n status\n startedAt\n id\n application{\n id\n }\n }\n }\n}","variables":{"startExecution":{"applicationId":"iIfYmHDqTj6Gsygtwt4VRQ","notes":"Demo","executionType":"WORKFLOW","entityId":"VzViT24gTluKWNFpdrL95A","serviceInputs":{"name":"guestbook","artifactValueInput":{"buildNumber":{"buildNumber":"v6","artifactSourceName":"guestbook"},"valueType":"BUILD_NUMBER"}},"variableInputs":[{"name":"Environment","variableValue":{"type":"NAME","value":"dev"}},{"name":"InfraDefinition_KUBERNETES","variableValue":{"type":"NAME","value":"k8s-dev"}},{"name":"Service","variableValue":{"type":"NAME","value":"guestbook"}},{"name":"skipCV","variableValue":{"type":"NAME","value":"true"}}]}}}'