This post was originally published on the
XLAB Steampunk blog .
Ansible is a powerful automation tool for managing and configuring systems and devices. With Ansible Playbooks, you can automate tasks from simple configuration changes to complex deployments. Simply describe the desired state of your infrastructure and Ansible will make it happen. But writing reliable, maintainable and secure playbooks takes practice.
In this blog post, we’ll explore a good developer flow for writing Ansible Playbooks and we’ll do it with a practical example: configuring and starting a documentation server.
Let’s jump right in!
1. Start with a plan Before you dive in to writing, take some time to think about what your playbook needs to do. What tasks do you need to automate? What hosts or groups of hosts do you need to apply the playbook to? Once you have a good understanding of your requirements, you can start to break down the playbook into smaller, more manageable tasks.
2. Write a diagram When you have all your answers, write up a high-level diagram of what your playbook should do. This will help you visualize all the steps involved in the playbook and identify any potential dependencies.
When making a basic diagram of an Ansible Playbook, remember it should:
Be easy to understand and not cluttered with too much detail. Show the overall flow of the playbook, including the steps involved and any dependencies between them. Identify any key decision points or branching logic. Use clear and concise labels. In our case, the diagram looks like this:
3. Find the right collections and approved modules After you’ve visualized your playbook, it’s time to research available modules, the building blocks of your automation. There are many different modules available, covering a wide range of tasks, and you can find them on Ansible Galaxy. But unfortunately, there’s no way of telling if the examples are actually of high-quality, meaning there’s no guarantee that they will do what they’re supposed to do, or even if they are actually safe to use in your environment.
For better assurance, use the Complete Ansible Collections Documentation generated by XLAB Steampunk team of Ansible experts. This documentation includes examples that have been checked for correctness and can be safely used in your playbooks.
4. Write the playbook Once you have a good understanding of the tasks you need to automate and are sure that the collections and modules you’re about to include in your playbook are high-quality, you can start writing your playbook. Be sure to organize it into distinct tasks, structured plays, and well-defined variables and conditionals to ensure your playbooks are clear, maintainable and efficient.
Check out the Ultimate guide for high-quality Ansible Playbooks to get started. Or get some help from AI to speed things up even more.
In our case, the playbook looks like this:
---
- name : An example playbook
hosts : localhost
vars :
documentation_user :
username : "documento"
home_dir : "/home/documento"
doc_dir : "/home/documento/www-data"
tasks :
- name : Install prerequisite packages
ansible.builtin.package :
name :
- nginx
- python3-pip
- rssh
state : present
- name : Create group
ansible.builtin.group :
name : "{{ documentation_user.username }}"
state : present
- name : Create a login user
ansible.builtin.user :
name : "{{ documentation_user.username }}"
group : "{{ documentation_user.username }}"
state : present
shell : /usr/bin/rssh
system : "no"
createhome : true
home : "{{ documentation_user.home_dir }}"
- name : Allow deployment pipeline access as the documentation user
ansible.posix.authorized_key :
user : "{{ documentation_user.username }}"
state : present
key : "{{ lookup('file', 'keys/documentation_ecdsa.pub') }}"
- name : Create directory for documentation files
ansible.builtin.file :
path : "{{ documentation_user.doc_dir }}"
state : directory
owner : "{{ documentation_user.username }}"
group : "{{ documentation_user.username }}"
- name : Install Ansible PKI prerequisites.
ansible.builtin.pip : name=pyOpenSSL
- name : Generate an OpenSSL private key
openssl_privatekey :
path : /etc/ssl/private/documentation.key
register : openssl_private_key_result
- name : Generate an OpenSSL Certificate Signing Request
openssl_csr :
path : /etc/ssl/private/documentation.csr
privatekey_path : /etc/ssl/private/documentation.key
register : openssl_csr_result
- name : Generate a Self Signed OpenSSL certificate
openssl_certificate :
path : /etc/ssl/certs/documentation.crt
privatekey_path : /etc/ssl/private/documentation.key
csr_path : /etc/ssl/private/documentation.csr
provider : selfsigned
register : openssl_crt_result
- name : Deploy the rssh config file
ansible.builtin.template :
src : rssh.conf
dest : /etc/rssh.conf
mode : "0644"
- name : Deploy the nginx config file
action :
module : ansible.builtin.template
src : nginx.conf
dest : /etc/nginx/nginx.conf
validate : "nginx -t -c %s"
mode : "0644"
register : nginx_conf_result
- ansible.builtin.service :
name : nginx
state : restarted
when : openssl_private_key_result.changed or openssl_csr_result.changed or openssl_crt_result.changed or nginx_conf_result.changed
5. Test your playbook Even if you’ve mapped out your playbook, dove deep into module research, and poured your heart and soul into writing your playbook, does that guarantee it’ll work? Well, sorry to break it to you, but nope! But don’t fret, there are awesome tools out there to whip your playbooks into shape in no time.
Check it with Ansible Lint First, always use Ansible Lint. Lint is a tool that will do a basic check of your playbook for syntax errors and general playbook writing best practices. Ansible Lint will make sure your playbook is correctly written, which is the basic requirement for your playbook to work.
Let’s lint the playbook.
As we can see, Lint exited at first error, since it did not recognize the ansible.posix collection, which we have not installed. Which is okay as Lint isn’t meant to dive deep into playbook analysis but only take care of the basics. But just because the syntax is correct doesn’t mean your playbook will actually do what it’s supposed to. So, here’s another tool to the rescue.
Check it with Steampunk Spotter Steampunk Spotter performs a thorough in-depth playbook analysis, focusing on security to ensure that the playbooks meet the highest standards and are easy to maintain. It’s like having an Ansible expert by your side, checking your playbooks and guiding you to perfection.
Let’s run a Spotter scan.
As we can see, Spotter gave us valuable information to work with. Most of the issues we are dealing with can be fixed using the rewrite function. Let’s do that using spotter scan --rewrite playbook.yml command.
We fixed most of the issues we were dealing with, and Spotter nicely shows precisely what was done and which checks still remain. We are left with only some hints and warnings which are quite easy to fix manually, and do not take a lot of time, but are very important if we want to follow best practices.
Spotter also generated a requirements file for us, so let’s go ahead and install it.
Let’s check the playbook with Spotter to see if we cleared up all the issues.
Spotter says we’re good to go.
With testing complete, it’s time for the production deployment. Let’s rerun Ansible Lint to see if the playbook is production ready.
And there we go; our playbook is ready for production.
6. Deploy playbook to production Once you have tested your playbook thoroughly in the test environment, you can deploy it to production. Putting your playbooks into action on real systems is a big deal in Ansible Playbook development. It’s crucial to watch closely and double-check to make sure everything is working right, and your system is performing its best.
Here is the finished playbook.
---
- name : Prepare and deploy documentation server
hosts : localhost
vars :
documentation_user :
username : "documento"
home_dir : "/home/documento"
doc_dir : "/home/documento/www-data"
tasks :
- name : Install prerequisite packages
ansible.builtin.package :
name :
- nginx
- python3-pip
- rssh
state : present
- name : Create group
ansible.builtin.group :
name : "{{ documentation_user.username }}"
state : present
- name : Create a login user
ansible.builtin.user :
name : "{{ documentation_user.username }}"
group : "{{ documentation_user.username }}"
state : present
shell : /usr/bin/rssh
system : false
create_home : true
home : "{{ documentation_user.home_dir }}"
- name : Allow deployment pipeline access as the documentation user
ansible.posix.authorized_key :
user : "{{ documentation_user.username }}"
state : present
key : "{{ lookup('file', 'keys/documentation_ecdsa.pub') }}"
- name : Create directory for documentation files
ansible.builtin.file :
path : "{{ documentation_user.doc_dir }}"
state : directory
owner : "{{ documentation_user.username }}"
group : "{{ documentation_user.username }}"
mode : "0777"
- name : Install Ansible PKI prerequisites.
ansible.builtin.pip :
name : pyOpenSSL
- name : Generate an OpenSSL private key
community.crypto.openssl_privatekey :
path : /etc/ssl/private/documentation.key
register : openssl_private_key_result
- name : Generate an OpenSSL Certificate Signing Request
community.crypto.openssl_csr :
path : /etc/ssl/private/documentation.csr
privatekey_path : /etc/ssl/private/documentation.key
register : openssl_csr_result
- name : Generate a Self Signed OpenSSL certificate
community.crypto.x509_certificate :
path : /etc/ssl/certs/documentation.crt
privatekey_path : /etc/ssl/private/documentation.key
csr_path : /etc/ssl/private/documentation.csr
provider : selfsigned
register : openssl_crt_result
- name : Deploy the rssh config file
ansible.builtin.template :
src : rssh.conf
dest : /etc/rssh.conf
mode : "0644"
- name : Deploy the nginx config file
ansible.builtin.template :
src : nginx.conf
dest : /etc/nginx/nginx.conf
validate : "nginx -t -c %s"
mode : "0644"
register : nginx_conf_result
- name : Restart nginx
ansible.builtin.service :
name : nginx
state : restarted
when : openssl_private_key_result.changed or openssl_csr_result.changed or openssl_crt_result.changed or nginx_conf_result.changed
7. Document your playbook Once you have tested and deployed your playbook, take some time to thoroughly document it. The future you will be thankful ;)
Creating clear documentation for your Ansible Playbooks has several advantages. It helps everyone involved to better understand and use them, improving overall collaboration and efficiency, and it also makes playbooks easier to maintain and update, as it provides a clear record of what the playbook does and how it works. Plus, well-documented playbooks can be easily repurposed for different tasks, saving both time and effort. It’s a crucial step worth taking.
8. Keep your playbook in check You’ve crafted a flawless playbook following all the steps, assuring reliable, secure, and efficient playbook execution. But remember, nothing is time-proof, especially playbooks. Regularly check if they still comply with best practices, your policies, and security standards. Using playbook scanning and management platforms like Steampunk Spotter, playbook maintainace becomes just another simple step in your automation workflow. Give it a try!
If you want to dive deeper into tips for writing high-quality Ansible Playbooks , give this a glance: