File, Package, Service, and User Modules¶
What You'll Learn¶
- The modules behind most day-to-day configuration management, grouped by job
- Which parameters matter, and how each module decides whether it
changed - When to choose a distro-specific module over the cross-platform one
- Which of these live in
ansible.builtinand which need a collection
Why This Exists¶
These modules cover the large majority of what a typical configuration-management playbook does day to day — this page is the category reference, one level below Command vs. Shell and Module Decision Trees.
Collections matter here
Most modules below ship with ansible-core as ansible.builtin.*. A few commonly used ones live in collections: authorized_key, mount, and sysctl are in ansible.posix; ufw is in community.general. Install them with ansible-galaxy collection install ansible.posix community.general and pin them in requirements.yml.
Packages¶
ansible.builtin.package — cross-distro¶
- name: Install common tools on any distribution
ansible.builtin.package:
name:
- curl
- jq
- git
state: present
It delegates to the host's package manager. Package names still differ between distributions (apache2 vs. httpd), so pair it with facts or per-OS variables.
ansible.builtin.apt / ansible.builtin.dnf — distro-specific¶
- name: Install nginx on Debian/Ubuntu, refreshing the cache at most hourly
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
cache_valid_time: 3600
when: ansible_facts['os_family'] == 'Debian'
- name: Apply security updates on RHEL-family hosts
ansible.builtin.dnf:
name: "*"
state: latest
security: true
when: ansible_facts['os_family'] == 'RedHat'
Reach for the specific module when you need its extra parameters: cache_valid_time, deb files, default_release, or security-only updates.
state |
Meaning |
|---|---|
present |
Install if missing; never upgrade. Idempotent and predictable. |
latest |
Upgrade on every run if a newer version exists — changes over time, so avoid for pinned fleets. |
absent |
Remove. |
Pin exact versions for anything you deploy: name: nginx=1.26.* (apt) or name: nginx-1.26.2 (dnf).
Services¶
- name: Ensure nginx is running and starts on boot
ansible.builtin.service:
name: nginx
state: started
enabled: true
ansible.builtin.systemd_service adds systemd-only features:
- name: Install a custom unit file
ansible.builtin.template:
src: checkout.service.j2
dest: /etc/systemd/system/checkout.service
mode: "0644"
notify: Restart checkout
- name: Reload systemd and start the service
ansible.builtin.systemd_service:
name: checkout
state: started
enabled: true
daemon_reload: true
started/stopped are idempotent. restarted/reloaded always report changed — put them in handlers, not in the main task list.
Files and Directories¶
file — state of a path¶
- name: Create the application directory tree
ansible.builtin.file:
path: "/opt/checkout/{{ item }}"
state: directory
owner: checkout
group: checkout
mode: "0750"
loop: [releases, shared, shared/logs]
- name: Point "current" at the new release
ansible.builtin.file:
src: "/opt/checkout/releases/{{ app_release }}"
dest: /opt/checkout/current
state: link
- name: Remove an old config file
ansible.builtin.file:
path: /etc/nginx/conf.d/default.conf
state: absent
copy and template — file content¶
- name: Ship a static CA bundle
ansible.builtin.copy:
src: files/internal-ca.pem
dest: /usr/local/share/ca-certificates/internal-ca.crt
mode: "0644"
backup: true
- name: Render nginx config, refusing to deploy an invalid one
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
validate: nginx -t -c %s
notify: Reload nginx
Both compare checksums, so an identical file reports ok. See Templates for Config Generation.
stat — read-only inspection¶
- name: Check whether the app was already initialized
ansible.builtin.stat:
path: /opt/checkout/shared/.initialized
register: init_marker
- name: Initialize the app once
ansible.builtin.command: /opt/checkout/bin/init
when: not init_marker.stat.exists
stat never changes anything, which is why it's almost always paired with register and when.
Downloads and archives¶
- name: Download a release tarball, verified by checksum
ansible.builtin.get_url:
url: "https://releases.example.com/checkout-{{ app_release }}.tar.gz"
dest: "/tmp/checkout-{{ app_release }}.tar.gz"
checksum: "sha256:{{ app_release_sha256 }}"
- name: Unpack it into a versioned release directory
ansible.builtin.unarchive:
src: "/tmp/checkout-{{ app_release }}.tar.gz"
dest: "/opt/checkout/releases/{{ app_release }}"
remote_src: true
creates: "/opt/checkout/releases/{{ app_release }}/bin/checkout"
creates: is what makes unarchive idempotent.
Source control¶
- name: Check out a pinned tag
ansible.builtin.git:
repo: https://github.com/example/ops-scripts.git
dest: /opt/ops-scripts
version: v3.2.0
Partial File Edits¶
| Module | Edits | Best for |
|---|---|---|
lineinfile |
One line, matched by regex | A single setting in a file you don't own |
blockinfile |
A marked multi-line block | A few lines you own inside a file you don't |
replace |
Every regex match in the file | Bulk substitutions |
template |
The whole file | Any file you fully own — usually the right answer |
- name: Disable SSH password authentication
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PasswordAuthentication'
line: PasswordAuthentication no
validate: sshd -t -f %s
notify: Reload sshd
- name: Add internal hosts entries
ansible.builtin.blockinfile:
path: /etc/hosts
marker: "# {mark} ANSIBLE MANAGED: internal services"
block: |
10.0.2.10 registry.internal
10.0.2.11 vault.internal
Without regexp, lineinfile appends a new line whenever the exact line is missing — a common way to end up with duplicate, conflicting settings. See Module Decision Trees for when template is the better choice.
Users and Access¶
- name: Create the application group
ansible.builtin.group:
name: checkout
system: true
- name: Create the application service account
ansible.builtin.user:
name: checkout
group: checkout
system: true
shell: /usr/sbin/nologin
home: /opt/checkout
create_home: false
- name: Authorize an engineer's SSH key
ansible.posix.authorized_key:
user: alice
key: "{{ lookup('ansible.builtin.file', 'files/keys/alice.pub') }}"
state: present
user with state: absent and remove: true also deletes the home directory. A full team-access design is in Case Study: User and SSH Access; key-based authentication itself ties to SSH and Connectivity.
Scheduling¶
- name: Nightly log cleanup at 02:30
ansible.builtin.cron:
name: checkout log cleanup # the unique ID Ansible uses to find this entry
user: checkout
minute: "30"
hour: "2"
job: "find /opt/checkout/shared/logs -name '*.log' -mtime +14 -delete"
The name is what makes cron idempotent — change it and you get a second, duplicate job.
Mounts and Kernel Settings¶
- name: Mount the data volume and persist it in /etc/fstab
ansible.posix.mount:
path: /data
src: UUID=3f0c1b7e-2a9d-4c55-9a51-6f3e2d1c9b10
fstype: xfs
opts: defaults,noatime
state: mounted
- name: Raise the listen backlog
ansible.posix.sysctl:
name: net.core.somaxconn
value: "4096"
state: present
reload: true
mount states: mounted (mount now + fstab), present (fstab only), unmounted (unmount, keep fstab), absent (unmount + remove from fstab).
Check Mode Support at a Glance¶
All modules on this page support --check. The catch is chained tasks: in check mode, get_url doesn't actually download, so a following unarchive with remote_src: true fails on a missing file. Guard such steps with when: not ansible_check_mode, as covered in Check Mode and Diff Mode.
Common Mistakes¶
- Using
state: latesteverywhere, so every run can upgrade packages nobody intended to upgrade. - Putting
state: restartedin a regular task, restarting the service on every run. - Using
lineinfilewithoutregexp, appending duplicate settings instead of replacing the existing line. - Editing
sshd_configorsudoerswithoutvalidate:, and locking yourself out with one typo. - Calling
authorized_keyormountby short name without installingansible.posix, then getting "couldn't resolve module/action." - Renaming a
cronjob'sname, which creates a second entry instead of updating the first.
Interview Questions¶
- When would you choose
apt/dnfdirectly instead of the cross-distropackagemodule? - What's the difference between
lineinfileandblockinfile, and when istemplatebetter than both? - Why is
statcommonly paired withregisterandwheninstead of used alone? - How does
unarchivestay idempotent, and what breaks about a download-then-unpack sequence in check mode? - Why does
service: state=restartedbelong in a handler?
Next¶
Continue to URI and API Automation.