Ansible with GitHub Actions

Spacelift team - Sep 19 - - Dev Community

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 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 arch

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.

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.

💡 You might also like:

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
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 }}

Enter fullscreen mode Exit fullscreen mode

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 are really helpful for 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.

Another advantage of using Spacelift is that you can manage different infrastructure tools like Ansible, OpenTofu, Terraform, Pulumi, AWS CloudFormation, and even Kubernetes from the same place and combine their stacks with building workflows across tools. Spacelift greatly simplifies and elevates your workflow for all of these tools, and the ability to create dependencies between stacks and passing outputs enables you to make end-to-end deployments with a small change.

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.

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.

Written by Ioannis Moustakis

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .