1 + 1 = 3
In part 1 of four (which might end up being 5 or 6), we’ve talked about dotfiles, what ‘dotfiles’ are and how we can easily manage them via Github.
In part 2 we installed our first program via Ansible and learned a little bit about Docker. Since we installed a single tool (zsh), the current local.yml file is okay-ish, but not very extensible.
In this article, we’ll extensively cover how to make your Ansible scripts more extensible and how can we utilize a shell script to automate things even further.
tl;dr
In this part, we’ll create different tasks for our Ansible Playbook to run, we’ll add tags so you can easily run them individually or all together. Again this can be run inside a Docker container. Last, we’ll create a small script that will automagically install Ansible on a new machine and runs the Playbooks for us.
Extending Ansible Playbook
Now, in the previous Article, we’ve created a single script to install zsh. Which isn’t bad in any way, but you can imagine that if we have 10, 20 or even more tools to install. This file will be blown and unreadable/unmaintainable. Our current file looks like this:
There is a ‘better’ way of writing these tasks, and therefore we need to adjust our local.yml file a little bit:
As you might see there are a few changes. The first noticeable change is pre_tasks, while running a Playbook the tasks specified there are run up front. In our case, we’ve added the following pre_tasks: force_apt_get, update_cache & state.
force_apt_get will install APT if it is not already installed, update_cache tells Ansible’s apt module to refresh the caches before applying whatever change is necessary (if any) and the state tells what should be the state of the package after the task is completed (present or absent).
Then, we added a tag called; install. What this does will be important later on, but with a tag, we can specifically tell Ansible which tasks we would like to run.
Then, there is a block called; tasks. This is the part where we will specify the files Ansible can have a look at for installing our tools. And as probably notes, we’ll now create a new folder in our Ansible repository called ‘tasks’ and create a new file in that folder called ‘zsh.yml’.
First Change Directory to your Ansible folder (the repository we’ve created in the previous article).
$ cd ~/foo/bar/Ansible
Create a new folder called tasks and Change Directory into that folder.
$ mkdir tasks
$ cd tasks
Separation of Tasks
Currently, we only install a single tool; zsh. So we’ll start with the creation of a separate task for this, create a new file inside the tasks folder (created above) called zsh.yml
$ touch zsh.yml
Now open this file in your favourite text editor (vim of course), and add the following content:
The code we’ve just pasted has the name Install zsh, become: true make sure we install zsh as superuser.
The apt keyword tells Ansible how to install zsh, which is the equivalent of:
$ sudo apt install zsh
The tags (zsh & install) can be used while running the Playbook, it makes it very easy to (re)install certain tools/programs on its own, or in groups. But we’ll come back to this later.
Test in Docker
Now, after this small refactoring, it might be a good idea to test if everything still works the way it should. We’ll use the Dockerfile from the previous article. If you return to the root of your project, you can enter the following commands:
$ docker build -t bootstrap_pc .
$ docker run --rm -it bootstrap_pc
If you are inside of your Docker container, you can run either of the following two commands:
$ ansible-playbook -t install local.ymlor$ ansible-playbook -t zsh local.yml
How does that work?
Can you remember that we’ve added two different tags inside of our zsh.yml file? Currently, we can use both tags (install and zsh) for the installation of zsh.
If we add multiple different tasks, all with the tag install, we can run all these tasks with a single command:
$ ansible-playbook -t install local.yml
If we would like to install a single program/tool, we can use that specific tag we’ve used for that task.
$ ansible-playbook -t zsh local.yml
Too much?
No worries, we’ll add one more tool, and test everything together one more time. Things will probably become much more clear after that, trust me.
Another tool that a lot of developers use these days is Node, so we’ll go over the installation of Node with Ansible-Playbook as well. Therefore, we need to add another file to our tasks folder.
$ cd tasks
$ touch node.yml
Again, open the node.yml file in your favourite vim editor, and add the following content to that file:
What we see here, is that we have the same setup as the zsh.yml file, except that the name is different, we install two tools (Node & npm) by specifying these in a list and of course the tags!
Now we have the install & node tags, this means that if we run the following command:
$ ansible-playbook -t install local.yml
We’ll install three tools: zsh, Node & npm! For that to work, we need to make one small adjustment to the local.yml file:
The difference between this and the previous local.yml file is the addition of the tasks/node.yml folder under the tasks keyword. Otherwise, Ansible wouldn't be able to detect this as a task and will be unable to install the desired tools.
First, we’ll need to rebuild our docker image and run it:
$ docker build -t bootstrap_pc .
$ docker run --rm -it bootstrap_pc
Now with everything in place, we can run either one of the following three commands, depending on what you want to install:
$ ansible-playbook -t install local.ymlor$ ansible-playbook -t zsh local.ymlor$ ansible-playbook -t node local.yml
Important!
Because we are installing these tools in a Docker container, the installation as superuser is ‘easier’. If you would install tools on your machine that involve sudo in the command, you’ll be prompted for your password. That is not necessary in Docker, but you do need that on your local machine, if you want to run these scripts locally you need an extra ‘flag’ after the ansible-playbook command:
$ ansible-playbook -t install local.yml --ask-become-pass
The ask-become-pass flag will prompt you for your superuser password, if you submit that, you’ll be able to run these scripts locally.
More Automation
Now you might wonder; is there more automation we can implement? And the answer to that is: YES!
Currently, if we would set up a new machine, we’ll have to go over a few steps:
1. Install Git
2. Install Ansible
3. Clone Ansible Repository from your GitHub
4. Run the desired Anbile-Playbook
5. Done
What if we could bring this down to:
1. Run an installation script from GitHub
2. Done
Now those three steps might sound peanuts, but chances are that you don't know how to install these tools and have to look that up, wait for the first installation to finish before you move on to the second, have to wait again, etc… You now can run a single command, and everything will be done for you, sit down, relax, grab some coffee and enjoy the process!
Now for we reach that state of relaxation, we first have to add an extra file at the root of our repository.
$ cd ~/foo/bar/Ansible
$ touch install
Once again we open the install file in our favourite editor, and add the following content to that file:
What we see, is that Git and Ansible are installed and the Repository is pulled by Ansible and also asks for the superuser password.
Now there are a few variables you’ll need to fill in yourself:
1. <USERNAME> = username from GitHub
2. <REPOSITORY> = repository name from Github
3. <BRANCH> = the Branch name, probably ‘main’
4. <SCRIPT_NAME> = name of the file (probably ‘installed’)
Once you’ve filled in these variables, you should be able to test everything one more time. The only thing you really need to do is adjust your Dockerfile so that Git and Ansible aren’t installed anymore:
With the new Dockerfile, we can once again build & run our Docker container:
$ docker build -t bootstrap_pc .
$ docker run --rm -it bootstrap_pc
Now, inside our container, there is a single command we should run to install everything we need. The command is located in the install script:
$ bash <(curl -s https://raw.githubusercontent.com/<USERNAME>/<REPOSITORY>/<BRANCH>/<SCRIPT_NAME>)
Once the script is finished, Ansible, Git, zsh & Node are installed in the Docker container. If this would be your new machine, this would mean a single shell command and you are finished, how great is that?!
Conclusion
Congratulations, after finishing this Article, you’ve successfully refactored some of the Ansible-Playbook code, created a second Ansible Playbook and again ran it inside a Docker container. You’ve installed zsh as well as Node, and in the end, we’ve created a shell script, that can be run with a single command from your command line. All your necessary tools are now installed and you are up and running within a blink of an eye.
Now you might wonder, this is part 3 of 4 (or 5, or 6, or …) and we are done, everything is installed and ready to go. Well, I think that we can push things even a little bit further. We can, for example, automate the SSH key setup with Ansible-Vault. More on that, in the upcoming article!
Thank you for reading, and if you want to have a look at my Ansible repository, here is a link. We’ll probably get to something similar to that in the upcoming Articles. And if not, you should be able to at least create something similar with all the information.