Custom Roles

Sometimes you need a specific role, but there isn't one in Ansible Galaxy. No problem - follow our guide to creating your own.


You now know how to use simple playbooks and leverage the roles found in Ansible Galaxy. However, you may find times when you need to create your own roles for something that does not exist in Ansible Galaxy, or requires a slight twist on those that are available in order to meet your specific needs.

In the last lab we created a simple playbook to install the Azure CLI. If you were to search on Azure CLI (filtered to Ubuntu) then you will notice that there are a few existing roles, designed for various combinations of platform and versions within them.

The first two roles in the list support xenial, but not bionic. (If you are using Ubuntu 18.04 then you are running the bionic version.)

(The third does support both xenial and bionic, but we’ll pretend it doesn’t exist for the sake of this lab.)

We will create a local Ubuntu only role for the Azure CLI, based on our az_cli.yml file. We’ll then add it to a GitHub repo and then update our requirements.yml and master.yml files to make use of the new role.

Git and GitHub

We will be using both git and GitHub during this lab, so you will need the following:

  1. Local git repos area

    You may have your own existing folder for repos. If so then use that.

    Using /git or /repos is a good default for WSL2, Linux or MacOS. For WSL1 then you may prefer to use /mnt/c/git instead.

    Create a repos folder if you don’t already have one, e.g.:

    mkdir -m 755 /git

    This lab will use /git throughout. Substitute your actual git repos directory path whenever you see /git mentioned in this lab.

  2. Git binary

    Make sure that you have the git binary installed locally. (Git installation guide.)

    Note that this lab will use the git binary commands wherever possible. If familiar with vscode then feel free to use the automatic git integration in the Source Control view, but you will still need to have the git binary installed.

  3. GitHub ID

    Sign up for a GitHub ID if you haven’t already got one.

If you would like an overview of git then the Git Basics: What is git? video is a good place to start.

Initialise the ansible-role-azure-cli area

  1. Change directory to your git repos folder

    cd /git

    Make sure you are directly in your git repos folder before continuing to the initialising step.

  2. Initialise the role area

    The ansible-galaxy init command creates a skeleton role in the current directory.

    umask 022
    ansible-galaxy init ansible-role-azure-cli
    cd ansible-role-azure-cli

    You can see the structure using the tree . command.

  3. Start vscode for the current folder

    code .

    The role will open up in its own vscode window.

    Again, install vscode using these instructions, plus the vscode extension for Ansible (vscoss.vscode-ansible). However you may complete the lab using your preferred editing tool.

Let’s take a look at the structure of the role.

Role structure

Each role has multiple folders. The ansible-galaxy init automatically creates the following, as output by tree /git/ansible-role-azure-cli:

├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

8 directories, 8 files

The various sections of the playbook will need to be split out and placed in the correct area:

  • tasks go into ./tasks/main.yml
  • meta file contains metadata about the role
    • required if you are planning to upload to Ansible Galaxy
  • variables and the lower priority defaults go into the vars and defaults folders respectively
  • handlers are there to manage errors during deployments
  • artefacts deployed by the task modules are placed in either files (static) or templates (dynamic)
  • automated role testing within CI/CD pipelines is stored in the tests folder
    • test results are visible in Ansible Galaxy with the build passing or build failing tags

For more info on the various sections within a role then read the docs. I would also recommend that you browse the various roles in Ansible Galaxy and then navigate to the linked repos.

Convert to a local git repo

Before we modify the skeleton files, let’s commit the current set of files as a local repo.

  1. Initialise the local repo

    Initialise git and add your user config. (Open the Terminal in vscode using CTRL+'.)

    git init
    git config --global ""
    git config --global "Richard Cheney"
    git config --global credential.helper store
    git config --global credential.user richeney

    Change the email and user name to your own. Change the credential.user to your GitHub ID.

  2. Git attributes file

    Create a .gitattributes file

    echo "* text=auto eol=lf" > .gitattributes

    This is highly recommended for WSL1 users. This file will a) automate CRLF to LF translation and b) ensure that Windows and WSL1 level git info are synced.

  3. Stage the files

    git add *
    git add .gitattributes
  4. Commit the files

    git commit -m "ansible-galaxy init"

    The -m switch adds a message for the commit.

  5. Show the status

    git status

    Expected result:

    On branch master
    nothing to commit, working tree clean

OK, we have a local repo, with a commit prior to customising the role.

Configure the local role

OK, let’s configure the role to install the Azure CLI.

  1. Remove the sections that are not needed

    We have no artefacts, variables or tests, so we’ll remove the following directories.

    • files
    • handlers
    • templates
    • tests
    • vars

    If using the CLI:

    cd /git/ansible-role-azure-cli
    rm -fR files handlers templates tests vars

    In vscode you can force a refresh the explorer view. (Hover over the bar above the folder’s explorer view.)

  2. Update the tasks/main.yml

    The tasks file is effectively a subset of the original az_cli.yml file, only containing the task information. These tasks are different to those in that previous lab, but not substantially.

    Replace the contents of the ./tasks/main.yml file with the yaml below:

    {% raw %}

    # Install the Azure CLI
    # Based on <>
    - name: Update apt cache
        update_cache: yes
    - name: Get packages needed for the install process
        name: "{{ apt_packages }}"
        state: present
      when: ansible_os_family == 'Debian'
    - name: Import the Microsoft signing key into apt
        url: "{{ repo_key_url }}"
        state: present
    - name: Add the Azure CLI software repository
        repo: "deb [arch=amd64] {{ansible_distribution_release}} main"
        filename: "{{ azure_package_name }}"
        state: present
    - name: Install Azure CLI
        name: "{{ azure_package_name }}"
        update_cache: yes
    - name: Update all packages
        upgrade: dist

    {% endraw %}

    In terms of file structure this is a straight YAML list of tasks. If you remember the format in the single playbook file then the tasks were indented and therefore nested.

    Note the curly braced variable names. We could have hardcoded the values, but instead we’ll define those in a defaults file.

  3. Update the defaults/main.yml

    Replace the contents of the ./defaults/main.yml with the following yaml:

    azure_package_name: azure-cli
      - aptitude
      - curl
      - apt-transport-https
      - lsb-release
      - gnupg

    The list of packages for apt to install is the one most likely to be updated over time, but chances are that the defaults will always be used. The mechanism is shown for your benefit.

  4. Update the meta/main.yml

    The meta file is used principally by both Ansible Galaxy and the ansible-galaxy executable. There is some good documentation on the Role Metadata.

    Replace the contents of the ./meta/main.yml file with the following yaml.

      role_name: azure_cli
      author: azurecitadel
      description: This role installs the Microsoft Azure CLI for Linux.
      company: "None"
      license: "MIT"
      min_ansible_version: 2.4
      - name: Ubuntu
          - xenial
          - bionic
        - cloud
        - microsoft
        - azure
        - cli
    dependencies: []
  5. Update the

    The markdown file is the one that you see when you are browsing the repo. (It is also used for the Ansible Galaxy web pages if you import the role into there.)

    Replace the contents of ./ with the following markdown.

    # Install Azure CLI
    Ansible role to install the Azure CLI for either Ubuntu 16.04 (xenial) and 18.04 (bionic).
    ## Installation
    `ansible-galaxy install richeney.azure_cli`
    ## Example Playbook
    - hosts: all
        - richeney.azure_cli
    ## Requirements
    ## Dependencies

    Change richeney to your GitHub ID.

  6. Ensure that all of the edited files have been saved and close the editor

Commit the deletes and modifications

  1. Check the status

    git status

    Expected result:

    On branch master
    Changes not staged for commit:
      (use "git add/rm <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
            modified:   defaults/main.yml
            deleted:    handlers/main.yml
            modified:   meta/main.yml
            modified:   tasks/main.yml
            deleted:    tests/inventory
            deleted:    tests/test.yml
            deleted:    vars/main.yml
    no changes added to commit (use "git add" and/or "git commit -a")
  2. Stage and commit the files

    git commit -a -m "Azure CLI tasks and defaults"

    The -a switch stages all of the folder’s deletions, modifications and creations. (Same as running git add * first.)

    Expected output:

    [master ef0af2c] Azure CLI tasks and defaults
     8 files changed, 89 insertions(+), 105 deletions(-)
     rewrite (99%)
     delete mode 100644 handlers/main.yml
     rewrite meta/main.yml (96%)
     rewrite tasks/main.yml (90%)
     delete mode 100644 tests/inventory
     delete mode 100644 tests/test.yml
     delete mode 100644 vars/main.yml

Test the role using local path

  1. Return to your ansible working area

    cd ~/ansible
  2. Create a test.yml file

    Create the file containing the following text, and then modify your host and path.

    # Master playbook to test custom role
    - hosts: vm1_cf1d
      become: yes
          - role: /git/ansible-role-azure-cli

    Ensure that the VM name against - hosts: is correct, as well as the directory path for your role directory. (Reminder: list hosts using ansible all --list-hosts.)

  3. Test

    Run the local playbook to prove that the VM is compliant.

    ansible-playbook test.yml

Push to GitHub

OK, our local repo containing our custom role is working nicely, but it won’t be widely available if it is stuck on your machine. Time to push it up to GitHub and then install it via the requirements.yml file.

  1. Create a GitHub repository

    Log in to GitHub.

    Click on the + at the top right to add a New Repository.

    • name it ansible-role-azure-cli
    • add a description, e.g. “Ansible role to install the Azure CLI on Ubuntu”
    • leave the repo as Public
    • do not click the “Initialize this repository with a README” checkbox
    • click Create Repository
  2. Copy the push commands

    • scroll down to the "…or push an existing repository from the command line" section
    • click the copy icon on the right

    The two commands should:

    1. add the new GitHub repo as the origin remote
    2. push your local repo up to the GitHub repo
  3. Return the the CLI and change directory to your local repo

    cd /git/ansible-role-azure-cli
  4. Add the remote and push

    Paste the two commands into the session and hit enter to run them. You will need to authenticate to GitHub for the push command to succeed.

    Windows 10 users can access clipboard history using Win+V

    Example output:

    Counting objects: 26, done.
    Delta compression using up to 4 threads.
    Compressing objects: 100% (12/12), done.
    Writing objects: 100% (26/26), 3.85 KiB | 985.00 KiB/s, done.
    Total 26 (delta 0), reused 0 (delta 0)
     * [new branch]      master -> master
    Branch 'master' set up to track remote branch 'master' from 'origin'.

    The credentials store we configured earlier will retain the credentials for future git push commands.

  5. List the remotes

    List the origin remote using:

    git remote -v

    Example output:

    origin (fetch)
    origin (push)
  6. Verify the status

    git status

    Expected output:

    On branch master
    Your branch is up to date with 'origin/master'.
    nothing to commit, working tree clean
  7. Check the GitHub repo

    Return to the browser.

    Refresh the GitHub repo webpage (CTRL+R) and you should see the files for the role and the contents of the

Add the new GitHub repo to your required roles

  1. Return to the terminal

  2. Change directory to your ansible working area

    cd ~/ansible
  3. Edit the requirement.yml

    Your ~/ansible/requirement.yml should currently look like this:

    - src: geerlingguy.pip
      name: pip
    - src: geerlingguy.docker
      name: docker

    Add a new entry to the end of that list for your new GitHub repo:

    - src:
      name: azure_cli

    Note the change in src format from the Ansible Galaxy default to fully pathed repo.

  4. Save and close the file

  5. Update your local set of roles

    ansible-galaxy install -r requirements.yml

    Example output:

     [WARNING]: - pip (1.3.0) is already installed - use --force to change version to unspecified
     [WARNING]: - docker (2.5.3) is already installed - use --force to change version to unspecified
    - extracting azure_cli to /home/richeney/ansible/roles/azure_cli
    - azure_cli was installed successfully

Update the master playbook to use the installed role

  1. Edit the master.yml file

    We’ll update the master.yml so that it uses two inventory groups rather than just our test host.

    Replace the contents with:

    # Master playbook pulling in roles
    - hosts: all
      become: yes
        - azure_cli
    - hosts: tag_docker_true
      become: yes
        - pip
        - docker
          - name: docker

    All hosts should have the Azure CLI installed. Those that have a docker:true tag (i.e. vm1 and vm2) will also get the pip and docker roles.

  2. Apply the playbook

    ansible-playbook master.yml

    OK, now the config is starting to look a little more impressive. The playbook will take a little while to run on the first pass

    Don’t forget that as well as lists of tasks and roles that we can have roles that include nested roles as well and define dependencies. It would be easy to iteratively update this configuration to something that covers a far wider set of requirements.

Contributing to Ansible Galaxy

I would absolutely recommend that you use the existing Ansible Galaxy roles wherever you can, but there is always the chance that you find that there is nothing in there that meets your particular requirement. If you have created something that truly has value to the community, then pay it back by uploading into Ansible Galaxy.

OK, before we continue, let’s be clear here. It would not be a good idea for those of you doing this lab to take your custom azure_cli roles and litter Ansible Galaxy. There are already good roles in Ansible Galaxy to deal with Azure CLI installation and it will not be enhanced my multiple copies of the same role from these labs!!!

All of the work you have done in creating a role using ansible-galaxy, testing it and pushing it into a GitHub repo is exactly the starting point for contributing into Ansible Galaxy. Let’s show the process and then clean up after ourselves.

  1. Browse to Ansible Galaxy
  2. Login using your GitHub ID
  3. Click on My Content in the sidebar on the left
  4. Click on Add Content (on the right)
  5. In the Add Content dialog, click on Import Role from GitHub
  6. Filter by “ansible” (optional)
  7. Check your ansible-role-azure-cli repo and click OK
  8. Refresh the page (CTRL+R)
  9. Click into your azure_cli role
    1. Your URI in the address bar should be similar to
    2. Note that the name, platforms, description etc. in the Details section are taken from that metadata/main.yml
  10. Click on the Read Me button
    1. The content here is pulled straight from your file
  11. Click on My Content again
  12. Click on the three dots on the far right of the azure_cli record to open the context menu
  13. Delete

If you’ve created a role that you think would benefit the wider community then please refer to the full contribution documentation.


Finishing Up

In the next lab we will return to Packer, and use Ansible playbooks in creating an image. We will also start using the Shared Image Gallery as our image repository.

Help us improve

Azure Citadel is a community site built on GitHub, please contribute and send a pull request

 Make a change