Creating a CI/CD Pipeline for Twilio Flex Plugins with Azure DevOps

Developers creating a process pipeline
June 15, 2023
Written by
Reviewed by
Abby Ford
Twilion

Working with Twilio Flex is a fun experience, especially when you have made all the feature customizations you want and are ready to deploy your solution. While Twilio Flex Plugins CLI provides an easy way to deploy and release your changes, it can become challenging to manage when there are multiple developers in the team all deploying various versions of your plugin from their local machines.

In this blog post, I will provide a step-by-step guide on how to configure your Twilio Flex plugin deployment pipeline with the help of Azure DevOps.

Prerequisites

The rest of this blog post will assume you have all of the prerequisites up and running.

Twilio Flex Plugin

For the Azure DevOps configuration, it is important to have a Flex Plugin to deploy to your Twilio account via a deployment pipeline. For this blog post, I’ll use a simple Flex Plugin for the outbound dialer. The reason I’m using this example is because it has both frontend (React) and backend (Twilio serverless functions) that need to be deployed.  

Not all Flex plugins will have both the frontend and backend code. The above repository is a good example because it has both, but you are welcome to customize the deployment pipeline to fit your needs.

Azure DevOps configuration

Before we jump into Azure DevOps configuration, let’s briefly talk about Azure DevOps itself. The official documentation can be found here. In a nutshell, Azure DevOps is a Microsoft product that offers various services to bring together developers, project managers, and contributors to develop software and, notably, source control and deployment pipelines. These services are crucial to configure our CI/CD deployment pipeline for Twilio Flex plugins.

Step 1: Source control (Azure Git)

The first step is to configure the code source control in Azure DevOps, which uses Git. This means you have to migrate code from GitHub over to Azure DevOps. Let’s walk through this process together.

In your GitHub, navigate to your repository, and click on Code and then copy the HTTPS URL:  

An image of the GitHub repository screen with a red arrow pointing to "Code" and another arrow pointing to the HTTPS URL

In your Azure DevOps, navigate to Repos, select the current active repo, and then click on the Import Repository option.

An image of the Azure DevOps screen with red arrow pointing to "Repos," a red arrow pointing to "active repo," and another arrow point to "Import Repository."

A new side pane will appear where you can define the Repository type, Clone URL, and the Name of the new repository. For the Clone URL, use the URL from GitHub you just copied:

An image of the Import a Git Repository screen in Azure DevOps.

Once all the information has been entered, click on Import. After a few minutes, your GitHub repository should be successfully imported into Azure DevOps.

Depending on your use case, you might not have a GitHub repository to begin with, but the migration process should be fairly similar. For more details, please visit the official Azure DevOps documentation.

Step 2: Twilio account access

As a security best practice, I recommended that you create a custom API key for your CI/CD operations. All examples in this guide will use an API key for authentication.

It is possible to use the Twilio Account SID and the Authentication Token directly with your pipelines; however, it is discouraged to use your main credentials in an event of a security breach.

Step 3: Deployment pipeline

The next step in the process is to configure a deployment pipeline. It is worth noting that Azure DevOps has multiple ways of configuring a pipeline. If you are using the Classic UI editor, you need to define a build and release part of your pipeline separately. In this example, I will leverage a YAML file to create the build, release, and environment configuration all in a single YAML file.

You begin this step by navigating to the Pipelines section and then clicking on the Create Pipeline button:

An image of the Create your first Pipeline screen in Azure DevOps with a red arrow point to "Pipeline."

Once you have clicked on the Create Pipeline button, a new view will appear. On that view, select where the code is located. For this scenario, select Azure Repos Git:

An image of the "Where is your code?" screen in Azure DevOps with red arrow pointing to "Azure Repos Git."

As you can see, it is also possible to link other source controls, such as GitHub, directly to Azure DevOps.

In the next view, select the proper repository for the Flex Plugin code you want to deploy. For this use case, that is the Flex-Dialpad-pipeline-example repository:

An image of the "Select a repository" in Azure DevOps with red arrow to the imported code.

Next, you will configure the pipeline by selecting the appropriate template. Since there are no official Twilio Flex templates, select the Starter pipeline and you will define everything yourself:

An image of the "Configure your pipeline" screen in Azure DevOps. A red arrow is pointing to "Starter Pipeline."

Now, this is where things get interesting. The next screen will allow you to edit the YAML file directly in the browser. The final YAML version needed for this Flex plugin can be found here:


# Twilio Flex Plugin YAML Pipeline Example
# This YAML file provides a CI/CD definition for deploying a Twilio Flex Plugin.
# The pipeline includes both the Flex frontend and backend deployment steps.
# Disclaimer: This pipeline is for educational purposes only
# Disclaimer: This software is to be considered "sample code", a Type B Deliverable, and is delivered "as-is" to the user.
# Disclaimer: Twilio bears no responsibility to support the use or implementation of this software.

# The trigger definition defines when the pipeline should get executed/triggered.
# For this use case, the pipeline will execute only if there is a change on the main branch.
# Additional documentation can be found here: https://learn.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/trigger?view=azure-pipelines
trigger:
 - main

# Stages are a collection of related jobs. By default, stages run sequentially. A stage is a logical boundary in the pipeline.
# Additional documentation can be found here: https://learn.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/stages-stage?view=azure-pipelines
stages:
 - stage: Flex_UI
   # You can organize your pipeline into jobs. Every pipeline has at least one job. A job is a series of steps that run sequentially as a unit.
   # In other words, a job is the smallest unit of work that can be scheduled to run.
   # Additional documentation can be found here: https://learn.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml
   jobs:
     - job: Flex_UI_Bash_Script
       # The pool keyword specifies which pool to use for a job of the pipeline. A pool specification also holds information about the job's strategy for running.
       # The pool keyword uses the vmImage string which is the name of the VM image you want to use; valid only in the Microsoft-hosted pool.
       # Additional documentation can be found here: https://learn.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/pool?view=azure-pipelines
       pool:
         vmImage: ubuntu-latest
       # Steps are a linear sequence of operations that make up a job.
       # Additional documentation can be found here: https://learn.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/steps?view=azure-pipelines
       steps:
         # A task step runs a task. Each task has various configuration options depending on the task itself.
         # Additional documentation can be found here: https://learn.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/steps-task?view=azure-pipelines
         - task: NodeTool@0
           inputs:
             versionSpec: "16.x"
         - task: Bash@3
           inputs:
             filePath: "./scripts/flex-ui-pipeline.sh"
           env:
             TWILIO_ACCOUNT_SID: $(TWILIO_ACCOUNT_SID)
             TWILIO_API_KEY: $(TWILIO_API_KEY)
             TWILIO_API_SECRET: $(TWILIO_API_SECRET)
             REACT_APP_TWILIO_SERVERLESS_SERVICE: $(REACT_APP_TWILIO_SERVERLESS_SERVICE)
 - stage: Serverless_functions
   jobs:
     - job: Serverless_Bash_Script
       pool:
         vmImage: ubuntu-latest
       steps:
         - task: NodeTool@0
           inputs:
             versionSpec: "16.x"
         - task: Bash@3
           inputs:
             filePath: "./scripts/flex-serverless-pipeline.sh"
           env:
             TWILIO_ACCOUNT_SID: $(TWILIO_ACCOUNT_SID)
             TWILIO_API_KEY: $(TWILIO_API_KEY)
             TWILIO_API_SECRET: $(TWILIO_API_SECRET)

Once you’ve copied the contents, paste it into the browser editor, and click Save:

An image of the YAML file code copied into Azure DevOps with red arrow pointing to "Save."

If this is your first pipeline, the Save button will say Save and Run. You can click on the ellipsis to the right and click Save from there.

The YAML file itself is heavily commented on and it provides detailed information on every step of the deployment process. Also keep in mind that this pipeline will run every time there are changes on the main branch.

Step 4: Pipeline variables

There are several ways to define variables inside of Azure DevOps. For this example, I’ve decided to use the single-scoped pipeline variables because I prefer to have a simplistic, self-contained pipeline.

To add custom variables, open your newly created YAML pipeline and click on Variables:

An image of the YAML pipeline in Azure DevOps with red arrow pointing to "Variables."

A side panel will present itself with an option to add or modify environment variables:

An image of the "Variables" side panel in Azure DevOps.

For this pipeline, you will need the following variables which you can get from the Twilio Console:

  • TWILIO_ACCOUNT_SID
  • TWILIO_API_KEY
  • TWILIO_API_SECRET
  • REACT_APP_TWILIO_SERVERLESS_SERVICE (this is the URL of your Twilio Serverless Service which gets created after deploying your Functions)

Step 5: Bash scripts

For people familiar with YAML configuration, you will notice this is a straight forward deployment file. It basically installs the necessary Node.js version and runs a bash script. The bash scripts for this deployment can be found below. Similar to the YAML file, it is heavily commented on to provide step-by-step instructions and explanations. There are two Bash scripts here: one for the Flex UI plugin deployment and one for the Twilio Serverless Function deployment.

Flex UI Plugin deployment script: flex-ui-pipeline.sh

#!/bin/bash

# Exit on error
set -e

# Defining bash script-level environment variables. The Twilio CLI will auto-magically pickup the variables
export TWILIO_ACCOUNT_SID=$TWILIO_ACCOUNT_SID
export TWILIO_API_KEY=$TWILIO_API_KEY
export TWILIO_API_SECRET=$TWILIO_API_SECRET
export BUILD_NUMBER=$BUILD_BUILDNUMBER
export PLUGIN_NAME="flex-dialpad-queue-list"
export FLEX_UI_FOLDER="flex-dialpad-queue-list"
export REACT_APP_TWILIO_SERVERLESS_SERVICE=$REACT_APP_TWILIO_SERVERLESS_SERVICE

# Check if running on a Debian-based system
if ! command -v apt >/dev/null 2>&1; then
 echo "This script requires a Debian-based system with 'apt' package manager."
 exit 1
fi

# Update package list
echo "Updating package list..."
sudo apt update

# Install Twilio CLI if not already installed
if ! command -v twilio >/dev/null 2>&1; then
 echo "Installing Twilio CLI with 'apt'..."
 curl -s https://twilio-cli-prod.s3.amazonaws.com/twilio_pub.asc | sudo apt-key add -
 echo "deb https://twilio-cli-prod.s3.amazonaws.com/apt/ /" | sudo tee /etc/apt/sources.list.d/twilio.list
 sudo apt update
 sudo apt install -y twilio
fi

# Install the Twilio Flex plugin to the Twilio CLI
echo "Installing the Twilio Flex plugin to the Twilio CLI..."
twilio plugins:install @twilio-labs/plugin-flex

# Navigate to the Flex plugin UI folder
echo "Entering Flex Plugin UI folder..."
cd $FLEX_UI_FOLDER

# Install npm plugin dependencies
echo "Installing NPM dependencies..."
npm i

# Deploy the Flex plugin
echo "Deploying the Flex plugin..."
twilio flex:plugins:deploy --version "1.0.0-$BUILD_NUMBER" --description "Flex Dialpad Customization" --changelog "CI/CD initatied changes"

# Release the Flex plugin
echo "Releasing the Flex plugin..."
twilio flex:plugins:release --plugin "$PLUGIN_NAME@1.0.0-$BUILD_NUMBER" --name "Autogenerated Release $BUILD_NUMBER" --description "Flex Dialpad CI/CD release"

Twilio Serverless Functions deployment script: flex-serverless-pipeline.sh

#!/bin/bash

# Exit on error
set -e

# Defining bash script-level environment variables. The Twilio CLI will auto-magically pickup the variables
export TWILIO_ACCOUNT_SID=$TWILIO_ACCOUNT_SID
export TWILIO_API_KEY=$TWILIO_API_KEY
export TWILIO_API_SECRET=$TWILIO_API_SECRET
export SERVERLESS_FOLDER="serverless"
export SERVERLESS_SERVICE_NAME="ado-pipeline"
export ENV_FILE_LOCATION=".env.example"

# Check if running on a Debian-based system
if ! command -v apt >/dev/null 2>&1; then
 echo "This script requires a Debian-based system with 'apt' package manager."
 exit 1
fi

# Update package list
echo "Updating package list..."
sudo apt update

# Install Twilio CLI if not already installed
if ! command -v twilio >/dev/null 2>&1; then
 echo "Installing Twilio CLI with 'apt'..."
 curl -s https://twilio-cli-prod.s3.amazonaws.com/twilio_pub.asc | sudo apt-key add -
 echo "deb https://twilio-cli-prod.s3.amazonaws.com/apt/ /" | sudo tee /etc/apt/sources.list.d/twilio.list
 sudo apt update
 sudo apt install -y twilio
fi

# Install the Twilio Serverless CLI
echo "Installing the Twilio Serverless CLI..."
twilio plugins:install @twilio-labs/plugin-serverless

# Navigate to the Flex Serverless folder
echo "Entering Serverless folder..."
cd $SERVERLESS_FOLDER

# Install npm plugin dependencies
echo "Installing NPM dependencies..."
npm i

# Deploy the Twilio Serverless Functions
echo "Deploying Twilio Serverless Functions..."
twilio serverless:deploy -n $SERVERLESS_SERVICE_NAME --override-existing-project --env $ENV_FILE_LOCATION

Summary of how the deployment pipeline works

Alright, we had a lot of configuration steps. Let's do an overview of the CI/CD setup we have here.

  • The code is hosted in the Azure DevOps Git alongside our YAML deployment definition and the bash script.
  • Once there is a change in the main branch, the pipeline will execute and deploy the latest version of the code to Twilio.
  • This deployment process is executed by the bash scripts.
  • One script deploys the Flex Plugin UI and the other deploys the serverless backend.
  • The bash scripts are taking Azure DevOps pipeline variables that identify the proper Twilio account used for the Flex deployment.

For additional environments, simply create another deployment pipeline in Azure DevOps and pass the appropriate variables.

Achieve greater control over your Flex environments

Congratulations, you have successfully created an Azure DevOps deployment pipeline for your Twilio Flex implementation. This process will empower your development team with greater control over different environments. They will be able to easily ship new versions of Flex plugins, keep track of deployments in a single place, and move faster without worrying who deployed what, when, and where.

Please keep in mind that this is just one of the many ways a deployment pipeline can be configured, so make sure you modify the process according to your business needs and engineering best practices.

If your team would like assistance with any aspect of your solution, Twilio Professional Services is here to help. Reach out to learn more about our offerings.

I can’t wait to see what you build!

Matija Vrzan is a Senior Solutions Architect at Twilio’s NAMER Professional Services Team, changing the world one contact center at a time. He can be reached at mvrzan [at] twilio.com.