10 Ansible modules for Linux system automation

These handy modules save time and hassle by automating many of your daily tasks, and they're easy to implement with a few commands.
268 readers like this.
gears and lightbulb to represent innovation


Ansible is a complete automation solution for your IT environment. You can use Ansible to automate Linux and Windows server configuration, orchestrate service provisioning, deploy cloud environments, and even configure your network devices.

Ansible modules abstract actions on your system so you don't need to worry about implementation details. You simply describe the desired state, and Ansible ensures the target system matches it.

This module availability is one of Ansible's main benefits, and it is often referred to as Ansible having "batteries included." Indeed, you can find modules for a great number of tasks, and while this is great, I frequently hear from beginners that they don't know where to start.

Although your choice of modules will depend exclusively on your requirements and what you're trying to automate with Ansible, here are the top ten modules you need to get started with Ansible for Linux system automation.

1. copy

The copy module allows you to copy a file from the Ansible control node to the target hosts. In addition to copying the file, it allows you to set ownership, permissions, and SELinux labels to the destination file. Here's an example of using the copy module to copy a "message of the day" configuration file to the target hosts:

- name: Ensure MOTD file is in place
    src: files/motd
    dest: /etc/motd
    owner: root
    group: root
    mode: 0644

For less complex content, you can copy the content directly to the destination file without having a local file, like this:

- name: Ensure MOTD file is in place
    content: "Welcome to this system."
    dest: /etc/motd
    owner: root
    group: root
    mode: 0644

This module works idempotently, which means it will only copy the file if the same file is not already in place with the same content and permissions.

The copy module is a great option to copy a small number of files with static content. If you need to copy a large number of files, take a look at the synchronize module. To copy files with dynamic content, take a look at the template module next.

2. template

The template module works similarly to the copy module, but it processes content dynamically using the Jinja2 templating language before copying it to the target hosts.

For example, define a "message of the day" template that displays the target system name, like this:

$ vi templates/motd.j2
Welcome to {{ inventory_hostname }}.

Then, instantiate this template using the template module, like this:

- name: Ensure MOTD file is in place
    src: templates/motd.j2
    dest: /etc/motd
    owner: root
    group: root
    mode: 0644

Before copying the file, Ansible processes the template and interpolates the variable, replacing it with the target host system name. For example, if the target system name is rh8-vm03, the result file is:

Welcome to rh8-vm03.

While the copy module can also interpolate variables when using the content parameter, the template module allows additional flexibility by creating template files, which enable you to define more complex content, including for loops, if conditions, and more. For a complete reference, check Jinja2 documentation.

This module is also idempotent, and it will not copy the file if the content on the target system already matches the template's content.

3. user

The user module allows you to create and manage Linux users in your target system. This module has many different parameters, but in its most basic form, you can use it to create a new user.

For example, to create the user ricardo with UID 2001, part of the groups users and wheel, and password mypassword, apply the user module with these parameters:

- name: Ensure user ricardo exists
    name: ricardo
    group: users
    groups: wheel
    uid: 2001
    password: "{{ 'mypassword' | password_hash('sha512') }}" 
    state: present

Notice that this module tries to be idempotent, but it cannot guarantee that for all its options. For instance, if you execute the previous module example again, it will reset the password to the defined value, changing the user in the system for every execution. To make this example idempotent, use the parameter update_password: on_create, ensuring Ansible only sets the password when creating the user and not on subsequent runs.

You can also use this module to delete a user by setting the parameter state: absent.

The user module has many options for you to manage multiple user aspects. Make sure you take a look at the module documentation for more information.

4. package

The package module allows you to install, update, or remove software packages from your target system using the operating system standard package manager.

For example, to install the Apache web server on a Red Hat Linux machine, apply the module like this:

- name: Ensure Apache package is installed
    name: httpd
    state: present


This module is distribution agnostic, and it works by using the underlying package manager, such as yum/dnf for Red Hat-based distributions and apt for Debian. Because of that, it only does basic tasks like install and remove packages. If you need more control over the package manager options, use the specific module for the target distribution.


Also, keep in mind that, even though the module itself works on different distributions, the package name for each can be different. For instance, in Red Hat-based distribution, the Apache web server package name is httpd, while in Debian, it is apache2. Ensure your playbooks deal with that.

This module is idempotent, and it will not act if the current system state matches the desired state.

[ Advance your automation expertise. Get the Ansible checklist: 5 reasons to migrate to Red Hat Ansible Automation Platform 2 ]

5. service

Use the service module to manage the target system services using the required init system; for example, systemd.

In its most basic form, all you have to do is provide the service name and the desired state. For instance, to start the sshd service, use the module like this:

- name: Ensure SSHD is started
    name: sshd
    state: started

You can also ensure the service starts automatically when the target system boots up by providing the parameter enabled: yes.

As with the package module, the service module is flexible and works across different distributions. If you need fine-tuning over the specific target init system, use the corresponding module; for example, the module systemd.

Similar to the other modules you've seen so far, the service module is also idempotent.

6. firewalld

Use the firewalld module to control the system firewall with the firewalld daemon on systems that support it, such as Red Hat-based distributions.

For example, to open the HTTP service on port 80, use it like this:

- name: Ensure port 80 (http) is open
    service: http
    state: enabled
    permanent: yes
    immediate: yes

You can also specify custom ports instead of service names with the port parameter. In this case, make sure to specify the protocol as well. For example, to open TCP port 3000, use this:

- name: Ensure port 3000/TCP is open
    port: 3000/tcp
    state: enabled
    permanent: yes
    immediate: yes

You can also use this module to control other firewalld aspects like zones or complex rules. Make sure to check the module's documentation for a comprehensive list of options.

7. file

The file module allows you to control the state of files and directories—setting permissions, ownership, and SELinux labels.

For instance, use the file module to create a directory /app owned by the user ricardo, with read, write, and execute permissions for the owner and the group users:

- name: Ensure directory /app exists
    path: /app
    state: directory
    owner: ricardo
    group: users
    mode: 0770

You can also use this module to set file properties on directories recursively by using the parameter recurse: yes or delete files and directories with the parameter state: absent.

This module works with idempotency for most of its parameters, but some of them may make it change the target path every time. Check the documentation for more details.

8. lineinfile

The lineinfile module allows you to manage single lines on existing files. It's useful to update targeted configuration on existing files without changing the rest of the file or copying the entire configuration file.

For example, add a new entry to your hosts file like this:

- name: Ensure host rh8-vm03 in hosts file
    path: /etc/hosts
    line: rh8-vm03
    state: present

You can also use this module to change an existing line by applying the parameter regexp to look for an existing line to replace. For example, update the sshd_config file to prevent root login by modifying the line PermitRootLogin yes to PermitRootLogin no:

- name: Ensure root cannot login via ssh
    path: /etc/ssh/sshd_config
    regexp: '^PermitRootLogin'
    line: PermitRootLogin no
    state: present

Note: Use the service module to restart the SSHD service to enable this change.

This module is also idempotent, but, in case of line modification, ensure the regular expression matches both the original and updated states to avoid unnecessary changes.

9. unarchive

Use the unarchive module to extract the contents of archive files such as tar or zip files. By default, it copies the archive file from the control node to the target machine before extracting it. Change this behavior by providing the parameter remote_src: yes.

For example, extract the contents of a .tar.gz file that has already been downloaded to the target host with this syntax:

- name: Extract contents of app.tar.gz
    src: /tmp/app.tar.gz
    dest: /app
    remote_src: yes

Some archive technologies require additional packages to be available on the target system; for example, the package unzip to extract .zip files.

Depending on the archive format used, this module may or may not work idempotently. To prevent unnecessary changes, you can use the parameter creates to specify a file or directory that this module would create when extracting the archive contents. If this file or directory already exists, the module does not extract the contents again.

10. command

The command module is a flexible one that allows you to execute arbitrary commands on the target system. Using this module, you can do almost anything on the target system as long as there's a command for it.

Even though the command module is flexible and powerful, it should be used with caution. Avoid using the command module to execute a task if there's another appropriate module available for that. For example, you could create users by using the command module to execute the useradd command, but you should use the user module instead, as it abstracts many details away from you, taking care of corner cases and ensuring the configuration only changes when necessary.

For cases where no modules are available, or to run custom scripts or programs, the command module is still a great resource. For instance, use this module to run a script that is already present in the target machine:

- name: Run the app installer
  command: "/app/install.sh"

By default, this module is not idempotent, as Ansible executes the command every single time. To make the command module idempotent, you can use when conditions to only execute the command if the appropriate condition exists, or the creates argument, similarly to the unarchive module example.

What's next?

Using these modules, you can configure entire Linux systems by copying, templating, or modifying configuration files, creating users, installing packages, starting system services, updating the firewall, and more.

If you are new to Ansible, make sure you check the documentation on how to create playbooks to combine these modules to automate your system. Some of these tasks require running with elevated privileges to work. For more details, check the privilege escalation documentation.

As of Ansible 2.10, modules are organized in collections. Most of the modules in this list are part of the ansible.builtin collection and are available by default with Ansible, but some of them are part of other collections. For a list of collections, check the Ansible documentation.

What to read next
Ricardo Gerardi is Technical Community Advocate for Enable Sysadmin and Enable Architect. He was previously a principal consultant at Red Hat Canada, where he specialized in IT automation with Ansible and OpenShift.


What's your recommendation for handling the correct package names across multiple OSes?

Hey Ben. Great question. Thank you.
I usually create a variable to represent the list of packages to install, for example "package_list" and define it in an OS specific var file, such as "RedHat.yaml" or "Debian.yaml".
Then you include the vars file in your playbook/role based on the Operating System family retrieved as Ansible fact using this syntax: "{{ ansible_os_family }}.yaml".
This way you have a different "package_list" version per distribution. You could even include the OS version in the file name in case the package name is different across multiple version of the same distribution.

In reply to by bcotton

Hey Ben. Great question. Thank you.
I usually create a variable to represent the list of packages to install, for example "package_list" and define it in an OS specific var file, such as "RedHat.yaml" or "Debian.yaml".
Then you include the vars file in your playbook/role based on the Operating System family retrieved as Ansible fact using this syntax: "{{ ansible_os_family }}.yaml".
This way you have a different "package_list" version per distribution. You could even include the OS version in the file name in case the package name is different across multiple version of the same distribution.

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.