Conceptualise your Ansible code

An idea on how to structure ansible code

Published on 20 Nov, 2023


Introduction

Like you need a concept of designing your software code base, you also need to have concept to design your ansible code.

A concept for your ansible code enables you to adjust or fix faster your code, as you know the places to look and what is dependet from it.
The development process itself will be faster, because the concept tells you where to place roles and how they are structured.
The onboarding of new members to your code base will be easier with a clear and documented structure.

A lot of these advantages will only come as your code base grows, but then you will be happy that you had a concept in place.

In this blog post I will give you an overview of our concept for ansible code.

Overall structure

The main parts of a ansible repository structure is defined by ansible, but how to structure our playbooks and your roles is up to you.
We structure by platform.
For example, we put our playbooks and roles in folders that represent the name of the platform like linux kubernetes or even elasticstack if we need to to do a lot for elasticstack.

Our folder structure for playbooks looks like this:

# inside playbook/ folder

├── k8s
│   ├── playbook.yml
│   └── SaaS
│       ├── playbook.yml
├── linux
│   ├── playbook.yml
│   ├── security
│   │   └── playbook.yml
└── utils
    ├── playbook.yml

The folder structure inside our roles folder has the same structure.

Roles

Roles are the centerpieces of ansible, here happends the dirty stuff, that’s why it is most important to have this decoupled and clearly written.

  • Roles should be idempotent, executing the roles multiple times should not lead to different results

    • In case it would be best if there would be no changed tasks after running the task once.
  • Roles should be do one thing and they should do it good

    • Don’t create roles, like common or security, one role for each logical step in common would be the better. These “common” roles can then be called from a playbook called common
  • Defining a naming schema for your roles will help you a lot to navigate through your code base

    • e.g. install_gitlab, configure_gitlab, set_hostname
  • Roles should not represent workflows

    • It might be okay if a role has to prepare data for other roles,
  • Don’t split the installation of the same tool on different platforms in separate roles, check inside the role and call the correct tasks

  • Don’t copy code!

    • If you need the same logic on more then one role, think of creating a utility role or a pre task in a shared playbook.
  • Be cautious if you’re using inventory groups, it can lead to a thight coupling of your inventories and the roles.

  • Think of giving the state from outside, then you can not only ensure that for example a tool is installed (present), you can also ensure that a tool gets uninstalled (absent)

Testing

Test your roles isolated from other roles.
If you need first execute other roles to test your role, you might need to rethink the design of your role.

Playbooks

Not every role needs a playbook. Playbooks are the orchestration of roles, they should contain workflows to set up an application and your system.

  • Avoid using to many tasks in playbooks

  • Aim to have one workflow per playbook

    • You can have orchestration playbooks that call other playbooks
  • Playbooks should be able to be executed from the host that they configure after they are executed once

    • You need this if you plan to use an automation server like AWX, which you also want to configure via ansible
  • Avoid adding variables to playbooks, use group_vars or inventories instead

  • “Utility playbooks” can be a nice replacement for a bunch of small scripts, but don’t rely too heavily on these

Testing

Testing a playbook is more like integration testing of the infrastructure, you will need more preparation and you need to make sure that everything is set up accordingly.
It is also possible that playbooks are not able to be tested on their own and that you will need more like end 2 end tests that execute multiple playbooks.

Inventories

Designing inventories can be very challenging and is very individual.

The main thing to keep in mind is to properly handle variables.

  • Prefer putting variables in to group_vars instead of your inventory
  • Try to avoid host_vars, as too many of them can get overwhelming, use group_vars wherenever possible

Client handling

I prefer to create an own project for each client and share the roles and if necessary the playbooks.
Handling multiple clients in one repository can get very junky after some time.

Last but not least

If you find a exception to your current concept, find a general way to tackle the exception and extend your concept.
If you add exception after exception to your code, it will get flaky and the advantages of a concrete concept will be gone.

Sometimes changing rules of the concept is nessesary. But if you change something in the concept, you need to adjust all affected places of the change. That doesn’t mean you need to do this directly, but you need to keep track of these changes and adjust everything as quickly as you can.