# Automating SailPoint SaaS Connector Deployments with GitLab CI/CD

**URL:** <https://developer.sailpoint.com/discuss/t/automating-sailpoint-saas-connector-deployments-with-gitlab-ci-cd/128400>\
**Category:** Community Blog\
**Tags:** sailpoint-identity-security, saas-connector\
**Created:** [May 14, 2025, 12:37pm UTC](https://developer.sailpoint.com/discuss/t/automating-sailpoint-saas-connector-deployments-with-gitlab-ci-cd/128400 "2025-05-14T12:37:30Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![adunker](https://sea1.discourse-cdn.com/sailpoint/discuss/user_avatar/developer.sailpoint.com/adunker/32/2664_2.png) [@adunker](https://developer.sailpoint.com/discuss/u/adunker)\
**Post date:** [May 14, 2025, 12:37pm UTC](https://developer.sailpoint.com/discuss/t/automating-sailpoint-saas-connector-deployments-with-gitlab-ci-cd/128400/1 "2025-05-14T12:37:30Z")

</div>

![Tutorials-Blue](https://global.discourse-cdn.com/sailpoint/original/3X/e/d/ed7f2f45abe77c319d4dbffb8c52a9d7b5dad448.png)

# Automating SailPoint SaaS Connector Deployments with GitLab CI/CD

Continuous Integration and Continuous Deployment (CI/CD) practices are essential for modern software development, and SailPoint SaaS connector development is no exception. Automating the build, test, and deployment process saves time, reduces errors, and ensures consistency. GitLab CI/CD is a powerful tool that allows you to define and run your pipelines directly from your GitLab repository.

This post will guide you through setting up a GitLab CI/CD pipeline for your SailPoint SaaS connectors using a practical example configuration file (`.gitlab-ci.yml`).

I will note - I do not consider myself a GitLab or CI/CD expert by any means - if you have any suggestions or ideas for improvement, please share!

Download the full file here: [gitlab-ci.yml](https://developer.sailpoint.com/discuss/uploads/short-url/m3787ZeCNt8ZOkfMXqHf2g29rin.yml) (4.1 KB)

### Prerequisites

Before you begin, ensure you have the following set up:

- **A GitLab Runner:** This is the application that runs your CI/CD jobs. You can use shared runners provided by GitLab or set up your own specific runner.
- **A GitLab Repository:** Your SailPoint SaaS connector code should be hosted in a GitLab repository.
- **SailPoint Tenant Credentials:** You’ll need API credentials (Client ID and Client Secret) for your SailPoint tenant with permissions to manage connectors. These will be stored securely as GitLab CI/CD variables.

### What is GitLab CI/CD?

GitLab CI/CD is a part of GitLab used to automate the stages of your software development lifecycle. You define a pipeline in a YAML file named `.gitlab-ci.yml` located in the root of your repository. This file tells the GitLab Runner (the agent that executes your jobs) what tasks to perform. These tasks are grouped into **stages** , and the stages run in a defined order. Common stages include building the code, running tests, and deploying the application.

### The `.gitlab-ci.yml` File Explained

Let’s break down an example `.gitlab-ci.yml` file designed for a SailPoint SaaS connector. This pipeline automates the process of building the connector package, running tests, and deploying it to different SailPoint environments (dev, test, production) based on the Git branch.

```auto
# .gitlab-ci.yml (Excerpt - Full file provided previously)
stages:
  - build
  - test
  - deploy

variables:
  SAIL_BASE_URL: ${TENANT_BASE_URL}
  SAIL_CLIENT_ID: ${TENANT_CLIENT_ID}
  SAIL_CLIENT_SECRET: ${TENANT_CLIENT_SECRET}
  CONNECTOR_ALIAS: "${TENANT_CONN_NAME}"
  CLI_VERSION: "2.1.10"
  NODE_VERSION: "18"

# ... rest of the file

```

#### `stages`

```auto
stages:
  - build
  - test
  - deploy

```

This section defines the sequence of stages in our pipeline. Jobs assigned to the `build` stage will run first. Once all `build` jobs succeed, jobs in the `test` stage will run. Finally, if `test` jobs are successful, the `deploy` stage jobs will execute. This ensures a logical flow where code is built before it’s tested, and tested before it’s deployed.

#### `variables`

```auto
variables:
  SAIL_BASE_URL: ${TENANT_BASE_URL}
  SAIL_CLIENT_ID: ${TENANT_CLIENT_ID}
  SAIL_CLIENT_SECRET: ${TENANT_CLIENT_SECRET}
  CONNECTOR_ALIAS: "${TENANT_CONN_NAME}"
  CLI_VERSION: "2.1.10"
  NODE_VERSION: "18"

```

Here, we define variables that can be used throughout the pipeline jobs.

- `SAIL_BASE_URL`, `SAIL_CLIENT_ID`, `SAIL_CLIENT_SECRET`: These are crucial for authenticating with your SailPoint tenant using the SailPoint CLI. **Crucially** , these values use GitLab’s variable syntax (`${VARIABLE_NAME}`). You should define `TENANT_BASE_URL`, `TENANT_CLIENT_ID`, and `TENANT_CLIENT_SECRET` as protected CI/CD variables within your GitLab project settings (Settings → CI/CD → Variables) rather than hardcoding sensitive credentials here.
- `CONNECTOR_ALIAS`: This specifies the alias for your connector within SailPoint. Similar to the credentials, `TENANT_CONN_NAME` should be defined in GitLab’s CI/CD variables, potentially varying per environment if needed.
- `CLI_VERSION`: Specifies the version of the SailPoint CLI to use during deployment.
- `NODE_VERSION`: Defines the Node.js version for the Docker images used in the `build` and `test` stages.

#### `build` Job

```auto
build:
  stage: build
  image: node:${NODE_VERSION}
  script:
    - npm ci
    - npm run pack-zip
  artifacts:
    paths:
      - dist/
    expire_in: 1 week
  rules:
    - if: $CI_COMMIT_BRANCH == "dev"
    - if: $CI_COMMIT_BRANCH == "test"
    - if: $CI_COMMIT_BRANCH == "main"
  timeout: 10m
  retry:
    max: 2
    when:
      - runner_system_failure
      - stuck_or_timeout_failure

```

This job defines the tasks for the `build` stage.

- `stage: build`: Assigns this job to the `build` stage.
- `image: node:${NODE_VERSION}`: Specifies that this job should run in a Docker container using the official Node.js image, tagged with the version defined in our `NODE_VERSION` variable.
- `script`: Contains the commands to execute. `npm ci` installs dependencies cleanly based on `package-lock.json`, and `npm run pack-zip` (assuming you have this script defined in your `package.json`) packages your connector code into a zip file suitable for deployment.
- `artifacts`: Defines files or directories to save after the job completes successfully. Here, the `dist/` directory (containing the packaged connector zip) is saved as an artifact. These artifacts can be downloaded or used by jobs in later stages. `expire_in: 1 week` sets how long the artifacts are kept.
- `rules`: Control when the job runs. This job is configured to run only for commits on the `dev`, `test`, or `main` branches.
- `timeout`: Sets a maximum execution time for the job.
- `retry`: Configures automatic retries (up to 2 times) if the job fails due to specific runner issues.

#### `test` Job

```auto
test:
  stage: test
  image: node:${NODE_VERSION}
  script:
    - npm ci
    - npm run test
  artifacts:
    paths:
      - coverage/
    reports:
      junit:
        - junit.xml
    expire_in: 1 week
  dependencies:
    - build
  rules:
    - if: $CI_COMMIT_BRANCH == "dev"
    - if: $CI_COMMIT_BRANCH == "test"
    - if: $CI_COMMIT_BRANCH == "main"
  timeout: 10m
  retry:
    max: 2
    when:
      - runner_system_failure
      - stuck_or_timeout_failure

```

This job handles the `test` stage.

- `stage: test`, `image`, `rules`, `timeout`, `retry`: Similar configuration to the `build` job.
- `script`: Installs dependencies (`npm ci`) and runs the test suite (`npm run test` - assuming this script is defined in `package.json` and configured to output results).
- `artifacts`: Saves the `coverage/` directory. It also specifies `reports: junit: junit.xml`, allowing GitLab to parse the test results from `junit.xml` (if your test runner generates it) and display them in the GitLab UI.
- `dependencies: - build`: This explicitly states that the `test` job depends on the artifacts produced by the `build` job. While not strictly necessary here (as artifacts are passed by default between stages), it makes the dependency clear. Note: This configuration doesn’t automatically download artifacts from `build`; it primarily controls job execution order when using `needs`. Since `test` follows `build` in `stages`, the artifacts are available.

**Note: if you don’t have tests written you’ll want to modify your package.json file to have npm test run `jest --coverage --passWithNoTests`.**

#### `.deploy_template` (YAML Anchor)

```auto
.deploy_template: &deploy_template
  stage: deploy
  image: ubuntu:latest
  script:
    - apt-get update && apt-get install -y wget jq
    - wget "https://github.com/sailpoint-oss/sailpoint-cli/releases/download/${CLI_VERSION}/sail_${CLI_VERSION}_linux_amd64.deb"
    - dpkg -i sail_${CLI_VERSION}_linux_amd64.deb
    - PACKAGE_VERSION=$(jq -r .version package.json)
    - CONNECTOR_NAME=$(jq -r .name package.json)
    # Simplified check for connector existence
    - |
      CONNECTOR_EXISTS=$(sail conn list | grep -c "${CONNECTOR_ALIAS}" || true)
      echo "Found ${CONNECTOR_EXISTS} matches for connector '${CONNECTOR_ALIAS}'"
      if ["${CONNECTOR_EXISTS}" -eq 0]; then
        echo "Connector $CONNECTOR_ALIAS not found. Creating it..."
        sail conn create "$CONNECTOR_ALIAS" || echo "Failed to create connector, continuing anyway..."
      else
        echo "Connector $CONNECTOR_ALIAS already exists."
      fi
    - echo "Uploading connector..."
    - sail conn upload -c "$CONNECTOR_ALIAS" -f ./dist/$CONNECTOR_NAME-$PACKAGE_VERSION.zip./dist/$CONNECTOR_NAME-$PACKAGE_VERSION.zip
  timeout: 15m
  retry:
    max: 2
    when:
      - runner_system_failure
      - stuck_or_timeout_failure

```

This section uses a YAML feature called an **anchor** (`&deploy_template`). It defines a template named `deploy_template` (note the leading dot `.`, which prevents it from being run as a standalone job). This template contains the common configuration and script steps for deploying the connector.

- `stage: deploy`: Assigns jobs using this template to the `deploy` stage.
- `image: ubuntu:latest`: Uses a basic Ubuntu image for the deployment tasks.
- `script`: This is the core deployment logic:
  1. Updates package lists and installs necessary tools (`wget` for downloading, `jq` for parsing JSON).
  2. Downloads the specified version of the SailPoint CLI Debian package.
  3. Installs the SailPoint CLI using `dpkg`.
  4. Reads the connector `version` and `name` from `package.json` using `jq`.
  5. **Checks if a connector with the specified `CONNECTOR_ALIAS` already exists** in the target tenant using `sail conn list` and `grep`. The `|| true` prevents the pipeline from failing if `grep` finds no matches.
  6. **Conditionally creates the connector** (`sail conn create`) if it doesn’t exist. It includes `|| echo "Failed..."` to prevent the pipeline from stopping if creation fails (e.g., due to permissions), allowing the upload step to proceed, which might be desired if the connector exists but wasn’t listed correctly.
  7. **Uploads the connector package** (`sail conn upload`) using the alias (`-c`) and the path to the zipped artifact (`-f`). The artifact path is constructed using the connector name and version read from `package.json`.

- `timeout`, `retry`: Standard job control settings.

#### `deploy-dev`, `deploy-test`, `deploy-prod` Jobs

```auto
deploy-dev:
  <<: *deploy_template # Inherits from the template
  environment:
    name: dev
  rules:
    - if: $CI_COMMIT_BRANCH == "dev"
      when: on_success # Run only if previous stages succeed
  needs:
    - build
    - test

deploy-test:
  <<: *deploy_template
  environment:
    name: test
  rules:
    - if: $CI_COMMIT_BRANCH == "test"
      when: on_success
  needs:
    - build
    - test

deploy-prod:
  <<: *deploy_template
  environment:
    name: production
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: on_success
  needs:
    - build
    - test

```

These are the actual deployment jobs, one for each environment (development, testing, production).

- `<<: *deploy_template`: This uses the YAML **merge key** (`<<`) and **alias** (`*deploy_template`) to inherit all the configuration from the `.deploy_template`. This avoids repeating the script and other settings.
- `environment: name: <env_name>`: Associates the job with a GitLab Environment (e.g., `dev`, `test`, `production`). This helps track deployments in the GitLab UI.
- `rules`: Defines the specific conditions for each deployment job.
  - `deploy-dev` runs only on successful commits to the `dev` branch.
  - `deploy-test` runs only on successful commits to the `test` branch.
  - `deploy-prod` runs only on successful commits to the `main` branch.
  - `when: on_success` ensures these jobs only run if all jobs in the preceding stages (`build`, `test`) completed successfully.

- `needs: - build - test`: Specifies that these deploy jobs require both the `build` and `test` jobs to complete successfully before they can start. This allows for potential parallel execution if stages were structured differently, but here it primarily reinforces the dependency on the successful completion of prior stages and ensures artifacts are available.

### Conclusion

By implementing a GitLab CI/CD pipeline like the one described, you can significantly streamline your SailPoint SaaS connector development workflow. This automation ensures that every change pushed to your key branches is automatically built, tested, and deployed, leading to faster feedback cycles, improved quality, and more reliable deployments. Remember to configure your sensitive tenant credentials and connector aliases securely using GitLab’s CI/CD variables. Happy automating!

### Additional Resources

[Get started with GitLab CI/CD | GitLab Docs](https://docs.gitlab.com/ci/)  
[Runners | GitLab Docs](https://docs.gitlab.com/ci/runners/)

---

<div class="post-metadata">

**Author:** ![zac\_adams\_idw](https://sea1.discourse-cdn.com/sailpoint/discuss/user_avatar/developer.sailpoint.com/zac_adams_idw/32/2839_2.png) [@zac\_adams\_idw](https://developer.sailpoint.com/discuss/u/zac_adams_idw)\
**Post date:** [May 15, 2025, 3:20am UTC](https://developer.sailpoint.com/discuss/t/automating-sailpoint-saas-connector-deployments-with-gitlab-ci-cd/128400/2 "2025-05-15T03:20:49Z")

</div>

Great work @adunker !

---

<div class="post-metadata">

**Author:** ![narayanag](https://sea1.discourse-cdn.com/sailpoint/discuss/user_avatar/developer.sailpoint.com/narayanag/32/25441_2.png) [@narayanag](https://developer.sailpoint.com/discuss/u/narayanag)\
**Post date:** [May 15, 2025, 2:32pm UTC](https://developer.sailpoint.com/discuss/t/automating-sailpoint-saas-connector-deployments-with-gitlab-ci-cd/128400/3 "2025-05-15T14:32:00Z")

</div>

Thanks a lot [Alex Dunker](https://developer.sailpoint.com/discuss/u/adunker)..
