Skip to content

Expert · Ansible fundamentals · topic 48 of 57 · 35 min

Everyday modules and handlers

Explanation

A small set of modules covers most admin work: dnf, service, copy, template, file, user, group, lineinfile and firewalld. Always use the full name (FQCN) such as ansible.builtin.copy. Handlers are tasks that run only when notified by a task that changed something, typically to restart a service once at the end of the play.

The modules you will use every week

ModuleUse it toKey options
ansible.builtin.dnfInstall, update or remove packagesname, state: present / latest / absent
ansible.builtin.service (or systemd_service)Start, stop, enable servicesname, state: started / stopped / restarted, enabled
ansible.builtin.copyCopy a file or write fixed contentsrc or content, dest, owner, group, mode
ansible.builtin.templateWrite a file from a Jinja2 templatesrc (a .j2 file), dest, mode
ansible.builtin.fileDirectories, links, permissions, deletepath, state: directory / link / absent / touch, mode
ansible.builtin.user / groupAccountsname, uid, groups, append, password, state
ansible.builtin.lineinfileMake sure one line is in a filepath, regexp, line, state
ansible.posix.firewalldFirewall services and portsservice or port, permanent, immediate, state
ansible.builtin.debugPrint a message or variablemsg, var

FQCN (fully qualified collection name) means collection.name.module, such as ansible.builtin.copy. Short names like copy usually still work, but FQCNs avoid confusion when two collections have a module with the same short name, and linting tools expect them.

yaml
- name: Create the app directory
  ansible.builtin.file:
    path: /opt/notes
    state: directory
    owner: notes
    mode: "0750"

- name: Allow only key logins
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config.d/01-hardening.conf
    regexp: '^PasswordAuthentication'
    line: PasswordAuthentication no
    create: true
    mode: "0600"

- name: Create user ravi in wheel
  ansible.builtin.user:
    name: ravi
    groups: wheel
    append: true
    state: present

Handlers: restart only when needed

After changing a config file you usually restart the service. But you do not want to restart it on every run, only when the file actually changed. A handler is a task in a separate handlers section. A normal task can notify it; the handler runs once, at the end of the play, and only if at least one notifying task reported changed.

yaml
- name: Configure Apache
  hosts: webservers
  become: true
  tasks:
    - name: Set the server admin email
      ansible.builtin.lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: '^ServerAdmin'
        line: ServerAdmin webmaster@example.com
      notify: Restart Apache

    - name: Apache is running
      ansible.builtin.service:
        name: httpd
        state: started
        enabled: true

  handlers:
    - name: Restart Apache
      ansible.builtin.service:
        name: httpd
        state: restarted
The notify text must match the handler's name exactly.
  • If several tasks notify the same handler, it still runs only once.
  • Handlers run at the end of the play, in the order they are written in handlers, not the order notified.
  • If a later task fails on a host, pending handlers do not run there (unless you use --force-handlers or force_handlers: true).
  • ansible.builtin.meta: flush_handlers runs pending handlers right away, in the middle of the play.

Collections you will need

ansible-core ships only ansible.builtin. Modules such as ansible.posix.firewalld, ansible.posix.selinux or community.general.timezone come from collections. Install them with ansible-galaxy collection install ansible.posix (from Ansible Galaxy or your company's automation hub), or use an execution environment that already contains them. The next module covers collections in depth.

Common mistakes

  • mode: 644 without quotes or a leading 0. YAML reads 644 as the decimal number 644, which is not the permissions you meant. Write mode: "0644".
  • Restarting a service as a normal task every time. Use a handler.
  • A notify name with a typo: the play fails with "The requested handler ... was not found".
  • lineinfile without regexp, so a changed value adds a second line instead of replacing the old one.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Everyday modules and handlers · 1/6

dnf · service · copy · template · file · user · lineinfile · firewalld

A few modules do most of the work: dnf, service, copy, template, file, user, lineinfile and firewalld.

Prefer a video by a teacher? Find free videos on “ansible handlers notify modules copy template lineinfile tutorial” on YouTube ↗

In real life: Rolling out a config change to a university's web farm

A university in Delhi runs its admission portal on six web servers. When the admissions office changed its contact email, the team updated one line in a playbook. On the run, only the lineinfile task reported changed, so the handler restarted Apache exactly once per server, at the end. Servers that already had the new line were not restarted at all.

bash
RUNNING HANDLER [Restart Apache] ***********************************************
changed: [web01.du.example.edu]
changed: [web02.du.example.edu]
...
PLAY RECAP *********************************************************************
web01.du.example.edu : ok=4  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

Practice (0/4 done)

Task 1 · Question

When does a handler run?

Task 2 · Question

Which line has the bug? 1: - name: Config file 2: ansible.builtin.copy: 3: src: app.conf 4: dest: /etc/app.conf 5: mode: 644

Task 3 · Code · Python

Model handlers. Each task result is a dict with name, changed (bool) and notify (a list of handler names, maybe empty). Write handlers_to_run(results, handler_order) that returns the handlers that will run: those notified by at least one CHANGED task, each once, in the order of handler_order (the order they are written in the handlers section).

Task 4 · Practice

Write motd.yml for all hosts: create the group "ops" and user "ops1" in it, write /etc/motd with copy (content includes the host name), make sure /opt/ops exists (owner ops1, mode 0750), set "ClientAliveInterval 300" in an sshd drop-in with lineinfile, and reload sshd through a handler only when that line changes.

Quick quiz

Question 1

1.Which module makes sure a directory exists?

Question 2

2.Three tasks notify the same handler and all change. How many times does it run?

Question 3

3.Which collection provides the firewalld module?

Question 4

4.In lineinfile, what does regexp do?

Question 5

5.Which module writes a file from a Jinja2 .j2 file with variables filled in?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Which module makes sure a directory exists?

Answer: ansible.builtin.file with state: directory

file manages paths, including directories.

Three tasks notify the same handler and all change. How many times does it run?

Answer: 1

Once, at the end of the play.

Which collection provides the firewalld module?

Answer: ansible.posix

ansible.posix.firewalld.

In lineinfile, what does regexp do?

Answer: Finds the existing line to replace, so you do not get duplicates

Matching lines are replaced by line.

Which module writes a file from a Jinja2 .j2 file with variables filled in?

Answer: template

template renders Jinja2.

Two tasks notify "Reload nginx" and both report changed. How many times does the handler run on that host?Related

Answer: Once, at the end of the play

Handlers are deduplicated and run once, after the tasks of the play.

From Ansible playbooks

In a role, where does the template module look for src files by default?Related

Answer: templates/

templates/ for template, files/ for copy.

From Roles, collections and ansible-galaxy

What is ansible_managed used for?Related

Answer: A header saying the file is managed by Ansible

It warns people not to edit by hand.

From Jinja2 templates

What is a collection in Ansible?Related

Answer: A package of modules, roles and plugins under a namespace, like community.general

Collections are how modules are packaged and shared, for example through Galaxy.

From Roles, templates, Galaxy and Vault

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. This is the last topic of “Ansible fundamentals”. The module exam comes next.
  2. Module 9 exam: Ansible fundamentals · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Ansible in depth. 57 topics left in the course before the final exam.

← Previous: Writing and running playbooks