« Previous 1 2 3 Next »
Automation with Ansible
IT as Planned
Sample Playbook with Different Original States
Because automatic provisioning has the task of achieving a defined state on a computer but does not necessarily start with the same baseline, I changed one of the certificate files on node02
manually before calling ansible-playbook
again (Listing 5).
Listing 5
Modified ansible-playbook Run
ansible-demo-scripts$ ansible-playbook -i inventories/hosts.baremetal site.yml --ask-sudo-pass sudo password: ******* PLAY [baremetals] *** GATHERING FACTS *** ok: [node01.baremetal] ok: [control01.baremetal] ok: [control02.baremetal] ok: [node02.baremetal] TASK: [base | Install CenterDevice Root CA Certificates] *** ok: [control02.baremetal] => (item=centerdevice-intermediate-ca.crt) ok: [control01.baremetal] => (item=centerdevice-intermediate-ca.crt) ok: [node01.baremetal] => (item=centerdevice-intermediate-ca.crt) changed: [node02.baremetal] => (item=centerdevice-intermediate-ca.crt) ok: [control02.baremetal] => (item=centerdevice-root-ca.crt) ok: [control01.baremetal] => (item=centerdevice-root-ca.crt) ok: [node01.baremetal] => (item=centerdevice-root-ca.crt) ok: [node02.baremetal] => (item=centerdevice-root-ca.crt) TASK: [base | Update root certificate database] *** changed: [control02.baremetal] changed: [control01.baremetal] changed: [node01.baremetal] changed: [node02.baremetal] PLAY RECAP *** control01.baremetal : ok=3 changed=1 unreachable=0 failed=0 control02.baremetal : ok=3 changed=1 unreachable=0 failed=0 node01.baremetal : ok=3 changed=1 unreachable=0 failed=0 node02.baremetal : ok=3 changed=2 unreachable=0 failed=0
The log confirms that Ansible discovered the modified file and replaced it with the original copy, whereas the other files already had the expected content.
Using Variables and Templates
Thus far, I have copied static files to a remote computer and remotely triggered command execution. Now I'll show a more complex case in which all nodes synchronize their internal clocks with a dedicated time server. To allow this to happen, you need to install the matching package, set up the configuration, and make sure the daemon launches automatically at boot time.
Assuming you run your own time server, its address could be hardcoded in the NTP configuration file. To improve maintainability and centralize the configuration, you will instead extract its address, store the address in a variable, and ensure that Ansible dynamically adds it to the service configuration.
In Ansible, variables can have different scopes. They can be valid for a single host, a group of hosts, or an entire site. Because you want to synchronize all of the "baremetal" computers with the NTP servers in the example, the scope of the defined variable is the group. To do this, you need to create a baremetals.yml
file in the new ansible-demo-scripts/group_vars
directory:
--- # Variables for all baremetal hosts NTP_SERVERS: - 192.168.0.150 - 192.168.0.151 - 192.168.0.152
You can then add the steps shown in Listing 6 to the base.yml
role.
Listing 6
Additions to base.yml
- name: Install NTP daemon sudo: true apt: pkg=ntp state=present - name: Ensure NTP daemon autostart sudo: true service: name=ntp enabled=yes - name: Setup NTP daemon config sudo: true template: src=etc/ntpd.conf.j2 dest=/etc/ntpd.conf notify: Restart NTP daemon
The first task ensures that the NTP package is installed using apt
if it does not already exist. The next task automatically starts the service when the system boots if the installation package doesn't handle this task automatically or if autostart was disabled for other reasons in the meantime.
Things start to get more exciting in the final task: In a similar style to copy
, template
transfers a local file to the remote system. However, instead of just copying it one-to-one, the task evaluates the file as a Jinja2 template [4]. This gives you the option of dynamically composing the content based on your own variables and Ansible facts.
In line with the convention, Ansible searches in ansible-demo-scripts/roles/base/templates
for templates for the base
role. Here I am creating an etc/ntp.conf.j2 file
, which is a copy of the regular ntp.conf
but contains the modification shown in Listing 7.
Listing 7
Modifications to ntp.conf
... # # removed all server lines referring to default ubuntu time servers here. # these are our own servers: # {% for item in NTP_SERVERS %} server {{ item }} {% endfor %} ...
This fragment iterates through all the values listed previously in the group variable file in NTP_SERVERS
, thus effectively generating three new lines in the final configuration.
The last line (Listing 6) in the template
task, notify: Restart NTP daemon
, tells Ansible to perform a special action (a handler) if the content of the target file has changed compared with the previous version. This makes sense in that it restarts the service only if the configuration really has changed. This means that you can execute the playbook multiple times without causing unnecessary service interruptions.
This handler is a special task and the last element you need to define. It comes as little surprise that it is stored in ansible-demo-scripts/roles/base/handlers/main.yml
:
--- name: Restart NTP daemon sudo: true service: name=ntp state=restarted
Following these changes, the playbook can now be executed again (Listing 8).
Listing 8
Executing ansible-playbook Again
ansible-demo-scripts$ ansible-playbook -i inventories/hosts.baremetal site.yml --ask-sudo-pass sudo password: ******* PLAY [baremetals] *** GATHERING FACTS *** ... TASK: [base | Install ntp daemon] *** changed: [control01.baremetal] changed: [control02.baremetal] changed: [node02.baremetal] changed: [node01.baremetal] TASK: [base | Setup ntp daemon] *** changed: [control02.baremetal] changed: [node02.baremetal] changed: [control01.baremetal] changed: [node01.baremetal] NOTIFIED: [base | Restart ntp daemon] *** changed: [control02.baremetal] changed: [node01.baremetal] changed: [node02.baremetal] changed: [control01.baremetal] PLAY RECAP *** ...
The logfile shows that the package was installed, the configurations were set up, and, finally, the service was restarted. A quick glance at the node configuration shows that three lines with concrete IP addresses really have been created:
ansible-demo-scripts$ ssh node01.baremetal \ grep "server\ 192" /etc/ntp.conf server 192.168.0.150 server 192.168.0.151 server 192.168.0.152
Now the playbook can be run again to demonstrate that the daemon is not restarted unless the file content has changed (Listing 9).
Listing 9
Another Run of the Playbook
ansible-demo-scripts$ ansible-playbook -i inventories/hosts.baremetal site.yml --ask-sudo-pass sudo password: ******* PLAY [baremetals] *** GATHERING FACTS *** TASK: [base | Ensure ntp daemon autostart] *** ok: [control01.baremetal] ok: [node01.baremetal] ok: [node02.baremetal] ok: [control02.baremetal] TASK: [base | Setup ntp daemon config] *** ok: [control02.baremetal] ok: [node02.baremetal] ok: [control01.baremetal] ok: [node01.baremetal]
Because the configuration files were not modified, the handler was not notified, and the service just kept on running.
Integrating Sensitive Variables
One frequent problem in the use of version management systems relates to the access credentials that are needed as variable content in the scope of an Ansible run to store SSH keys automatically, for example, or be able to access a database.
Of course, you will not want to leave these in the clear in the Git repository for everyone to see. To prevent this happening, Ansible uses the concept of vaults , an encrypted form of YAML file for storing sensitive date of this type.
Using vaults is easy as pie: With the ansible-vault
command you can create and edit practical data vaults. When you run the playbook, you can pass in the decryption password at the command line or parse it from a file that can only be accessed by authorized users, preventing the password from appearing in the shell history.
ansible-demo-scripts$ vim .vaultpass ansible-demo-scripts$ ansible-vault \ create --vault-password-file \ ~/.vaultpass secrets.yml
If you now integrate the secrets.yml
file with the playbook, it expects you to provide the decryption password for the vault before running the commands:
ansible-demo-scripts$ ansible-playbook \ -i inventories/hosts.baremetal \ --vault-password-file ~/.vaultpass \ --ask-sudo-pass site.yml
The vault file itself can be checked into version management without any problems; it is AES256 encrypted and encoded as a pure text format.
« Previous 1 2 3 Next »
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.