Azure DevOps Pipelines with multiple schedules

Azure DevOps Pipelines with multiple schedules

Context

A client is aware that significant cost savings could be realised by a scheduled manipulation of some of their deployed resources and services. Their working week leads to predictable hot and cold periods in the utilisation of their build services and pre-production environments in which scaling up and down could take place.

The resources in question don’t have any other suitable means to scale based on demand.

The client wants an Azure DevOps native solution, and everything is deployed by ADO pipelines.

The goal

I would like to provide a single pipeline, which executes on a defined schedule, and applies some settings appropriate for that time. Sort of like an old programmable thermostat.

Simplistically, it’s likely to be “run a scale up” or “run a scale down”.

ADO provides scheduled triggers, defined using a cron syntax. You can set a pipeline to run whenever you want. You can set multiple cron schedules for a single job.

I’m picturing a pipeline something like this:

# Two trigger definitions
schedules:
  - cron: "0 18 * * Mon-Fri"
    displayName: Every weekday at 1800
    branches:
      include:
        - master
    always: true

  - cron: "0 8 * * Mon-Fri"
    displayName: Every weekday at 0800
    branches:
      include:
        - master
    always: true

stages:
  - stage: Run_task
    jobs:
      - job: Task
        pool: hubAgents
        steps:
          - bash: # do different stuff depending on which cron schedule triggered us

ADO Limitations

However, there’s a painful limitation: you cannot - within the YAML schema - set parameters or variables per trigger. Moreover, a running pipeline doesn’t “know” which particular cron schedule triggered it.

ADO exposes many built in variables, but it strangely seems that inspecting the trigger is one of them.

You could create multiple pipelines - one for each schedule - with some hardcoded set of arguments to pass to your pipeline payload template, but that creates duplication, and will get unmanageable quickly. I am also working in a Frontier Digital ADO framework where GitOps rules all, and there’s automation both upstream and downstream creating and consuming the config - even this pipeline will be programmatically created!

REST API to the rescue

There is however the trusty ADO REST API. It has all the trigger information - what we need is for our pipeline to query the API for information about itself as it’s running.

We can see in the docs that the builds/build endpoint returns an object containing what we need:

Here’s an example of the API response from the endpoints that we’re interested in

{
  // ...
  "triggerInfo": {
    "scheduleName": "Every weekday at 1800"
  },
  // ...
}

Where scheduleName contains the displayName for the triggering schedule.

This can be done with a relatively simple bash task using curl, piping the returned JSON into jq to allow us to pull out what we’re after and set a variable.

This task is completely portable with no parameters needed as all settings can be pulled from built in variables!

The task

steps:
  - bash: |
      set -euo pipefail
      schedule_name=$( \
        curl \
          -H "Content-Type: application/json" \
          -s \
          -u ":$(System.AccessToken)" `# authenticate with Access Token` \
          "$(System.CollectionUri)/$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)?api-version=7.0" |
        jq --raw-output '.triggerInfo.scheduleName' \
      )
      echo "##vso[task.setvariable variable=scheduleName]$schedule_name"
      echo "##vso[task.setvariable variable=scheduleName;isOutput=true]$schedule_name"      
    name: getScheduleName

Building up the API endpoint

  • System.CollectionUri: https://dev.azure.com/my-org/
  • System.TeamProject: my-project
  • Build.BuildId: The Id for this running pipeline

jq options

  • --raw-output: removes quotes around the returned value

The task sets a variable $schedule_name (and an output variable too, for multi stage goodness), which can be passed on to whatever the main payload of the pipeline is.

In my situation, I use it to filter a configuration file. The payload itself doesn’t need to know if it’s scaling up or down, it’s just applying whatever the filtered configuration is!

Unfortunately this variable is obviously only available at run time, so can’t be used as a template variable for ADO native template conditions etc. You have to make some switching within your main payload.

Non-scheduled pipeline runs

Manual runs of the pipeline won’t have a scheduleName set. The returned object looks like this:

{
  // ...
  "triggerInfo": {},
  // ...
}

In this instance, our task returns a string with the literal value null. We can handle this special case in our code.

Summary

It would be great if ADO just exposed this information for us, but at least this way we can work around it and have one scheduled pipeline that handles all the cases.

Keep reading...
blog-post-image
Secrets as code with Mozilla SOPS

Secrets as code with Mozilla SOPS The problem Modern cloud platforms move at speed. The rate of change required to stay competitive in any market requires development teams to ship new features at a high velocity. When releasing new features rapidly, it is often difficult to maintain and control the configuration and secrets that are required to make your application work. Part of this problem has been solved by moving configuration to code repositories therefore leveraging all of the advantages of git.

blog-post-image
Autoscaling Kuberenetes workloads with custom schedules

Autoscaling Kuberenetes workloads with custom schedules Context Would you like to know how we cut annual cloud costs by over 45% for one of our major financial services clients? Read on! Following on from a previous blog post where we discovered how to make use of custom schedules in ADO pipelines, we decided to use the same mechanism as part of our FinOps practices for one of our major clients.

blog-post-image
The misconception of simplicity

Cloud platforms are increasing in popularity, is the current perception correct?

blog-post-image
Azure DevOps Pipelines with multiple schedules

Azure DevOps Pipelines with multiple schedules Context A client is aware that significant cost savings could be realised by a scheduled manipulation of some of their deployed resources and services. Their working week leads to predictable hot and cold periods in the utilisation of their build services and pre-production environments in which scaling up and down could take place. The resources in question don’t have any other suitable means to scale based on demand.

blog-post-image
Why Frontier Digital?

It’s time for your business to move to the Cloud, but how do you ensure you get there safely, securely and without it running away with the pennies?