Going to AWS re:Invent 2024?

➡️ Book a meeting with Spacelift

Ansible

Ansible with GitHub Actions: Automating Playbook Runs

ansible github actions

Ansible is one of the most popular tools for managing cloud and on-premises infrastructure. GitHub Actions allows users to automate the software development and deployment tasks defined in a GitHub repository. Combining them will enable you to automate and streamline your Ansible deployments, leveraging continuous integration and continuous delivery (CI/CD) principles. 

What we will cover:

  1. What is Ansible?
  2. What is GitHub Actions?
  3. Benefits of automating Ansible deployments
  4. How to run Ansible Playbooks with GitHub Actions

What is Ansible?

Ansible is an open-source, battle-tested automation tool known for its simplicity and powerful capabilities. It is a flexible, powerful tool for automating infrastructure management and configuration tasks, making it an excellent choice for configuration management, infrastructure provisioning, and application deployment use cases.

Ansible is targeted chiefly at IT operators, administrators, and decision-makers, helping them achieve operational excellence across their entire infrastructure ecosystem. Backed by RedHat and a loyal open-source community, Ansible can operate across hybrid clouds, on-prem infrastructure, and IoT. It’s an engine that can greatly improve the efficiency and consistency of your IT environments.

ansible architecture

What is GitHub Actions?

GitHub Actions is a robust CI/CD platform provided by GitHub. It allows you to automate your software workflows directly from your GitHub repository, enabling tasks such as building, testing, and deploying code. GitHub Actions uses YAML files called “Workflows” to define the sequence of tasks to be executed, triggered by events like code pushes, pull requests, or scheduled times.

github actions wokflow

Workflows

Workflows are configurable sets of processes that run jobs. They are written in YAML in the .github/workflows directory and define when jobs should be triggered. GitHub repositories can have multiple workflows, each with their specific configuration. For more information about workflows, see Using workflows.

Events

Events are activities that could trigger a workflow run. Examples include creating a pull request, pushing new code, and opening an issue. For a complete list of events that can trigger workflows, see Events that trigger workflows in the documentation.

Jobs

Jobs define the different steps and processes that should be executed to complete a workflow. Each step in a job executes a specific script or triggers a predefined action. Steps in a job are executed in order. By default, different jobs have no dependencies and run in parallel. You can customize this behavior and define dependencies between jobs. For more information about jobs, see the section on using jobs in a workflow in the documentation.

Actions

Actions are custom applications or scripts that perform a frequently used task and need to be packaged for reuse. You can write down your own GitHub action, but you can also reuse actions from others in the GitHub Marketplace. For more information, see Creating actions in the documentation.

Runners

Runners are virtual machines that effectively execute the code and actions defined in your workflows when triggered. 

Learn more about GitHub Actions with this GitHub Actions tutorial.

Download the Build vs. Buy Guide to Scaling Infrastructure as Code

cheatsheet_image

Benefits of automating Ansible deployments

Automating Ansible deployments with GitHub Actions or any other tool provides several benefits that enhance the efficiency and reliability of software development workflows. Let’s look at some of the key advantages:

  1. Consistent and reproducible deployments: By automating Ansible Playbook execution through GitHub Actions, you can ensure consistent and reproducible deployments across your infrastructure. Ansible’s idempotent nature ensures that tasks are executed consistently and predictably, reducing the risk of operations. GitHub Actions enables automated testing, linting, and deployment of your Ansible Playbooks whenever changes are pushed to your repository.
  2. Audit trail and traceability: GitHub Actions provides a detailed log of deployments, making it easier to track changes and troubleshoot issues. Thus, it effectively creates an audit trail for your infrastructure changes.
  3. Scalability and parallelization: GitHub Actions allows you to run Ansible Playbooks in a parallel and automated fashion across multiple runner instances, enabling you to manage large-scale infrastructures efficiently.

How to run Ansible Playbooks with GitHub Actions?

In this section, we examine the different parts of developing a solution to manage and deploy Ansible Playbooks with CI/CD and GitHub Actions.

1. Create and organize Ansible Playbooks in a repository

Start by organizing your Ansible playbooks in a structured manner within a GitHub repository. Check out Quickstart for repositories to create a new repository.

You can organize your Ansible code repositories in multiple ways, so look for the one that suits your needs. This is an example of a well-organized Ansible directory structure that you can modify:

inventory/
    production                # inventory file for production servers
    staging                   # inventory file for staging environment
    testing                   # inventory file for testing environment
 
group_vars/
   group1.yml             # variables for particular groups
   group2.yml
host_vars/
  host1.yml               # variables for particular systems
  host2.yml
 
library/                  # Store here any custom modules (optional)
module_utils/             # Store here any custom module_utils to support modules (optional)
filter_plugins/           # Store here any filter plugins (optional)
 
master.yml                # master playbook
webservers.yml            # playbook for webserver tier
dbservers.yml             # playbook for dbserver tier
 
roles/
   example_role/               # this hierarchy represents a "role"
       tasks/            #
           main.yml      #  <-- tasks file can include smaller files if warranted
       handlers/         #
           main.yml      #  <-- handlers file
       templates/        #  <-- files for use with the template resource
           ntp.conf.j2   #  <------- templates end in jinja2
       files/            #
           bar.txt       #  <-- files for use with the copy resource
           foo.sh        #  <-- script files for use with the script resource
       vars/             #
           main.yml      #  <-- variables associated with this role
       defaults/         #
           main.yml      #  <-- default lower priority variables for this role
       meta/             #
           main.yml      #  <-- role dependencies
       library/          # roles can also include custom modules
       module_utils/     # roles can also include custom module_utils
       lookup_plugins/   # or other types of plugins, like lookup in this case
 
   monitoring/              # same kind of structure as "common" was above, done for the monitoring role

2. Create a GitHub Actions Workflow

To trigger and manage GitHub Actions workflows, define a YAML file in your repository’s .github/workflows directory. A GitHub Actions Workflow is a configurable automated process with one or more jobs that need to be executed. 

github actions workflow

Create a file named lint_ansible.yml and add the workflow below: 

lint_ansible.yml

name: Ansible files & Deployment 

on:
  push:
    paths:
      - 'playbooks/**'
  pull_request:
    paths:
      - 'playbooks/**'

jobs:
  ansible-lint:
    uses: ansible/ansible-content-actions/.github/workflows/ansible_lint.yaml@main
    with:
      args: '-p playbooks'

The on parameter defines which events can trigger the workflow. For a list of available events, see Events that trigger workflows

For this example, the workflow will be triggered when changes are made in files under the playbooks directory via a code push to any branch on this repository or when someone creates a pull request.

The jobs parameter defines the various jobs that run in runner environments to complete the workflow. For our example, we are running the ansible-lint job that checks playbooks for best practices, syntax issues, and behavior that could be improved. In this example, we opt to lint ansible files only on the playbooks directory by using the flag -p.

Now, with the workflow for linting our Ansible playbooks in place, let’s push a commit with an example playbook deploy_web_server.yml under the playbooks directory and see the GitHub Action workflow get triggered.

deploy_web_server.yml

- name: Install and Configure Nginx
  hosts: all
  become: true
  pre_tasks:
    - name: Set SSH user for Ubuntu
      ansible.builtin.set_fact:
        ansible_user: ubuntu
      when: ansible_os_family == "Debian"

  tasks:
    - name: Install Nginx Web Server
      ansible.builtin.apt:
        update_cache: true
        name: nginx
        state: present

    - name: Create index.html
      ansible.builtin.copy:
        dest: "/var/www/html/index.html"
        content: |
          <!DOCTYPE html>
          <html>
          <head><title>Server Details</title></head>
          <body>
          <h1>Served from {{ ansible_hostname }}</h1>
          </body>
          </html>
        mode: '0644'

    - name: Ensure Nginx is running and enabled
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true

We see that the workflow has been executed successfully, which means our file syntax follows best practices:

deploy ansible with github actions

3. Handle sensitive data in GitHub Actions

You can leverage Environment and Repository GitHub Secrets to avoid storing sensitive information in configuration and code files. In this example, let’s create two repository secrets. 

Go to Repository → Settings → Secrets and variables → Actions. Here, you have to select the New repository secret option and configure the secret ANSIBLE_USER storing the value ubuntu:

ansible sensitive data in GitHub Actions

Similarly, create a secret SSH_PRIVATE_KEY and store the content of the private SSH key to connect to the remote target server. 

4. Deploy the Ansible Playbook with GitHub Actions

Next, define another workflow file to automate running an Ansible Playbook against remote targets. Create a new file, deploy_playbook.yml, under the .github/workflows directory.

deploy_playbook.yml

name: Deploy with Ansible

on:
  pull_request:
    branches:
      - main
    types: [closed]

jobs:
  deploy:
    name: Deploy Ansible Playbook
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Set up SSH
        run: |
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > private_key.pem
          chmod 600 private_key.pem
          
      - name: Install Ansible
        shell: bash
        run: |
          sudo apt update
          sudo apt install -y ansible

      - name: Run Ansible Playbook
        env:
          ANSIBLE_USER: ${{ secrets.ANSIBLE_USER }}
          ANSIBLE_HOST_KEY_CHECKING: False
        run: |
          ansible-playbook -i inventory/hosts.ini playbooks/deploy_web_server.yml --private-key private_key.pem -u ${{ secrets.ANSIBLE_USER }}

In this workflow, set up the SSH key, install Ansible on the runner, and finally run the Ansible Playbook against the remote targets. 

We only trigger this workflow when pull requests are closed on the main branch and only if the GitHub event is a pull request merge. 

Let’s also create a GitHub Environment named production to enable manual approvals for deployments. On your repository, go to Settings → Environments and select New Environment:

GitHub Environment for ansible

On the next screen, opt for a required manual approval on this environment:

ansible github actions configuration

We are ready to deploy. Push code to a new branch and create a pull request against the main branch to trigger this workflow. For the needs of this demo, let’s update the deploy_web_server.yml playbook to kick-start the workflow:

update ansible with github actions

After the linting finishes, we can merge the pull request and proceed. Go to Actions, and you should see the deployment workflow waiting for approval to continue:

ansible github actions request

Select Approve and deploy:

ansible github actions approve deployment

Finally, we have configured automatic linting for all our Ansible Playbooks, and we can automatically execute them against our server targets via GitHub Actions. See the action completed successfully:

ansible github actions completed successfully

How can Spacelift help you with Ansible projects?

Compared to building a custom and production-grade infrastructure management pipeline with a CI/CD tool like GitHub Actions, adopting a collaborative infrastructure delivery tool like Spacelift feels a bit like cheating. 

Spacelift’s vibrant ecosystem and excellent GitOps flow can greatly assist you in managing and orchestrating Ansible. By introducing Spacelift on top of Ansible, you can easily create custom workflows based on pull requests and apply any necessary compliance checks for your organization.

With Spacelift, you get:

  • Better playbook automation – Manage the execution of Ansible playbooks from one central location.
  • Inventory observability – View all Ansible-managed hosts and related playbooks, with clear visual indicators showing the success or failure of recent runs.
  • Playbook run insights – Audit Ansible playbook run results with detailed insights to pinpoint problems and simplify troubleshooting.
  • Policies – Control what kind of resources engineers can create, what parameters they can have, how many approvals you need for a run, what kind of task you execute, what happens when a pull request is open, and where to send your notifications
  • Stack dependencies – Build multi-infrastructure automation workflows with dependencies, having the ability to build a workflow that, for example, generates your EC2 instances using Terraform and combines it with Ansible to configure them
  • Self-service infrastructure via Blueprints, or Spacelift’s Kubernetes operator – Enable your developers to do what matters – developing application code while not sacrificing control
  • Creature comforts such as contexts (reusable containers for your environment variables, files, and hooks), and the ability to run arbitrary code
  • Drift detection and optional remediation

If you want to learn more about using Spacelift with Ansible, check our documentation, read our Ansible guide, or book a demo with one of our engineers.

Would you like to see this in action – or just want a tl;dr? Check out this video I put together showing you Spacelift’s new Ansible functionality:

ansible product video thumbnail

Key points

This blog post combined GitHub Actions with Ansible to run playbooks via CI/CD. We explored the benefits of running Ansible in an automated fashion, and we walked through a complete example of configuring a code repository with GitHub Actions to lint Ansible files and execute playbooks against remote hosts.

Thanks for reading, and I hope you enjoyed this as much as I did.

Manage Ansible Better with Spacelift

Managing large-scale playbook execution is hard. Spacelift enables you to automate Ansible playbook execution with visibility and control over resources, and seamlessly link provisioning and configuration workflows.

Learn more

The Practitioner’s Guide to Scaling Infrastructure as Code

Transform your IaC management to scale

securely, efficiently, and productively

into the future.

ebook global banner
Share your data and download the guide