Configure External Automation (Webhook-Based) Approval in your Deployment Config File

Configure external automation (webhook-based) approval in your Armory CD-as-a-Service app deployment process.

Before you begin

You have read the External Automation Overview guide, which explains how webhook-based approval works in CD-as-a-Service.

In order to configure webhook-based approval, you should have the following:

  1. The URL to trigger your external job

  2. Client Credentials (Client ID and Client Secret) with Deployments Full Access permission. You need these to fetch an OATH token to use in the callback that sends the job result to CD-as-a-Service.

    Show me how
    1. Access the CD-as-a-Service Console.
    2. Go to the Configuration tab.
    3. If you have more than one tenant, make sure you select the desired tenant in the User context menu.
    4. In the left navigation menu, select Access Management > Client Credentials.
    5. In the upper right corner, select New Credential.
    6. Create a credential for your RNA. Use a descriptive name for the credential that matches what it is being used for. For example, name the credentials the same as the account name you assigned the target deployment cluster if creating a credential for an Remote Network Agent (RNA).
    7. Select an RBAC role from the Select Roles list. You must assign an RBAC role in order for the credential to access CD-as-a-Service.
      • If the credential for is a Remote Network Agent, select Remote Network Agent.
      • If you plan to use the credential to deploy from a GitHub Action or similar tool, select Deployments Full Access.
    8. Note the values for both Client ID and Client Secret. You need these values when configuring the RNA or any other service that you want to grant access to. Make sure to store the secret somewhere safe. You are not shown the value again.

Steps to use webhook-based approval

  1. Create your external job and note the URL to trigger the job

    • Your job does not have to be accessible from the internet. CD-as-a-Service can use your Remote Network Agent as a proxy.
    • You can send context variables from CD-as-a-Service to your job.
    • The job result should be a boolean pass or fail.
  2. Configure the webhook in your deployment config file.

    • You define the callbackUri that CD-as-a-Service passes in the request to trigger your job.
    • You can include context variables for CD-as-a-Service to pass to your job.
  3. Configure your external job to send the result to CD-as-a-Service.

    • Extract the callbackUri.
    • Fetch an OAUTH token from CD-as-a-Service.
    • Send the job result to the callback URI, including the OAUTH token.

Create your external job

How you create your job varies from system to system and is beyond the scope of this guide.

A few things to keep in mind:

  • You can send send context variables from CD-as-a-Service to your job.
  • The result that you send to CD-as-a-Service is a boolean. See the Configure your external job section later in this guide for callback format.

Configure the webhoook in your deployment config file

In your deployment file, you configure your webhook by adding a top-level webhooks section with the following information:

webhooks:
  - name: <webhook-name>
    method: <endpoint-method-type>
    uriTemplate: <endpoint-uri>
    networkMode: <network-mode>
    agentIdentifier: <remote-network-agent-id>
    headers:
      - key: Authorization
        value: <auth-type-and-value>
    bodyTemplate:
      inline: >-
      {
      }      
    retryCount: <num-retries>
  • name: the unique name of your webhook

  • method: (Required) REST API method type of the webhook

  • uriTemplate: (Required) webhook URL; supports placeholders that are replaced at runtime

  • networkMode: (Optional; Default: direct) direct or remoteNetworkAgent; direct means a direct connection to the internet; if your webhook is not internet-accessible, use the remoteNetworkAgent as a proxy.

  • agentIdentifier: (Optional) Use when networkMode is remoteNetworkAgent; the Remote Network Agent identifier to use as a proxy; the identifier must match the Agent Identifier value listed on the Agents UI screen; if not specified, Armory CD-as-a-Service uses the Remote Network Agent associated with the environment account.

  • headers: (Optional) Request headers; the Authorization header is required if your webhook requires authorization. Also supports use of context and armory provided variables.

  • bodyTemplate: (Optional) the body of the REST API request; the inline content depends on the endpoint you are calling.

  • retryCount: (Optional; Default: 0) if the first connection attempt fails, the number of retries before failing and declaring that the webhook cannot be reached.

  • disableCallback: (Optional; Default: false) if set to true, Armory CD-as-a-Service does not wait for a callback before moving on to the next deployment step.

Callback URI

You must pass the callback URI as {{armory.callbackUri}}/callback. Armory CD-as-a-Service generates the value for armory.callbackUri and fills it in at runtime.

Where you configure the callback URI depends on your external automation tool. If you use a GitHub workflow, for example, you configure the callback URI in the inline section of the bodyTemplate.

Webhooks context variables

You can use variables in the webhook templates you define in the webhooks block of your deployment for the follow fields: webhooks.[].uriTemplate, webhooks.[].headers, and webhooks.[].bodyTemplate.

Armory-provided context variables

Armory provides the following variables for every webhook execution:

VariableAnnotationEnvironment variableNotes
applicationNamedeploy.armory.io/applicationARMORY_APPLICATION_NAMEAdded as annotation resources and as environment variables on pods*
deploymentIddeploy.armory.io/deployment-idARMORY_DEPLOYMENT_IDAdded as annotation resources and as environment variables on pods*
environmentNamedeploy.armory.io/environmentARMORY_ENVIRONMENT_NAMEAdded as annotation resources and as environment variables on pods*
replicaSetNamedeploy.armory.io/replica-set-nameARMORY_REPLICA_SET_NAMEAdded as annotation resources and as environment variables on pods*
accountName--The name of the account (or agentIdentifier) used to execute the deployment
namespace--The namespace resources are being deployed to

Prefix the variable with armory and surround with {{}}. For example, to use applicationName, add {{armory.applicationName}} to the webhook query template.

Custom context variables

Add your custom variables to the strategies.<strategyName>.canary.steps.runWebhook.context section of your deployment file:

strategies:
  ...
    canary:
      steps:
        ...
        - runWebhook:
            name: <webhookName>
            ...
            context:
              <variableName>: <variableValue>

You need to configure your custom variables for each webhook step that references them.

In supported webhook fields, you reference them with the following format: {{context.<variableName>}}. For example, if you create a variable called uri for webhooks.[].uriTemplate, you reference it in webhooks.[].bodyTemplate with {{context.uri}}, so that uriTemplate: {{context.uri}} is the address the webhook calls.

Configuration examples

The first example configures a GitHub webhook that uses token authorization, with the token value configured as a CD-as-a-Service secret. This webhook requires you to pass the callback URI in the request body. The payload also contains context variables that you pass in when invoking the webhook in your deployment file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
webhooks:
  - name: myWebhook
    method: POST
    uriTemplate: https://api.github.com/repos/armory/docs-cdaas-sample/dispatches
    networkMode: direct
    headers:
      - key: Authorization
        value: token {{secrets.github_token}}
      - key: Content-Type
        value: application/json
    bodyTemplate:
      inline:  >-
        {
        "event_type": "webhookCallback",
        "client_payload": {
            "callbackUri": "{{armory.callbackUri}}/callback"
            "environment": "{{armory.environmentName}}"
            "applicationName": "{{armory.applicationName}}"
            "replicaSetName": "{{armory.replicaSetName}}"
            }
        }        
    retryCount: 3

The second example configures a webhook that is not accessible from the internet. The networkMode is set to remoteNetworkAgent and the agentIdentifier specifies which Remote Network Agent to use. The agentIdentifier value must match the Agent Identifier value listed on the Agents UI screen. The Authorization Bearer value is configured as a CD-as-a-Service secret. Note that in this example, the callback URI is passed in the header.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
webhooks:
  - name: integration-tests
    method: POST
    uriTemplate: https://integrations.armory.io/tests/
    networkMode: remoteNetworkAgent
    agentIdentifier: test-rna
    headers:
      - key: Authorization
        value: Bearer {{secrets.test_token}}
      - key: CallbackURI
        value: {{armory.callbackUri}}/callback
      - key: Content-Type
        value: application/json
      - key: environment
        value: {{context.environment}}
      - key: applicationName
        value: {{armory.applicationName}}
      - key: replicaSetName
        value: {{armory.replicaSetName}}
    retryCount: 5

Trigger a webhook

You can trigger a webhook from the following areas:

  • Deployment constraints: beforeDeployment and afterDeployment
  • A canary step within a canary strategy
  • The redirectTrafficAfter section of a blue/green strategy

You add a runWebhooks section where you want to trigger the webhook.

- runWebhook:
    name: <webhook-name>
    context:
        myCustomKey: myCustomValue
  • name: (Required) webhook name; must match the name you gave your webhook in the webhooks configuration section.
  • context: (Optional) dictionary; declare values to use in templates or headers.

Deployment constraints

Before deployment

In this example, you have a webhook named Update-Database-Schema. You want to trigger this webhook before your app gets deployed. So you trigger the webhook in the beforeDeployment constraint of your environment deployment.

1
2
3
4
5
6
7
8
9
targets:
  dev:
    account: dev-cluster
    namespace: myApp-dev
    strategy: rolling-canary
    constraints:
      beforeDeployment:
        - runWebhook:
            name: Update-Database-Schema

App deployment proceeds only if the Update-Database-Schema callback sends a “success: true” message.

After deployment

In this example, you have a webhook named Run-Integration-Tests. You want to trigger this webhook after your app has been deployed to staging but before it gets deployed to production. So you trigger the webhook in the afterDeployment constraint of your staging environment deployment.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
targets:
  staging:
    account: staging-cluster
    namespace: myApp-staging
    strategy: rolling-canary
    constraints:
      afterDeployment:
        - runWebhook:
            name: Run-Integration-Tests
            context:
              environment: staging
  prod:
    account: prod-cluster
    namespace: myApp-prod
    strategy: rolling-canary
    constraints:
      dependsOn: ["staging"]

Deployment to production proceeds only if the Run-Integration-Tests callback sends a “success: true” message.

Blue/Green strategy

In this example, there is a security-scan webhook that scans your deployed app. You have a blue/green deployment strategy in which you want to run that security scan on the preview version of your app before switching traffic to it. You add the runWebhook section to the redirectTrafficAfter section in your blue/green strategy configuration.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
strategies:
  myBlueGreen:
    blueGreen:
      activeService: myApp-external
      previewService: myApp-preview
      redirectTrafficAfter:
        - analysis:
            interval: 7
            units: seconds
            numberOfJudgmentRuns: 1
            rollBackMode: manual
            rollForwardMode: automatic
            queries:
              - avgCPUUsage-pass
        - runWebhook:
            name: security-scan

Since tasks in the redirectTrafficAfter section run in parallel, both tasks in this example must be successful for deployment to continue. If the analysis task fails, rollback is manual. If the runWebhook task fails, rollback is automatic.

Canary strategy

In this example, there is a system-health webhook that you want to trigger as part of your canary strategy. Add the runWebhook section to your steps configuration.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
strategies:
  canary-rolling:
    canary:
      steps:
        - setWeight:
            weight: 25
        - runWebhook:
            name: system-health
            context:
              environment: staging

Configure your external job’s callback

After you have configured your webhook in your deployment config file, you should configure the callback in your job.

  1. Extract the callback URI from the HTTP Request that CD-as-a-Service sent to trigger your job. How you do this depends on what external automation tool you use.

  2. Fetch an OAUTH token from CD-as-a-Service.

    Replace <CLIENT-ID> and <CLIENT-SECRET> with your values.

    Request format:

     curl --request POST \
     --url https://auth.cloud.armory.io/oauth/token \
     --header 'Content-Type: application/x-www-form-urlencoded' \
     --data data=audience=https://api.cloud.armory.io \
     --data grant_type=client_credentials \
     --data client_id=<CLIENT-ID> \
     --data client_secret=<CLIENT-SECRET>
    

    Example response:

    {
    "access_token": "<very long access token>",
    "expires_in": 86400,
    "token_type": "Bearer"
    }
    
  3. Configure the callback

    curl --request POST \
    --url '<CALLBACK-URI>' \
    --header 'Authorization: Bearer <OAUTH_TOKEN>' \
    --header 'Content-Type: application/json' \
    --data '{"success": <true|false>, "mdMessage": "<MESSAGE>"}'
    
    • <CALLBACK-URI>: Replace with the callback URI you extracted from the HTTP Request that CD-as-a-Service sent to trigger your job.
    • <OAUTH_TOKEN>: Replace with the OAUTH token you fetched from CD-as-a-Service.
    • data dictionary job outcome: CD-as-a-Service looks for a success value of true or false to determine the webhook’s success or failure. mdMessage should contain a user-friendly message for CD-as-a-Service to display in the UI and write to logs.

What’s next


Last modified October 31, 2023: (f370fe9)