RH294 Ch 3: Writing Your First Playbooks
A playbook is a YAML list of plays. Each play maps a group of hosts to an ordered list of tasks, and each task calls one module with arguments. Indentation is structure in YAML: use spaces only and keep the same depth for keys at the same level.
yaml --- - name: Web servers hosts: webservers tasks: - name: Install packages ansible.builtin.dnf: name: [httpd, firewalld] state: present - name: Home page ansible.builtin.copy: content: "Welcome\n" dest: /var/www/html/index.html - name: Start services ansible.builtin.service: name: httpd state: started enabled: true - name: Open the firewall ansible.posix.firewalld: service: http permanent: true immediate: true state: enabled
Modules you will use constantly - dnf: packages (state: present | latest | absent). - service / systemd: state: started | stopped | restarted and enabled: true. - copy: a file from src or inline content, with owner, group, mode. - user / group: accounts, uid, groups plus append: true to keep existing groups. - ansible.posix.firewalld: always set both permanent: true and immediate: true, otherwise the rule is either lost on reload or not active yet.
Running. ansible-navigator run site.yml runs inside the execution environment, which includes collections like ansible.posix. ansible-playbook site.yml uses the control node's own ansible-core, so modules outside ansible.builtin must be installed there too. Before a real run, check syntax with --syntax-check, and use --check (dry run) or -v for more detail.
Reading the recap. The PLAY RECAP line per host shows ok, changed, unreachable, failed. Run the playbook twice: a well written playbook reports changed=0 the second time. If something always shows as changed, you probably used command or shell where a real module exists.
Common mistakes - Tabs or uneven indentation, which give confusing parse errors; --syntax-check finds them. - Forgetting enabled: true, so the service does not survive a reboot. - Opening the firewall only at runtime (no permanent). - groups: wheel without append: true on an existing user, which removes the user's other supplementary groups. - Targeting the wrong hosts: check the play with ansible-navigator run site.yml --list-hosts or ansible webservers --list-hosts.