Ansible for Linux System Administration: crontab, mounts, users, sudoers
By Luca Berton · Published 2024-01-01 · Category: installation
Complete guide to Ansible for Linux system administration. Manage crontab jobs, filesystem mounts, user accounts, groups, sudoers rules, and SSH keys.
Every Linux sysadmin runs the same tasks: manage users, set up cron jobs, mount filesystems, configure sudo. Here's how to automate all of them with Ansible — idempotently.
Managing Users
Create a User
- name: Create application user
ansible.builtin.user:
name: deploy
comment: "Deployment user"
groups:
- wheel
- docker
shell: /bin/bash
create_home: true
home: /home/deploy
state: present
Create Multiple Users
- name: Create team accounts
ansible.builtin.user:
name: "{{ item.name }}"
groups: "{{ item.groups | default(omit) }}"
shell: "{{ item.shell | default('/bin/bash') }}"
state: present
loop:
- { name: alice, groups: ['developers', 'docker'] }
- { name: bob, groups: ['developers'] }
- { name: charlie, groups: ['developers', 'wheel'] }
- name: Deploy SSH keys for team
ansible.posix.authorized_key:
user: "{{ item.name }}"
key: "{{ item.key }}"
state: present
exclusive: false
loop:
- { name: alice, key: "ssh-ed25519 AAAA... alice@company" }
- { name: bob, key: "ssh-ed25519 AAAA... bob@company" }
- { name: charlie, key: "ssh-ed25519 AAAA... charlie@company" }
Remove a User
- name: Remove former employee
ansible.builtin.user:
name: ex_employee
state: absent
remove: true # Also removes home directory
force: true # Remove even if user is logged in
Set Password
- name: Set user password
ansible.builtin.user:
name: deploy
password: "{{ 'SecurePassword123!' | password_hash('sha512', 'salt') }}"
update_password: on_create # Only set on creation, not every run
no_log: true
Manage Groups
- name: Create application groups
ansible.builtin.group:
name: "{{ item }}"
state: present
loop:
- developers
- operations
- dbadmins
- name: Add user to supplementary groups
ansible.builtin.user:
name: deploy
groups:
- developers
- docker
append: true # Don't remove existing groups
See also: Ansible Mount Module: Mount NFS, CIFS & ansible.posix.mount Guide
Managing Crontab
Add a Cron Job
- name: Schedule daily backup at 2 AM
ansible.builtin.cron:
name: "daily database backup"
minute: "0"
hour: "2"
job: "/opt/scripts/backup-db.sh >> /var/log/backup.log 2>&1"
user: postgres
Cron with Special Time
- name: Run at reboot
ansible.builtin.cron:
name: "start application on boot"
special_time: reboot
job: "/opt/app/bin/start.sh"
user: deploy
# Other special_time values: hourly, daily, weekly, monthly, annually
Add Cron Environment Variable
- name: Set cron PATH
ansible.builtin.cron:
name: PATH
env: true
job: "/usr/local/bin:/usr/bin:/bin"
user: deploy
- name: Set cron MAILTO
ansible.builtin.cron:
name: MAILTO
env: true
job: "admin@example.com"
user: deploy
Remove a Cron Job
- name: Remove old backup job
ansible.builtin.cron:
name: "old backup script"
state: absent
user: postgres
Complete Cron Management
- name: Configure all cron jobs
ansible.builtin.cron:
name: "{{ item.name }}"
minute: "{{ item.minute | default('*') }}"
hour: "{{ item.hour | default('*') }}"
day: "{{ item.day | default('*') }}"
month: "{{ item.month | default('*') }}"
weekday: "{{ item.weekday | default('*') }}"
job: "{{ item.job }}"
user: "{{ item.user | default('root') }}"
state: present
loop:
- { name: "DB backup", hour: "2", minute: "0", job: "/opt/scripts/backup.sh", user: postgres }
- { name: "Log rotation", hour: "3", minute: "30", job: "/usr/sbin/logrotate /etc/logrotate.conf" }
- { name: "SSL renewal", hour: "4", minute: "0", weekday: "1", job: "certbot renew --quiet" }
- { name: "Disk cleanup", hour: "5", minute: "0", day: "1", job: "/opt/scripts/cleanup.sh" }
Managing Filesystem Mounts
Mount an NFS Share
- name: Install NFS client
ansible.builtin.apt:
name: nfs-common
state: present
- name: Mount NFS share
ansible.posix.mount:
src: "nfs-server:/exports/data"
path: /mnt/data
fstype: nfs
opts: "rw,sync,hard,intr"
state: mounted # Mounts AND adds to fstab
Mount Options Explained
# state: mounted — mount now AND add to fstab
# state: present — add to fstab only (don't mount now)
# state: unmounted — unmount but keep in fstab
# state: absent — unmount AND remove from fstab
# state: remounted — remount (apply new options)
Mount a New Disk
- name: Create filesystem
community.general.filesystem:
fstype: ext4
dev: /dev/sdb1
- name: Mount data volume
ansible.posix.mount:
src: /dev/sdb1
path: /data
fstype: ext4
opts: "defaults,noatime"
state: mounted
- name: Set ownership
ansible.builtin.file:
path: /data
owner: deploy
group: deploy
mode: '0755'
Mount tmpfs
- name: Mount tmpfs for application cache
ansible.posix.mount:
src: tmpfs
path: /opt/app/cache
fstype: tmpfs
opts: "size=512M,mode=0755,uid=deploy,gid=deploy"
state: mounted
See also: Ansible mount Module: Manage Filesystem Mounts and fstab (Complete Guide)
Managing Sudoers
Add Sudoers Rule (Safe Method)
# ALWAYS use /etc/sudoers.d/ — never edit /etc/sudoers directly
- name: Grant deploy user sudo for service management
ansible.builtin.copy:
content: |
# Managed by Ansible
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp, /usr/bin/systemctl status myapp
dest: /etc/sudoers.d/deploy
mode: '0440'
owner: root
group: root
validate: 'visudo -cf %s' # Validate before writing!
Group-Based Sudo
- name: Grant wheel group full sudo
ansible.builtin.copy:
content: |
# Managed by Ansible
%wheel ALL=(ALL) NOPASSWD: ALL
dest: /etc/sudoers.d/wheel
mode: '0440'
validate: 'visudo -cf %s'
- name: Grant developers limited sudo
ansible.builtin.copy:
content: |
# Managed by Ansible
%developers ALL=(deploy) NOPASSWD: /opt/app/bin/deploy.sh
%developers ALL=(root) NOPASSWD: /usr/bin/journalctl -u myapp*
dest: /etc/sudoers.d/developers
mode: '0440'
validate: 'visudo -cf %s'
Remove Sudoers Rule
- name: Remove old sudoers rule
ansible.builtin.file:
path: /etc/sudoers.d/old_rule
state: absent
Complete Server Setup Playbook
---
- name: Configure Linux server
hosts: all
become: true
vars:
admin_users:
- { name: alice, key: "ssh-ed25519 AAAA... alice" }
- { name: bob, key: "ssh-ed25519 AAAA... bob" }
app_user: deploy
data_mount: /data
tasks:
# Users
- name: Create admin users
ansible.builtin.user:
name: "{{ item.name }}"
groups: [wheel]
shell: /bin/bash
loop: "{{ admin_users }}"
- name: Deploy SSH keys
ansible.posix.authorized_key:
user: "{{ item.name }}"
key: "{{ item.key }}"
loop: "{{ admin_users }}"
- name: Create application user
ansible.builtin.user:
name: "{{ app_user }}"
system: true
shell: /bin/bash
# Sudo
- name: Configure sudoers
ansible.builtin.copy:
content: "{{ app_user }} ALL=(root) NOPASSWD: /usr/bin/systemctl * myapp\n"
dest: "/etc/sudoers.d/{{ app_user }}"
mode: '0440'
validate: 'visudo -cf %s'
# Cron
- name: Schedule backups
ansible.builtin.cron:
name: "daily backup"
hour: "2"
minute: "0"
job: "/opt/scripts/backup.sh"
user: "{{ app_user }}"
# Mounts
- name: Mount data volume
ansible.posix.mount:
src: /dev/sdb1
path: "{{ data_mount }}"
fstype: ext4
state: mounted
when: ansible_devices.sdb is defined
See also: Ansible mount Module: Mount Filesystems, NFS, SMB/CIFS Shares (Guide)
FAQ
Should I manage /etc/sudoers directly or use sudoers.d?
Always use /etc/sudoers.d/ drop-in files. Never edit /etc/sudoers directly — a syntax error locks you out of sudo entirely. The validate: 'visudo -cf %s' parameter ensures Ansible checks syntax before writing.
How do I handle user password expiry?
Use ansible.builtin.user with password_expire_max, password_expire_min, and password_expire_warn parameters. For LDAP/AD-managed users, manage password policies in the directory, not locally.
What's the difference between mounted and present in the mount module?
mounted mounts the filesystem immediately AND adds it to /etc/fstab. present only adds to /etc/fstab without mounting — useful for volumes that should mount on next reboot but not now.
Conclusion
Ansible replaces ad-hoc useradd, crontab -e, manual fstab edits, and hand-written sudoers files with version-controlled, idempotent, auditable automation. Every example above is safe to run repeatedly — exactly what production infrastructure needs.
Related Articles
• Ansible cron Module Guide • Ansible file Module Cookbook • Ansible user Module Guide • How to Make Playbooks IdempotentSee also
• Ansible mount Module: Manage Filesystem Mounts and fstab (Complete Guide)Category: installation