
Lead Image © Kittipong Jirasukhanont, 123RF.com
Explore automation-as-code with Ansible
Automated
The everything-as-code trend is popular in IT today. The complete hardware and software configuration is stored in text files, and an automation tool rolls everything out. Fortunately, arcane file formats (e.g., sendmail.cf
) are pretty much a thing of the past; instead, you can expect easily readable formats such as YAML and perhaps JSON. The information in these files can usually be converted into one of the other formats without loss.
YAML is the preferred format of the Ansible automation tool, which is used here for as-code automation. Thanks to its modular structure, Ansible can control pretty much everything in the data center and the cloud, including rolling itself out from its command line. This scenario might sound strange, but in the automation-as-code example, it makes sense to roll out a complete AWX setup in this way on Kubernetes and configure the AWX server with all the required settings.
The code here can act as a skeleton, or a template if you prefer, for your own more extensive as-code scenarios, as well as for cases in which you roll out and configure other applications. Because a huge amount of code is involved, I can only describe fragments of it in detail here. The complete code is available on GitHub [1]. Incidentally, it works with both the free AWX and, with a few minor changes, the commercial Ansible automation controller implementation.
Two Approaches
The first approach to automating the AWX rollout involves setting up an empty AWX server, configuring it in the web user interface (UI), and then exporting the complete configuration as a JSON or YAML file. You can then upload the results to a new, empty AWX server. The approach is mainly used if you want to clone a setup or migrate to a new platform or a new release (e.g., Ansible Tower to Ansible controller).
However, this approach does not lend itself to as-code scenarios, because the YAML files you need to export can be fairly large and confusing. With AWX as code, you will be looking to create your AWX configuration directly in YAML, which you will then be able to roll out automatically, which is why the code used here needs to be simpler, more clear-cut, and readable for the admin.
The second approach uses a playbook that fetches the configuration data from a YAML document. You can format and expand the document to suit your own requirements, which makes the YAML document far simpler, at the price of no longer being compatible with the exported format. Of course, the as-code idea is to roll out the installation from the code and not go down the opposite route.
Code vs. Config
One basic rule when using Ansible is that you want to avoid assigning values to variables in your Ansible playbook, which would massively affect the playbook's flexibility. Therefore, variable declarations should always be kept separate from the actual Ansible code.
In this case, you will use two separate files: vars.yaml
contains the complete configuration of the AWX server to be rolled out, and secrets.yaml
stores the passwords and keys you need for your AWX service's credentials.
Once you have entered all the access credentials and passwords in readable form in the YAML file, you need to encrypt secrets.yaml
with Ansible vault. When executing the code, and only then, you can unlock access with the --ask-vault-pass
option.
Hierarchical Variables and Dictionaries
The configuration variables for the as-code scenario are grouped hierarchically:
scm: name:"Github" User: "user01" url: "https://www.github.com"
The playbook then accesses variables like {{ scm.url }}
, which means you can easily see the sub-section to which a variable belongs. If you want to extend the code later, you can add sub-variables at any time.
The second entity in the variables area is the dictionary. With this unnumbered array, Ansible can execute a loop for each element:
user: kirk: firstname: "James T" rank: "Admiral" spock: rank: "Captain" uhura: firstname: "Nyota" rank: "lieutenant"
An Ansible task now uses,
"with_dictionary: "{{ user }}"
or, in line with the more modern (but not necessarily better) notation,
"loop: "{{ user | dict2items }}"
to iterate through a loop. In a loop task, Ansible then accesses parts of the dictionary with items
, which is a hierarchical loop variable. In this example, the first execution of a task in the loop would give you the following variable content:
item.key = "kirk" item.value.firstname = "James T" item.value.rank = "Admiral"
When declaring a dictionary, you do not need to declare all of a variable's hierarchies, as in the case with the spock
entry. That said, the playbook task must be able to cope with empty variables in this case. Ansible has a default
function for this:
- ansible.builtin.debug: msg: "firstname : {{ item.value.firstname |default('Mister') }}"
If the item.value.firstname
variable is empty in this case, Ansible uses Mister
instead. This approach is used later to configure the SSH ports of the inventory entries.
Armed with these simple tips for formatting YAML dictionaries and using loops to evaluate them in Ansible, you can now basically configure arbitrary hardware and software with Ansible. The AWX rollout in this example is just one of many options.
Buy this article as PDF
(incl. VAT)
Buy ADMIN Magazine
Subscribe to our ADMIN Newsletters
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Most Popular
Support Our Work
ADMIN content is made possible with support from readers like you. Please consider contributing when you've found an article to be beneficial.
