GitLab: invoking Ansible from a GitLab pipeline job

If you have a previous investment in Ansible Configuration Management for command line automation, you may now want to invoke that same logic from a GitLab CI/CD pipeline.

The cleanest way to provide Ansible to a pipeline job is to create a custom Docker image that contains all the Ansible binaries and required Galaxy modules.  The job’s “image” element will then reference this custom Docker image.

When the job is invoked from the pipeline, the custom image will be run with your git repository files fetched to the “/builds/<project-path>” directory.   The ‘script‘ element can then run ansible/ansible-playbook, using the Ansible configuration files fetched from the git repository.

Docker image with Ansible installed

I have created a GitLab docker-python-ansible-user-venv project, that takes a Debian 12 base and installs Python, Ansible and its related utilties, and several common Ansible Galaxy modules into a venv running as non-root.

This image is published to the GitLab Container Registry:

registry.gitlab.com/gitlab-pipeline-helpers/docker-python-ansible-user-venv:1.0.0

Outside of any pipeline, you can run a quick test to validate the ansible binary is available inside this image.

docker run -it --rm registry.gitlab.com/gitlab-pipeline-helpers/docker-python-ansible-user-venv:1.0.0 ansible --version

Notice this image intentionally only contains the Ansible binary utilities and modules, not any specific Ansible configuration files (ansible_inventory, roles).  These are expected to be provided externally.

Pipeline job invoking Ansible

The custom image above contains all the Ansible-related binaries, so now we must provide the other side, which are the Ansible configuration files (ansible.cfg, ansible_inventory, group_vars, host_vars, roles, etc).   These should be placed into your git source repository.

I have a example GitLab project gitlab-call-ansible-in-pipeline, that contains a full set of Ansible configuration files.

When the GitLab runner invokes our job with the custom image, it fetches the git repository files to “/builds/<project-path>”, whose exact path is the predefined pipeline variable $CI_PROJECT_DIR.  It also makes this the current directory.

Below is a simple job from my sample .gitlab-ci.yml that uses ansible-playbook to render a template using a role.

ansible-render-template-simple:
  stage: build
  image: registry.gitlab.com/gitlab-pipeline-helpers/docker-python-ansible-user-venv:1.0.0
  before_script: |
    export ANSIBLE_CONFIG=$(realpath ansible.cfg)
  script: |
    ansible-playbook playbook-testrole.yaml -l localhost

The ANSIBLE_CONFIG needs to be a full path, so we define that in the before_script.  Then we use ‘script‘ to invoke the playbook, when in turn calls an Ansible role that renders a file.

Pipeline job invoking Ansible with vault secret

If you currently use Ansible vault for encrypted-at-rest secret management for Ansible, you can continue to leverage that by adding a CI/CD file variable.

Ansible (per normal) will look for the vault secret in the file denoted by the environment variable ANSIBLE_VAULT_PASSWORD_FILE.  But instead of manually creating that file, we can use a CI/CD file variable which renders the file into the image at runtime.

From your GitLab repo, go to Settings > CI/CD > Variables > Add Variable.  Use:

  • Key = ANSIBLE_VAULT_PASSWORD_FILE
  • Value = myfakepass123
  • Type = File
  • Check “Mask variable”

We are using the password “myfakepass123” because that is the value I used when creating the ansible vault file “host_vars/secondhost/vault“.  In a real environment, this value would only be know to essential personnel.

Below is a job from my .gitlab-ci.yml that uses ansible-playbook to render a secret using a role.

ansible-render-template-vault:
  stage: test
  image: registry.gitlab.com/gitlab-pipeline-helpers/docker-python-ansible-user-venv:1.0.0
  rules:
    - if: $ANSIBLE_VAULT_PASSWORD_FILE
      when: always
  before_script: |
    echo ANSIBLE_VAULT_PASSWORD_FILE=$ANSIBLE_VAULT_PASSWORD_FILE
    export ANSIBLE_CONFIG=$(realpath ansible.cfg)
  script: |
    ansible-playbook playbook-secretrole.yaml -l secondhost

We have a rule to only execute this job if ANSIBLE_VAULT_PASSWORD_FILE is defined.  And then invoke the “playbook-secretrole.yaml” which renders a template using the secret found inside “host_vars/secondhost/vault“.

 

REFERENCES

Gitlab source, docker-python-ansible-user-venv

GitLab docs, running your CI/CD jobs in docker containers

Ansible docs

Ansible Galaxy

NOTES

Invoking custom image with volume mount of git project

git clone https://gitlab.com/gitlab-pipeline7091038/gitlab-call-ansible-in-pipeline.git && cd $(basename $_ .git)
docker run -it -v $(pwd):/builds -w /builds --rm registry.gitlab.com/gitlab-pipeline-helpers/docker-python-ansible-user-venv:1.0.0 ansible-playbook playbook-testrole.yaml -l localhost