Files
ansible/playbooks/user_setup.yml
T
Harwinder Sekhon 5aaa7020ac Fix playbook bugs from review, add Telegram alerts to health and service checks
Review fixes:
- Availability plays: "ping_result is succeeded" is true for unreachable
  hosts, so offline hosts were added to reachable_hosts and failed the
  next play. Test reachability explicitly (7 playbooks).
- bootstrap_lxc: vim-enhanced and mtr-tiny package names, enable CRB next
  to EPEL, add the root key without replacing authorized_keys, validate
  sshd_config edits and restart sshd only on change.
- backup_docker: the command module does not expand $(...); fix rsync
  filter order and add --exclude=*; restart only the containers that were
  stopped, even when rsync fails; read the storage address from the
  inventory; keep semaphore and semaphore-db running.
- new_lxc: community.general.proxmox no longer exists, use
  community.proxmox; node is mini-pc; valid hostname; password or token
  auth; let the module pick the VMID; longer timeout.
- proxmox-backup: surface the vzdump exit code, let vzdump prune with
  keep-last, drop the rename/prune steps that globbed *.zstd.
- time-region: chrony regex no longer comments out its own server line;
  /etc/localtime as a symlink; reuse facts across plays.
- update_pihole_blocklists: FQCN, become: true, changed_when.
- ansible.cfg gathering = smart; update_system only pings ubuntu/rocky.

Telegram alerts:
- health_check: filesystems above disk_warn_pct (default 80), excluding
  efivarfs, and offline hosts.
- service_check: services not running.
- One message per run from localhost, credentials from Semaphore
  (TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID), no_log on the send task,
  prints the alert text when Telegram is not configured.

Claude-Session: https://claude.ai/code/session_01XEizHawueRXUcxQELRZ8kq
2026-09-06 23:06:18 -04:00

105 lines
3.7 KiB
YAML

---
# Creates a non-root admin user with sudo and the homelab SSH keys on every
# reachable Ubuntu/Rocky guest. First step toward running Ansible as a
# non-root user.
#
# Extra vars (optional):
# admin_user - default harry
# admin_sudo_nopasswd - default true (required for unattended become)
# admin_ssh_keys - list of public keys (defaults to the ansible-key and
# the personal key already used by add_root_key.yml)
- name: Check Host Availability
hosts: ubuntu:rocky
gather_facts: false
ignore_unreachable: true
tasks:
- name: Ping hosts to check availability
ansible.builtin.ping:
register: ping_result
ignore_errors: true
ignore_unreachable: true
- name: Add reachable hosts to group
ansible.builtin.group_by:
key: reachable_hosts
when: ping_result is reachable and ping_result is succeeded
- name: Report offline hosts
ansible.builtin.debug:
msg: "{{ inventory_hostname }} - OFFLINE"
when: ping_result is unreachable or ping_result is failed
delegate_to: localhost
- name: User Setup - Create admin user on Ubuntu
hosts: ubuntu:&reachable_hosts
become: true
gather_facts: false
vars:
admin_user: harry
tasks:
- name: Create admin user in the sudo group - {{ admin_user }}
ansible.builtin.user:
name: "{{ admin_user }}"
shell: /bin/bash
groups: sudo
append: true
create_home: true
- name: User Setup - Create admin user on Rocky Linux
hosts: rocky:&reachable_hosts
become: true
gather_facts: false
vars:
admin_user: harry
tasks:
- name: Create admin user in the wheel group - {{ admin_user }}
ansible.builtin.user:
name: "{{ admin_user }}"
shell: /bin/bash
groups: wheel
append: true
create_home: true
- name: User Setup - Authorized keys and sudo
hosts: reachable_hosts
become: true
gather_facts: false
vars:
admin_user: harry
admin_sudo_nopasswd: true
admin_ssh_keys:
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINegitHaQG8i+s412h3wTWf/KvpeX1CEpYFva9MhHR+7 ansible-key"
- "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDbFKxd6W+gHcNrugkxMwvixGmk3LxtsMEnSdaFp4L78WZ4VhdZrsnSJQ/rViT+vuc6K5SeugtggfQ90L25UyyMv2BhLZaoMS2dpoVJEZon/Y2Hemm11vdEApAK/u0kPTTM5JB0/TZfpoP9sr4pmRIUruRXBhLDvtUV9tJvKSzbhChtB+vBg+MpywG9VvDP6eCVpFaP43RcAFtJR7irJmexd5m4CVhIFaWNeTbbe8YI08i2gDinYl95ygu9i/TorQKLWme2v9ejxQFfJTWGaXHnQ18W0X94Ns4a4OccGcvJ8JmvYsaAPRAOSveq1Fpdw1VXHSKigTmRl6pHWd9/rq6K6Ojctbjo1kHiNHdLKLZbLDeM0OQlzNl74snsImmZddY59wnOKyHtfY0ZI7sQ+7A/0cSaMY6h7lt2xK0WmayuoT8UtKZlItZHCkOA5bV29arI/HTz+yR/LW8SihPPFpVv5aDiOUs0PYd9j0+CtULBJeYpMQJpkD93g06vgEJjhUOBZ6JmilxVDihDARU0x0xiRCQJcq4Nhk/x3GzPe2kkrTI+nZFaQn1K8hcBdSyA12/xAG8Fh1SMCxLwJdCNuL3gQoYd6XPzKqn6BIqAgiyq5DbHhO6mehOye4Rro6i/H1UkZe862QH/R5uH+8Z2/FYRpiCPzQyLwrqx/eVfT7JMFQ== harwinder@harwinder.net" # yamllint disable-line rule:line-length
tasks:
- name: Add authorized keys for {{ admin_user }}
ansible.posix.authorized_key:
user: "{{ admin_user }}"
key: "{{ item }}"
state: present
loop: "{{ admin_ssh_keys }}"
loop_control:
label: "{{ item.split()[-1] }}"
- name: Grant sudo to {{ admin_user }}
ansible.builtin.copy:
dest: "/etc/sudoers.d/90-{{ admin_user }}"
content: "{{ admin_user }} ALL=(ALL) {{ 'NOPASSWD:' if admin_sudo_nopasswd | bool else '' }}ALL\n"
owner: root
group: root
mode: '0440'
validate: /usr/sbin/visudo -cf %s
- name: Summary
ansible.builtin.debug:
msg: >-
{{ inventory_hostname }} - user {{ admin_user }} ready
(sudo {{ 'NOPASSWD' if admin_sudo_nopasswd | bool else 'with password' }}, {{ admin_ssh_keys | length }} key(s))