Ansible - Inventory, Variables, and Modules (Part 2.2 of 14)

Welcome back. Today we will cover Host and Group variables, followed by modules. I originally planned to cover plays in this section, but realized it makes more sense to cover when we cover playbooks (part 5).

Host and Group Vars

Sometimes you need to store additional information associated with a host or a group. Ansible supports this through variables (referred to as vars in the Ansible documentation). Let’s take a look at some host variable examples.

1
2
3
4
5
[proddatabase_servers]
sql1.softwarecorp.com sql_port=8888
[devdatabase_servers]
devsql1.softwarecorp.com sql_port=1433

In the above example, we’ve assigned a variable named sql_port to the host sql1.softwarecorp.com with a value of 8888 and a variable of the same name to devsql1.softwarecorp.com with a value of 1433. While this can be useful, it’s more likely that you will be setting variables at a group level instead on individual hosts.

1
2
3
4
5
6
7
8
[prodapp_servers]
appserver1.softwarecorp.com
appserver2.softwarecorp.com
[prodapp_servers:vars]
http_port=443
proxy=proxy.softwarecorp.com
maxRequestPerChild=900

In the above example, we set 3 variables (http_port, proxy and maxRequestPerChild) on the prodapp_server group. These variables are applied to each host that is part of the prodapp_server group. There are a few rules around variables to keep in mind. First we’ll look at variables precedence is handled.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[appserver:children]
prodapp_servers
devapp_servers
[prodapp_servers]
appserver1.softwarecorp.com port=443 proxy=proxy2.softwarecorp.com
appserver2.softwarecorp.com port=443
[devapp_servers]
devappserver1.softwarecorp.com
devappserver2.softwarecorp.com
[production_servers:children]
appserver
[production_server:vars]
proxy=proxy.softwarecorp.com

Variables have an order of precedence, with host variables having the highest precedence. Group precedence lowers as you move further from the host, so a variable defined on the host appserver1.softwarecorp.com would override the value set on the production_servers group, but would not override a variable set on the appserver group. The next rule is how variables work when hosts are in multiple groups.

1
2
3
4
5
6
7
8
9
10
11
12
[app_servers]
appserver1.softwarecorp.com port=443
appserver2.softwarecorp.com port=443
devappserver1.softwarecorp.com
devappserver2.softwarecorp.com
[production_servers]
webserver1.softwarecorp.com
webserver2.softwarecorp.com
sql1.softwarecorp.com
appserver1.softwarecorp.com proxy=proxy.softwarecorp.com
appserver2.softwarecorp.com proxy=proxy.softwarecorp.com

Hosts in multiple groups will have their variables merged together, so in the above example appserver1.softwarecorp.com will have proxy and port variables.

Ansible will allow you to define group and host variables in their own file or files in addition to setting them in the inventory file. These files can have no extension or end in ‘.yml’, ‘.yaml’, or ‘.json’. Assuming you have an inventory path (an ansible configuration we will cover in a later part) of /etc/ansible/hosts you can create host and group variable files in directories as follows.

1
2
3
/etc/ansible/host_vars/appserver1.softwarecorp.com
/etc/ansible/group_vars/production_servers
/etc/ansible/group_vars/all

Ansible will apply all variables defined in the directory (matching the above schema) /etc/ansible/host_vars/appserver1.softwarecorp.com to the host appserver1.softwarecorp.com and /etc/ansible/group_vars/production_server and /wtc/ansible/group_vars/all to all members of the production_server group (and child groups). Note that we’ve used the special all group covered in the previous post to assign variables to all hosts.

Lastly, we still need to know how to define variables within these files. Fortunately for us, this process is pretty straightforward.

1
2
3
--- production_server
port: 888
proxy: proxy.softwarecorp.com

As you can see it’s simply a key value pair separated by a colon (:) and a space. The three dashes (—) indicate a comment and are ignored by Ansible.

Modules

Modules are the core working component of Ansible. Modules are commands that perform some operation and can return information or data. There’s a extensive library of existing modules. Ansible has modules for managing networks, windows, cloud resources(AWS, Azure, etc), source control and more. You can find a full listing in the Module Index Should you need a module that doesn’t exist, you can create your own using powershell or python. We will cover this in Building modules (Part 11).

Modules can be called as part of a play (covered below) or ad-hoc via the shell. For these examples, we are going to assume we have the inventory file from the last post. Lets take a look at 3 examples of ad-hoc ansible commands.

1
2
3
$ ansible all -m ping
$ ansible webservers ping
$ ansible devweb_servers,devapp_server ping

Well that’s pretty simple, but lets review. The first command is passing the argument all. This tells ansible to run the module against all hosts in our inventory file (This is the special all group covered in the previous post). In the second example, we are passing webservers and in the third example we are passing both devweb_servers and devapp_server. Ansible will use the host defined in these groups to run the command against. Next we pass -m followed by the module name (in this example the module is ping). I bet some of you noticed that in the second and third examples, -m was omitted. This is because modules that take no parameters do not require -m. Let move to something more complex.

1
$ ansible devwebservers -a "/sbin/reboot" -f 10

Now this is a bit more complex. It should be clear by now, but devwebservers is telling ansible to use the devwebservers group in our inventory file. You’ll notice that we do not have a -m or a module. When this is omitted, Ansible will use the command module by default. Next we pass -a followed by '/sbin/reboot'. This is passing the argument to the command module. This will run the command /sbin/reboot on each host in the devwebservers group. Next up is -f which specifies that we want to run this command in 10 parallel forks. Now that’s a pretty useful parameter if you need a nightly/weekly system reboot against a large number of machines. Since this is clearly not a windows command, it would fail if we had a windows machine in our inventory list. Lets move on to another example.

1
ansible all -m win_chocolatey -e 'state="latest"'

Now this is a favorite of mine. Lets break it down. We are targeting all servers in our inventory file running the module [![win_chocolatey] (http://docs.ansible.com/ansible/latest/win_chocolatey_module.html)]. Next we have -e. This allows up to specific variables in a key/value or YAML/JSON format for use by the module. In this example, we are telling the win_chocolatey module we want the state to be the latest (which will attempt to update all chocolatey installs).

You can also get documentation on a specific module by using the ansible-doc command. Let look at what we get if we user the win_chocolatey module.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
[root@AnsibleVM ~]# ansible-doc win_chocolatey
> WIN_CHOCOLATEY (/usr/lib/python2.7/site-packages/ansible/modules/windows/win_chocolatey.py)
Installs packages using Chocolatey (http://chocolatey.org/). If Chocolatey is missing from the system, the module will install it. List of packages can be found at http://chocolatey.org/packages
Options (= is mandatory):
- allow_empty_checksums
Allow empty checksums to be used.
[Default: False]
- force
Forces install of the package (even if it already exists).
Using `force' will cause ansible to always report that a change was made.
(Choices: True, False)[Default: False]
- ignore_checksums
Ignore checksums altogether.
[Default: False]
- ignore_dependencies
Ignore dependencies, only install/upgrade the package itself.
[Default: False]
- install_args
Arguments to pass to the native installer.
[Default: (null)]
= name
Name of the package to be installed.
- params
Parameters to pass to the package
[Default: (null)]
- source
Specify source rather than using default chocolatey repository.
[Default: (null)]
- state
State of the package on the system.
(Choices: present, absent, latest, reinstalled)[Default: present]
- timeout
The time to allow chocolatey to finish before timing out.
[Default: 2700]
- upgrade
If package is already installed it, try to upgrade to the latest version or to the specified version.
As of Ansible v2.3 this is deprecated, set parameter `state' to "latest" for the same result.
(Choices: True, False)[Default: False]
- version
Specific version of the package to be installed.
Ignored when `state' is set to "absent".
[Default: (null)]
EXAMPLES:
# Install git
win_chocolatey:
name: git
state: present
# Upgrade installed packages
win_chocolatey:
name: all
state: latest
# Install notepadplusplus version 6.6
win_chocolatey:
name: notepadplusplus.install
version: '6.6'
# Install git from specified repository
win_chocolatey:
name: git
source: https://someserver/api/v2/
# Uninstall git
win_chocolatey:
name: git
state: absent
MAINTAINERS: Trond Hindenes (@trondhindenes), Peter Mounce (@petemounce), Pepe Barbe (@elventear), Adam Keech (@smadam813)
METADATA:
Status: ['preview']
Supported_by: curated

Now this tells us a lot of information about what parameters can be used and some example of calling the module.

Next time we’ll be installing Ansible and creating a couple machines for testing