Introduction to Ansible console

In previous Ansible tutorials we saw how easy it is to understand the basic concepts of this fantastic automation tool, how to write our first playbooks, how to organize tasks into roles, and how to use Ansible Vault to protect sensitive data. This time, we focus on an interactive tool which let us execute tasks on the fly, targeting single hosts or host groups: the Ansible console.

In this tutorial you will learn:

  • How to install the Ansible console on the most used Linux distributions
  • How to use the Ansible console
Introduction to the ansible console
Introduction to Ansible console
Software Requirements and Linux Command Line Conventions
Category Requirements, Conventions or Software Version Used
System Distribution agnostic
Software ansible
Other Privileged access to your Linux system as root or via the sudo command in order to perform system-wide installation of required packages
Conventions # – requires given linux-commands to be executed with root privileges either directly as a root user or by use of sudo command
$ – requires given linux-commands to be executed as a regular non-privileged user

Introduction and installation

Ansible console is a Read Eval Print Loop (REPL) environment from which we can execute tasks on the fly and immediately visualize their results. The ansible-console utility is part of the ansible-core package, which on Fedora and Fedora-based distributions we can install with the following command:

$ sudo dnf install ansible-core

To perform the installation on Debian and on Debian-base distributions, instead, we can run:

$ sudo apt install ansible-core

Using Archlinux? The ansible-core package is available in the “Extra” repository, so it can be easily installed with pacman:

$ sudo pacman -S ansible-core

It is also possible to install the package in a distribution-agnostic way, using pip:

$ pip install --user ansible-core

Invoking the ansible-console

We use ansible-console practically in the same manner we use ansible-playbook, since the two utilities share the vast majority of options. The most basic possible way to invoke the console is by running:

$ ansible-console

Our shell prompt will change to confirm the Ansible REPL environment is accepting commands:

[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
Welcome to the ansible console. Type help or ? to list commands.

doc@all (0)[f:5]$



The warning message you see above is due to the fact that we didn’t specify any inventory file. In such cases Ansible generates an implicit localhost for us when we use “localhost” as a target. You can see this is reflected in the console prompt, where “doc” is the Ansible “remote user” and “all” is the host group we are targeting.

doc@all (0)[f:5]$

By default, the user we invoke the ansible-console as, is also used for remote connections; another one, however, can be specified as argument to the --user option when we invoke the console, or, from the console itself, by executing the remote_user command:

doc@all (0)[f:5]$ remote_user username

Since, as stated in the warning, the “all” group doesn’t match the implicit localhost, we can see a  0  between parenthesis in the prompt, which is the number of matched hosts. After this, between square brackets, we can see the number of available forks: 5, (the default). Forks are the simultaneous connections Ansible can use on a task. The number of available forks can be specified when invoking the console, as argument to the -f option:

$ ansible-console -f 7

Alternatively, it can be changed from the console itself, with the fork command:

doc@all (0)[f:5]$ forks 7

Since our current configuration matches no hosts, the tasks we will run will have no effect. Since at the moment we can only target the implicit localhost, to do something effective, we should “switch” to it, let’s see how to do it.

Targeting specific hosts or groups

How can we target a specific host (or host group) from the Ansible console? We can do it by using the cd command, as if we were changing working directory. To target the implicit localhost, for example, we would run:

doc@all (0)[f:5]$ cd localhost

The prompt will immediately reflect the change of target:

doc@localhost (1)[f:5]$



Notice how, as expected, the number of matched host has become 1. The cd command can be also used to target specific hosts or groups explicitly defined into an inventory file. Let’s see an example. Suppose we have the following, very basic, ansible inventory, saved into a file named inventory.ini:

[workstations]
localhost ansible_connection=local

[servers]
feanor ansible_host=192.168.0.39

In the inventory we explicitly defined a “localhost”, and two groups : “workstations” (which includes localhost), and “servers”, which contains only one host (feanor). To point the ansible-console to this inventory file, we pass it as argument to the -i option:

$ ansible-console -i inventory.ini

The console prompt reports the number of matched hosts:

doc@all (2)[f:5]$

To get a list the available hosts, we can use the list command:

doc@all (2)[f:5]$ list
localhost
feanor  

To list the available groups, instead, we use list groups:

doc@all (2)[f:5]$ list groups
all
servers
ungrouped
workstations  

Above we can see the groups we explicitly defined in the inventory file appearing in the output of the command, together with other two groups automatically created by Ansible: “all” and “ungrouped”. The former is the group which encompasses all available hosts, the latter represents a way to reference all the hosts which are not part of any group.

In order to switch to a specific group, we can use cd, exactly as we did in the previous examples:

doc@all (2)[f:5]$ cd workstations

Getting a list of the available modules

As we already know, Ansible functionalities are organized in modules. To list all available modules from the Ansible console, we can use the help command:

doc@all (2)[f:5]$ help

The list will be printed on screen (output truncated):

Documented commands (type help <topic>):
========================================
EOF
amazon.aws.autoscaling_group
amazon.aws.autoscaling_group_info
amazon.aws.aws_az_info
amazon.aws.aws_caller_info
amazon.aws.cloudformation
amazon.aws.cloudformation_info
amazon.aws.cloudtrail
amazon.aws.cloudtrail_info
amazon.aws.cloudwatch_metric_alarm
amazon.aws.cloudwatch_metric_alarm_info
amazon.aws.cloudwatchevent_rule
amazon.aws.cloudwatchlogs_log_group
amazon.aws.cloudwatchlogs_log_group_info
amazon.aws.cloudwatchlogs_log_group_metric_filter
[...]



To obtain help about a specific module, we can, again, use the “help” command, this time, passing the name of the module as argument. For instance, to read the documentation of the ansible.builtin.file module, we would run:

doc@all (2)[f:5]$ help ansible.builtin.file

Here is the result of invoking the command above:

Manage files and file properties
Parameters:
  path Path to the file being managed.
  state If C(absent), directories will be recursively deleted, and files or symlinks will be unlinked. In the case of a directory, if C(diff) is declared, you will see the files and folders deleted listed under C(path_contents). Note that C(absent) will not cause C(file) to fail if the C(path) does not exist as the state did not change.
  src Path of the file to link to.
  recurse Recursively set the specified file attributes on directory contents.
  force Force the creation of the symlinks in two cases: the source file does not exist (but will appear later); the destination exists and is a file (so, we need to unlink the C(path) file and create symlink to the C(src) file in place of it).
  follow This flag indicates that filesystem links, if they exist, should be followed.
  modification_time This parameter indicates the time the file's modification time should be set to.
  modification_time_format When used with C(modification_time), indicates the time format that must be used.
  access_time This parameter indicates the time the file's access time should be set to.
  access_time_format When used with C(access_time), indicates the time format that must be used.
  mode The permissions the resulting filesystem object should have.
  owner Name of the user that should own the filesystem object, as would be fed to I(chown).
  group Name of the group that should own the filesystem object, as would be fed to I(chown).
  seuser The user part of the SELinux filesystem object context.
  serole The role part of the SELinux filesystem object context.
  setype The type part of the SELinux filesystem object context.
  selevel The level part of the SELinux filesystem object context.
  unsafe_writes Influence when to use atomic operation to prevent data corruption or inconsistent reads from the target filesystem object.
  attributes The attributes the resulting filesystem object should have.

Running a task

Running a tasks from the Ansible console is pretty easy. All we have to do is to invoke the module we want to use and provide required parameters. Let’s suppose we want to install a Python package with pip using the ansible.builtin.pip module, targeting only “localhost”. Here is what we would do. First we would switch to “localhost” with the cd command:

doc@all (2)[f:5]$ cd localhost

Than, we would invoke the module and pass the name of the package we want to install as argument to the “name” parameter. In the example below we install youtube-dl, an utility we can use to download videos from Youtube and from many other platforms:

doc@localhost (1)[f:5]$ ansible.builtin.pip name=youtube-dl

We can immediately visualize the output of the task:

localhost | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": true,
    "cmd": [
        "/usr/bin/python3",
        "-m",
        "pip.__main__",
        "install",
        "youtube-dl"
    ],
    "name": [
        "youtube-dl"
    ],
    "requirements": null,
    "state": "present",
    "stderr": "",
    "stderr_lines": [],
    "stdout": "Defaulting to user installation because normal site-packages is not writeable\nCollecting youtube-dl\n  Downloading youtube_dl-2021.12.17-py2.py3-none-any.whl (1.9 MB)\n     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.9/1.9 MB 4.1 MB/s eta 0:00:00\nInstalling collected packages: youtube-dl\nSuccessfully installed youtube-dl-2021.12.17\n",
    "stdout_lines": [
        "Defaulting to user installation because normal site-packages is not writeable",
        "Collecting youtube-dl",
        "  Downloading youtube_dl-2021.12.17-py2.py3-none-any.whl (1.9 MB)",
        "     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.9/1.9 MB 4.1 MB/s eta 0:00:00",
        "Installing collected packages: youtube-dl",
        "Successfully installed youtube-dl-2021.12.17"
    ],
    "version": null,
    "virtualenv": null
}

In the example above, we installed a single package, therefore we provided a string as value of the “name” parameter. It is also possible to pass multiple elements, so to install multiple packages. In a playbook we would use a list, in the Ansible console, instead, we separate items with a comma:

doc@localhost (1)[f:5]$ ansible.builtin.pip name=youtube-dl,bs4

Becoming another user

Certain tasks modifies the global state of the system, therefore we must invoke them with elevated privileges. Just as we would do when using the ansible-playbook utility, we invoke ansible-shell with the -K option (short for --ask-become-pass); we will be prompted to provide our password, since the default method of becoming another user is “sudo”:

$ ansible-console -i inventory.ini --ask-become-pass
BECOME password: 



Once we provide the password, we can become root by toggling the “become” flag:

doc@all (1)[f:5]$ become true

The prompt will reflect the change: the text color will become red, and $ will change to #:

doc@all (1)[f:5]#

Now we can invoke the task requiring administrative privileges. We can, for example, use the ansible.builtin.package module to install a package system-wide:

ansible.builtin.package name=git

To switch back to an unprivileged user we can simply use:

doc@all (1)[f:5]# become false

Running a shell command

Sometimes, we may want to run a standard shell command without leaving the Ansible console environment. This can be done by prefixing the command we want to run with a ! (this may remind you of something if you are a Vim user). Just as an example, suppose we want to print the name of the current working directory with pwd. We would run:

doc@all (1)[f:5]$ !pwd

Parameter expansions and command substitution also work with this technique:

doc@all (1)[f:5]$ !echo ${HOSTNAME}
localhost | CHANGED | rc=0 >>
fingolfin

doc@all (1)[f:5]$ !echo "today is $(date)"
localhost | CHANGED | rc=0 >>
today is Thu Jul 20 04:16:03 PM CEST 2023

Exiting the Ansible console

Once we are done working in the Ansible console, to return to the system shell, all we have to do is to use the exit command:

doc@all (1)[f:5]$ exit

Ansible-console was exited.

Conclusions

This tutorial is just the last one in our series about Ansible. Here we saw how to invoke the Ansible console and how to read its prompt, how to get information about available modules, how to execute a task, and how to navigate through hosts and groups. We also sow how to run tasks with elevated privileges, and, finally how to exit the console.



Comments and Discussions
Linux Forum