Ansible Modules and Ad-Hoc Commands

Modules


When you want to run something on the remote managed hosts using Ansible, you perform either a single task or a set of tasks. For a single task, you use ad-hoc commands and for a set of tasks, you will need playbooks.

You use ad-hoc commands if you want to do something really quick for only one time that does not necessarily needs a playbook, like powering off or rebooting a group of servers or stopping a specific service.

Both Ansible ad-hoc commands and playbooks use modules, which are unites of code that performs the actual task. There are many modules in Ansible that can be used to run all kind of tasks.

Use the ansible-doc -l command to get the list of all modules available.

$ ansible-doc -l
a10_server                       Manage A10 Networks AX/SoftAX/Thunder/vThunder devices' server object.
a10_server_axapi3                Manage A10 Networks AX/SoftAX/Thunder/vThunder devices
a10_service_group                Manage A10 Networks AX/SoftAX/Thunder/vThunder devices' service groups.
a10_virtual_server               Manage A10 Networks AX/SoftAX/Thunder/vThunder devices' virtual servers.
accelerate                       Enable accelerated mode on remote node
aci_aaa_user                     Manage AAA users (aaa:User)
...
win_service                      Manage and query Windows services
win_share                        Manage Windows shares
win_shell                        Execute shell commands on target hosts.
win_shortcut                     Manage shortcuts on Windows
win_stat                         returns information about a Windows file
win_tempfile                     Creates temporary files and directories.
...

To get more information about a module, you can use ansible-doc along with the module name. This is a very useful command as it provides a full description of the module, all arguments that can be used with it, a clear and concise help on how to use them, and some very helpful examples.

Here we want to get more information about the win_service module, which is used to manage Windows services.

$ ansible-doc win_service

> WIN_SERVICE    (/usr/lib/python2.7/site-packages/ansible/modules/windows/win_service.py)

		Manage and query Windows services. For non-Windows targets, use the [service] module instead.

OPTIONS (= is mandatory):

- dependencies
		A list of service dependencies to set for this particular service.
		This should be a list of service names and not the display name of the service.
		This works by `dependency_action' to either add/remove or set the services in this list.
		[Default: (null)]
		version_added: 2.3

- dependency_action
		Used in conjunction with `dependency' to either add the dependencies to the existing service dependencies.
		Remove the dependencies to the existing dependencies.
		Set the dependencies to only the values in the list replacing the existing dependencies.
		(Choices: set, add, remove)[Default: set]
		version_added: 2.3
...

A practical example

Let’s sat we want to find out the module syntax that needs to be run to install the nginx server on your RHEL/CentOS remote server, so it is already assumed that yum package manager is used for package installation.

We first start by searching in the Ansible modules documentation for any yum module.

$ ansible-doc -l | grep yum
yum                                                  Manages packages with the `yum' package manager
yum_repository                                       Add or remove YUM repositories

We will then have a closer look on how this module is used and what are its arguments with ansible-doc yum. This command will provide detailed information but the ones that are of interest for us is the name and state arguments. The mandatory arguments like name are preceded by = sign.

$ ansible-doc yum

> YUM    (/usr/lib/python2.7/site-packages/ansible/modules/packaging/os/yum.py)

		Installs, upgrade, downgrades, removes, and lists packages and groups with the `yum' package manager. This module only works on Python 2. If
		you require Python 3 support see the [dnf] module.

OPTIONS (= is mandatory):

- allow_downgrade
		Specify if the named package and version is allowed to downgrade a maybe already installed higher version of that package. Note that setting
		allow_downgrade=True can make this module behave in a non-idempotent way. The task could end up with a set of packages that does not match
...
= name
		A package name , or package specifier with version, like `name-1.0'.
		If a previous version is specified, the task also needs to turn `allow_downgrade' on. See the `allow_downgrade' documentation for caveats
		with downgrading packages.
		When using state=latest, this can be '*' which means run `yum -y update'.
		You can also pass a url or a local path to a rpm file (using state=present). To operate on several packages this can accept a comma separated
		list of packages or (as of 2.0) a list of packages.
		(Aliases: pkg)
...
- state
		Whether to install (`present' or `installed', `latest'), or remove (`absent' or `removed') a package.
		(Choices: absent, installed, latest, present, removed)[Default: present]
...

Both argments are mandatory here. name will tell Ansible which package is needed and state with the value “installed” to effectively install the package. This will be enough to install the nginx server. Below is the command:

$ ansible appservers -m yum -b -a "name=nginx state=installed"
  • -m is used to provide the name of the module.
  • -a is used to pass the module arguments.
  • -b is for privilege escalation. If used, Ansible will use root privileges to run the command.

If you have any doubts to figure out the minimal syntax for your command to work, you can find some great examples at the end of the ansible-doc. These are mainly for describing playbooks syntax, but you can also use them for ad-hoc commands. You just need to write the module arguments in one line and replace the collon (:) with equal sign (=) as done in the previous example.

...
EXAMPLES:
- name: install the latest version of Apache
  yum:
	name: httpd
	state: latest

- name: remove the Apache package
  yum:
	name: httpd
	state: absent

- name: install the latest version of Apache from the testing repo
  yum:
	name: httpd
	enablerepo: testing
	state: present
...	

 

Ad-Hoc Commands


Ansible ad-hoc commands are run using the “ansible” command, followed by the IP, hostname, or hostgroup on which the task needs to be run, then the actual module and its arguments, if any.

For example, the following ad-hoc command will test the connectivity to the all group

$ ansible all -m ping
server2.contoso.local | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
server1.contoso.local | SUCCESS => {
    "changed": false,
    "ping": "pong"

The only thing ping module does is checking that Ansible is able to start an SSH session with the server. The “changed”: false part of the output tells us that executing the module did not change the state of the server.

This one will check the uptime of the appservers group.

$ ansible appservers -m command -a "uptime"
server2.contoso.local | SUCCESS | rc=0 >>
 17:07:04 up  1:16,  2 users,  load average: 0.08, 0.04, 0.05

The command module is so commonly used that it’s the default module, so we can omit it.

$ ansible appservers -a "uptime"
server2.contoso.local | SUCCESS | rc=0 >>
 17:14:03 up  1:23,  2 users,  load average: 0.16, 0.05, 0.06

This following command will reboot all servers in appservers group.

$ ansible appservers -m command -a "/sbin/reboot -t now"

OF COURSE YOU WILL NOT USE THIS COMMAND IN A PRODUCTION ENVIRONMENT, UNLESS YOU DO KNOW EXACTLY WHAT YOU ARE DOING.

If we need root access access, we pass in the -b argument to become root and the -K argument to allow root password input while executing the ad-hoc command. Obviously, with the use of a configuration file, there is no need to pass these arguments.

Let’s run some common admin tasks using ad-hoc commands.

  • Installing Apache on the the webservers group
]$ ansible webservers -b -K -m yum -a "name=httpd state=installed"
SUDO password:
server1.contoso.local | SUCCESS => {
    "changed": true,
    "msg": "",
    "rc": 0,
    "results": [
        "Loaded plugins: fastestmirror, langpacks\nLoading mirror speeds from cached hostfile\n * base: mirrors.coreix.net\n * epel: mirrors.aliyun.com\n * extras: centos.kw.zain.com\n * updates: centos.kw.zain.com\nResolving Dependencies\n--> Running transaction check\n---> Package httpd.x86_64 0:2.4.6-80.el7.centos.1 will be installed\n--> Processing Dependency: httpd-tools = 2.4.6-80.el7.centos.1 for package: httpd-2.4.6-80.el7.centos.1.x86_64\n--> Running transaction check\n---> Package httpd-tools.x86_64 0:2.4.6-67.el7.centos.6 will be updated\n---> Package httpd-tools.x86_64 0:2.4.6-80.el7.centos.1 will be an update\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package           Arch         Version                     Repository     Size\n================================================================================\nInstalling:\n httpd             x86_64       2.4.6-80.el7.centos.1       updates       2.7 M\nUpdating for dependencies:\n httpd-tools       x86_64       2.4.6-80.el7.centos.1       updates        90 k\n\nTransaction Summary\n================================================================================\nInstall  1 Package\nUpgrade             ( 1 Dependent package)\n\nTotal size: 2.8 M\nDownloading packages:\nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n  Updating   : httpd-tools-2.4.6-80.el7.centos.1.x86_64                     1/3 \n  Installing : httpd-2.4.6-80.el7.centos.1.x86_64                           2/3 \n  Cleanup    : httpd-tools-2.4.6-67.el7.centos.6.x86_64                     3/3 \n  Verifying  : httpd-tools-2.4.6-80.el7.centos.1.x86_64                     1/3 \n  Verifying  : httpd-2.4.6-80.el7.centos.1.x86_64                           2/3 \n  Verifying  : httpd-tools-2.4.6-67.el7.centos.6.x86_64                     3/3 \n\nInstalled:\n  httpd.x86_64 0:2.4.6-80.el7.centos.1                                          \n\nDependency Updated:\n  httpd-tools.x86_64 0:2.4.6-80.el7.centos.1                                    \n\nComplete!\n"
    ]
}

We can check the successful installation on the remote host

$ systemctl status httpd
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: man:httpd(8)
           man:apachectl(8)
  • Starting the httpd service on all servers in the webservers group
$ ansible webservers -b -m service -a "name=httpd state=started"
server1.contoso.local | SUCCESS => {
    "changed": true,
    "name": "httpd",
    "state": "started",
    "status": {
        "ActiveEnterTimestampMonotonic": "0",
        "ActiveExitTimestampMonotonic": "0",
        "ActiveState": "inactive",
        "After": "nss-lookup.target -.mount network.target system.slice basic.target systemd-journald.socket remote-fs.target tmp.mount",
        "AllowIsolate": "no",
        "AmbientCapabilities": "0",
		...
        "TimeoutStopUSec": "1min 30s",
        "TimerSlackNSec": "50000",
        "Transient": "no",
        "Type": "notify",
        "UMask": "0022",
        "UnitFilePreset": "disabled",
        "UnitFileState": "disabled",
        "Wants": "system.slice",
        "WatchdogTimestampMonotonic": "0",
        "WatchdogUSec": "0"
    }
}
  • Creating files and directories

Another example would be creating a files or directories on remote hosts using the file module. Again, if you have any doubt about which module does which task, the ansible-doc -l and the ansible-doc file commands will be your friends.

$ ansible-doc -l | grep file
...
file                                                 Sets attributes of files
filesystem                                           Makes a filesystem
...
$ ansible-doc file
> FILE    (/usr/lib/python2.7/site-packages/ansible/modules/files/file.py)

        Sets attributes of files, symlinks, and directories, or removes files/symlinks/directories. Many
        other modules support the same options as the `file' module - including [copy], [template], and
        [assemble]. For Windows targets, use the [win_file] module instead.
...

In this example, the file is called /tmp/testFile.txt and will be created in the appservers group. The state argument is used here creates the file if it does not exist.

$ ansible appservers -m file -a 'path=/tmp/testFile.txt state=touch'
server2.contoso.local | SUCCESS => {
    "changed": true,
    "dest": "/tmp/testFile.txt",
    "gid": 0,
    "group": "root",
    "mode": "0644",
    "owner": "root",
    "secontext": "unconfined_u:object_r:user_tmp_t:s0",
    "size": 0,
    "state": "file",
    "uid": 0
}

The file has been created successfully. You might want to ssh to server2 to check the file is effectively there.

[user@server1 ~]$ ssh server2
Last login: Tue Aug 21 09:54:26 2018 from server1.contoso.local
[user@server2 ~]$ ls /tmp/tes*
/tmp/testFile.txt

To create a directory, the state argument is also used, simply by defining state=directory.

$ ansible appservers -m file -a 'path=/tmp/testFolder state=directory'
server2.contoso.local | SUCCESS => {
    "changed": true,
    "gid": 0,
    "group": "root",
    "mode": "0755",
    "owner": "root",
    "path": "/tmp/testFolder",
    "secontext": "unconfined_u:object_r:user_tmp_t:s0",
    "size": 6,
    "state": "directory",
    "uid": 0
}
  • Copying files and directories

If you have a an existing file or directory that needs to be copied to your remote servers, the copy module is used for this purpose. We have here a directory called AppFiles containing three files.

$ ls AppFiles
file1  file2  file3

To copy this directory along with the files inside it to the appservers, the following command will do it.

$ ansible appservers -m copy -a 'src=~/install/AppFiles dest=/tmp owner=user group=user mode=0644'
server2.contoso.local | SUCCESS => {
    "changed": true,
    "dest": "/tmp/",
    "src": "/home/user/install/AppFiles"
}

In the above command, the src and dest arguments are mandatory with the copy module. All the other arguments are optional.s

Let’s check the result!

[user@server1 install]$ cd
[user@server1 ~]$ ssh server2
Last login: Wed Aug 22 15:42:54 2018 from server1.contoso.local
[user@server2 ~]$ ls /tmp/AppFiles/
file1  file2  file3

As you can see, ad-hoc commands are very useful for one-off kind of things but you won’t use them that often for complex repetitive and automated tasks. Instead, you will use playbooks most of the time, and this is what we will see in the next post.

 

1 Comment

  1. Pingback: Ansible for the Impatient Beginners – vAdmin-Land

Leave a Comment

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