This doc will explain how to build an Ansible role from scratch then pushed to GitHub and subsequently deploy to Ansible Galaxy for playbook consumption. Playbook flow is not covered here.
[[toc]]
The first step in creating an Ansible role is to initialize it using the ansible-galaxy init
command. This command creates a basic directory structure for the role.
When naming your role, Ansible Galaxy uses a namespace (like shaneholloman
or cello
), followed by the name of the role (like bootstrap
or ntpsec
). The final result would look something like shaneholloman.bootstrap
.
ansible-galaxy init shaneholloman.bootstrap
However, this naming convention is not suitable for hosting the Ansible role code on GitHub, as GitHub does not allow .
in repository names. Also, when we have many repositories, a standard naming convention becomes essential for organization. Therefore, we initialize the repository with the name we expect to use on Ansible Galaxy, but we rename it for our working code base. Instead of naming it shaneholloman.bootstrap
, we rename it to ansible-role-bootstrap
.
mv shaneholloman.bootstrap ansible-role-bootstrap
After renaming the directory, navigate into it and initialize a Molecule scenario. Molecule is a testing framework for Ansible that helps us write and manage test cases for our roles. We use the molecule init scenario
command to create a skeleton for our test cases. The -d docker
option specifies that we want to use Docker as our driver for running the tests.
cd ansible-role-bootstrap
molecule init scenario -d docker
Next, you need to add some default files to the scenario and role. You can find examples of these default files in the 1.ansible-role-model
directory in This workspace.
Some default files may not be necessary for your specific role or scenario. You can find a list of these unnecessary default files in the 1.ansible-role-model
directory in this workspace. They are "listed" by their absence in the 1.ansible-role-model
directory.
Finally, you need to write the correct metadata for your role. You can find an example of how to write this metadata in the 1.ansible-role-model/meta/main.yml
file in this workspace.
- Write Role Tasks
- Write Role Handlers
- Write Role Variables
- Write Role Templates
- Write Role Files
- Write Role Defaults
- Write Role Meta
This part may seem like magic, but the reason it just works is because we are using the docker images I have already created and posted to DockerHub for this purpose. Molecule will pull the image from DockerHub and run the tests in the container on your machine. This is the same process that will be used in the CI/CD pipeline on GitHub via the Action workflows.
❗ Note: This is why we need to set the environment variables for Docker Hub and Galaxy in the GitHub repository.
molecule test
This will be based on the initialized template
The dir ./.github
is where you'll find the GitHub Actions workflow magic. Usually you'll not have to make many changes in here.
.github/workflows/ci.yml
.github/workflows/release.yml
.github/workflows/remove.yml
Replace the shaneholloman.model
with the rolename you are working on. This is the name of the role to be on Ansible Galaxy.
You can do this manually or simply use the kickstart script
bash kickstart.sh
Or consider these steps manually if you want to understand the process:
Here's a step-by-step breakdown of what the script does, including the corresponding code:
-
Define the GitHub account or organization name: This is the name of your GitHub account or organization where the repository will be created.
githubAccount="shaneholloman" # Change this to your personal account name if needed
-
Initialize a new git repository: This step creates a new git repository in the current directory. The
-b main
option specifies that the main branch should be created.git init -b main
-
Get the name of the current repository from the top-level directory: This step retrieves the name of the current directory, which is assumed to be the name of the repository.
repoName=$(basename "$(git rev-parse --show-toplevel)")
-
Create a new repository on GitHub using the gh CLI: This step creates a new repository on GitHub with the same name as the current directory. The repository is public and the creation is confirmed without prompting the user.
gh repo create "$repoName" --public -y
-
Add the remote repository: This step adds the newly created GitHub repository as a remote repository for the local git repository.
git remote add origin https://github.com/$githubAccount/"$repoName".git
-
Add all files in the current directory to the git repository: This step stages all files in the current directory for commit in the git repository.
git add .
-
Commit the changes: This step commits the staged changes with the message "Initial commit".
git commit -m "Initial commit"
-
Push the changes to GitHub: This step pushes the committed changes to the main branch of the remote repository on GitHub.
git push -u origin main
-
Define an associative array where the key is the name of the secret and the value is the secret value: This step defines an associative array of secrets, where the key is the name of the secret and the value is the secret value.
declare -A secrets secrets =( ["DOCKERHUB_TOKEN"]="$DOCKERHUB_TOKEN" ["DOCKERHUB_USERNAME"]="$DOCKERHUB_USERNAME" ["GALAXY_API_KEY"]="$GALAXY_API_KEY" # Add more secrets here as needed )
-
Check if environment variables exist: This step checks if the environment variables for the secrets exist. If any are missing, it prints a message to the console and exits the script.
You can add these environment variables to your
.bashrc
file by running the following commands:nano .bashrc
add the following lines to the end of the file
export DOCKERHUB_TOKEN="dckr_xxxxxxxxxxxxxxxxxxxx" export DOCKERHUB_USERNAME="shaneholloman" export GALAXY_API_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxx"
missingVars=() for key in "${!secrets[@]}" do if [ -z "${secrets[$key]}" ]; then missingVars+=("$key") fi done if [ ${#missingVars[@]} -ne 0 ]; then echo "The following environment variables are missing:" for var in "${missingVars[@]}" do echo "$var" done echo "Please add them to your .bashrc file and run 'source ~/.bashrc'" exit 1 fi
-
Loop through each secret to set it for the current repository: This step loops through each secret and sets it for the current repository on GitHub using the
gh secret set
command.for key in "${!secrets[@]}" do value=${secrets[$key]} command="echo -n $value | gh secret set $key --repo=$githubAccount/$repoName" eval "$command" done
-
Tag and push after setting the secrets: This step creates an empty commit, tags it with the version "0.0.1", and pushes the tag to the remote repository on GitHub.
git commit --allow-empty -m "tagging first version" git tag -a 0.0.1 -m "pre release version - ubuntu only" git push origin 0.0.1
The script manages this part like so:
# Tag and push after setting the secrets commitMessage="tagging first version" tagVersion="0.0.1" tagMessage="pre release version - ubuntu only" git commit --allow-empty -m "$commitMessage" git tag -a $tagVersion -m "$tagMessage" # Ask the user if the current git tag and message are correct echo "The current git tag is $tagVersion with the message '$tagMessage'. Is this correct? (yes/no)" read answer if [ "$answer" != "${answer#[Yy]}" ] ;then git push origin $tagVersion else echo "Please edit the git tag and message in this script." fi
We just went through the process of creating an Ansible role from scratch, pushing it to GitHub, and deploying it to Ansible Galaxy for playbook consumption.
We started by initializing the Ansible Galaxy role skeleton and renaming it for GitHub compatibility. We then initialized a Molecule scenario for testing and added necessary files while removing unnecessary ones. We wrote the correct metadata for the role and outlined the Ansible role development workflow, which includes writing role tasks, handlers, variables, templates, files, defaults, and metadata.
We also covered how to test the role using Molecule and Docker, and how to write the Ansible role readme file. We set up continuous integration using GitHub Actions workflows and deployed the role to GitHub, triggering the CI process. This included defining the GitHub account, initializing a new git repository, creating a new repository on GitHub, adding the remote repository, committing and pushing changes, defining and checking environment variables, setting secrets for the repository, and tagging and pushing after setting the secrets.