Ansible Part II: Using Modules

On the first part of this series, you learned how to setup and configure Ansible. Part II explains how Ansible modules work and how you can use Ansible in ad-hoc mode from the command line.

Before you proceed, you should be able to test the basic of Ansible. As demonstrated on the first part, ansible is a push-based automation tool, which means the connection is initiated from your “master server”, which technically can just be another workstation or an SSH bastion host since ansible no specific definition of a Master and Client.

The following sections are the common Ansible modules that you will probably be using. This is not an exhaustive list, as more modules are explained on the Anisible documentation.

Command Module

This is the safest module to execute remote commands on the client machine. As with most Ansible modules, it requires Python to be installed on the client. When Ansible executes commands using the Command Module, it does not process those commands through the user’s shell. This means some variables like $HOME are not available. It also means stream functions (redirects, pipes) don’t work. If you don’t need to redirect output or to reference the user’s home directory as a shell variable, the Command Module is what you want to use. To invoke the Command Module in ad-hoc mode, do something like this:

# ansible host_or_groupname -m command -a "whoami"

~$ ansible webservers -m command -a "whoami"
ansible-client1 | CHANGED | rc=0 >>
ansible
ansible-client2 | CHANGED | rc=0 >>
ansible

Your output should show SUCCESS for each host referenced and then return the user name that the user used to log in. You’ll notice that the user is not root, unless that’s the user you used to connect to the client computer.

If you want to see the elevated user, you’ll add another argument to the ansible command. You can add -b in order to “become” the elevated user (or the sudo user). So, if you were to run the same command as above with a “-b” flag:

 # ansible host_or_groupname -b -m command -a "whoami" 

~$ ansible webservers -b -m command -a "whoami"
ansible-client1 | CHANGED | rc=0 >>
root
ansible-client2 | CHANGED | rc=0 >>
root

You should see a similar result, but the whoami results should say root instead of the user you used to connect. That flag is important to use, especially if you try to run remote commands that require root access.

Shell Module

The Shell Module is also used to execute remote commands similar to the command module.  The key difference is that it uses remote user’s environment, so if there’s something misconfigured with the user’s account, it might cause problems when your command depends on those variables. The Shell module allows you use shell special characters like redirects, pipes and multiline (;) commands.  Here’s the below example:

# ansible host_or_groupname -m command -a " uptime; whoami > /tmp/myname.txt"

should result in an error such as not a valid argument or no such file or directory. Since the Command Module doesn’t run inside any shell, it interprets the semicolon character as something you’re trying to pass to the whoami command. If you use the Shell Module, however, you have no problems:

# ansible host_or_groupname -m shell -a " uptime; whoami > /tmp/myname.txt"

This should execute and give you a SUCCESS message for each host. And on the remote machines there should be a file called myname.txt on/tmp.

The Raw Module

Functionally, the Raw Module works like the Shell Module. With the Raw module, ansible just executes the command over SSH directly without error checking or syntax checking (similar to how you pass a command string to an SSH connection). So while the Shell Module will use /bin/sh by default, the Raw Module just uses whatever the user’s personal default shell might be. This is useful because it does not require Python on the remote computer—at all. Although it’s true that most servers have Python installed by default, or easily could have it installed, many embedded devices don’t and can’t have Python installed. With ansible, as long as you have SSH connection, you can still manage these types of machines.

Copy Module

Although it’s certainly possible to do file and folder manipulation with the Command and Shell Modules, Ansible includes a module specifically for copying files to the server. Even though it requires learning a new syntax for copying files, it is recommended to use this module because Ansible will check to see whether a file exists, or whether it’s the same file and it can make backups of existing files. This is very helpful when updating configuration files. The syntax is a little more complicated than with Command, Shell or Raw but it’s still easy to understand:

 # ansible host_or_groupname -b -m copy -a "src=/etc/updated-ntp.conf dest=/etc/ntp.conf owner=root group=root mode=0644 backup=yes"

:~$ ansible webservers -b -m copy -a "src=/etc/updated-ntp.conf dest=/etc/ntp.conf owner=root group=root mode=0644 backup=yes"
ansible-client2 | CHANGED => {
"changed": true,
"checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
"dest": "/etc/ntp.conf",
"gid": 0,
"group": "root",
"md5sum": "d41d8cd98f00b204e9800998ecf8427e",
"mode": "0644",
"owner": "root",
"size": 0,
"src": "/home/ansible/.ansible/tmp/ansible-tmp-1549505160.66-213032951624251/source",
"state": "file",
"uid": 0
}
ansible-client1 | CHANGED => {
"changed": true,
"checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
"dest": "/etc/ntp.conf",
"gid": 0,
"group": "root",
"md5sum": "d41d8cd98f00b204e9800998ecf8427e",
"mode": "0644",
"owner": "root",
"size": 0,
"src": "/home/ansible/.ansible/tmp/ansible-tmp-1549505160.65-31824770893343/source",
"state": "file",
"uid": 0
}

This will look for a file called updated-ntp.conf and then copy it to each host. On the remote system, the file will be put in /etc/ntp.conf, and if a file already exists, and it’s different, the original will be backed up with a date extension. If the files are the same, Ansible won’t make any changes.

It’s also important to note that the Copy Module supports copying remote files to other locations on the remote filesystem using the remote_src=true directive.

File Module

The File Module has a lot in common with the Copy Module, but File Module does all its actions on the remote machine, so src and dest are all references to the remote filesystem. The File Module often is used for creating directories, creating links or deleting remote files and folders. The following will simply create a folder named /etc/newfolder on the remote servers and set the mode:

# ansible host_or_groupname -b -m file -a "path=/etc/newfolder state=directory mode=0755"

You can set the owner and group, along with a lot ofother options. You’ll use this module usually to create a folder or symbolically link a file. To create a symlink:

# ansible host_or_groupname -b -m file -a "src=/etc/ntp.conf dest=/home/ansible/ntp.conf owner=root group=root state=link"

Note that the state directive is how you inform Ansible what you actually want to do. There are several state options:

  • link — create symlink.
  • directory — create directory.
  • hard — create hardlink.
  • touch — create empty file.
  • absent — delete file or directory recursively.

This might seem a bit redundant especially when you easily could do the same with a Command or Shell Module command, but the clarity of using the appropriate module makes it more difficult to make mistakes.

More importantly learning these commands in ad-hoc mode will make playbooks, which consist of many commands, easier to understand which will be covered on the next Sections.

Package Module

If you manage multiple distributions it can be tricky to handle the various package managers. In Ansible there are specific modules for apt and yum, but there’s also a generic module called “package” that will install on the remote computer regardless of whether it’s Red Hat-based or Debian/Ubuntu-based.

Unfortunately, while Ansible usually can detect the type of package manager it needs to use, it doesn’t have a way to fix packages with different names – for example Apache. On Red Hat-based systems, the package is “httpd”, but on Debian/Ubuntu systems, it’s “apache2”. That means some more complex things need to happen in order to install the correct package automatically. The individual modules, however, are very easy to use. I find myself just using apt or yum as appropriate, just like when I manually manage servers. Here’s an apt example:

# ansible host_or_groupname -b -m apt -a "update_cache=yes name=apache2 state=latest"

:~$ ansible webservers -b -m apt -a "update_cache=yes name=apache2 state=latest"
ansible-client2 | SUCCESS => {
"cache_update_time": 1549505388,
"cache_updated": true,
"changed": false
}
ansible-client1 | SUCCESS => {
"cache_update_time": 1549505391,
"cache_updated": true,
"changed": false
}

With this one simple line, all the host machines will run apt-get update (that’s the update_cache directive at work), then install apache2’s latest version including any dependencies required. Much like the File Module, the state directive has a few options:

  • latest — get the latest version, upgrading existing if needed.
  • absent — remove package if installed.
  • present — make sure package is installed, but don’t upgrade existing.

The Yum Module works similarly to the Apt Module, but I generally don’t bother with the update_cache directive, because yum updates automatically. Installing Apache on a Red Hat-based system looks like this:

# ansible host_or_groupname -b -m yum -a "name=httpd state=present"

The difference with this example is that if Apache is already installed, it won’t update, even if an update is available. Sometimes updating to the latest version isn’t want you want, so this stops that from accidentally happening.

Service Module

The Service module is used to control the services/daemons running on the remote systems. Supported init systems include BSD init, OpenRC, SysV, Solaris SMF, systemd, upstart.

This module is usually useful along with the package module. Here’s an example syntax.

# ansible webservers -b -m service -a "name=apache2 enabled=yes state=started"

The command will make sure that apache2 service is running and is enabled on startup.

User Module

The user module is dedicated to creating/deleting/modifying local user accounts and groups on remote systems.

To create a user, use the following syntax:

# ansible -b -m user
-a "name=testuser createhome=yes shell=/bin/bash groups=admin append=yes"
webservers

This will create the account testuser on webservers list, creating a home folder, assign the bash shell and adding the admin group a supplementary group.

To remove a user, here’s an example:

# ansible -b -m
user -a "name=test remove=yes state=absent" webservers

The state=absent will remove the user account and the remove=yes option will also remove the home folders/mail files.

Setup Module

It’s possible to use ad-hoc mode to peek at the sorts information Ansible gathers which are called “facts”. If you run the setup module, it will show you all the details from a remote system:

 # ansible host_or_groupname -b -m setup

That will output a lot of information. You can scroll through them all to see the vast amount of information Ansible pulls from the host machines. If you want, you can filter the results:

# ansible host_or_groupname -b -m setup -a "filter=*memory*"

~$ ansible webservers -b -m setup -a "filter=ansible_memory_mb"
ansible-client2 | SUCCESS => {
"ansible_facts": {
"ansible_memory_mb": {
"nocache": {
"free": 713,
"used": 740
},
.....
.....
}
ansible-client1 | SUCCESS => {
"ansible_facts": {
"ansible_memory_mb": {
"nocache": {
"free": 712,
"used": 741
},
.....
.....
}

That should just return a single variable, ansible_memory_mb, which will give memory related information to the remote servers. You can try to filter with “ansible_os_family” which will determine if the remote system is Red Hat-based or Debian-based. 

This is helpful when you start building more complex Ansible setups with playbooks, you can insert some logic and conditionals in order to use yum module and apt module when installing packages on appropriate systems. It’s important to keep these “facts” variables in mind when building playbooks which is discussed on the next part.

See you on Part III: Using Playbooks.

Ansible Part I: Installation and Setup
Ansible Part II: Using Modules
Ansible Part III: Using Playbooks
Ansible Part IV: Roles Overview

—————————————–

– masterkenneth

Leave a Reply

Your email address will not be published. Required fields are marked *