ALL system changes MUST go through Ansible playbooks. Manual operations are PROHIBITED.
This is a hard rule with NO exceptions.
- ❌ Manual file copies —
sudo cp file /path/orcp file dest - ❌ Manual installations —
sudo dnf install,npm install -g,pip install - ❌ Manual configuration edits — Direct editing of system files
- ❌ Manual service management —
systemctl enable/startcommands - ❌ Manual downloads —
curlorwgetscripts piped to shell - ❌ Manual symlinks —
ln -soperations - ❌ Manual testing —
pip install packageto "test if it works" - ❌ Direct file creation — Creating files directly in
~/.local/bin/or/usr/local/bin/
Edit → Playbook → Deploy → Test (in this order, always):
- Edit source files in the repository (e.g.,
extensions/,files/) - Update/create playbook in
playbooks/imports/ - Run the playbook to deploy to the system
- Test the deployed result on the system
# 1. Edit files in repo
vim extensions/my-extension/script.sh
# 2. Update playbook to deploy
vim playbooks/imports/optional/common/play-my-feature.yml
# 3. Deploy via Ansible
ansible-playbook playbooks/imports/optional/common/play-my-feature.yml
# 4. Test the deployed result
~/.local/bin/script.sh --test- Creates drift between repo and system state
- Breaks auditability — changes not tracked in version control
- Defeats reproducibility — system can't be rebuilt from git
- "Quick test" trap — manual state often becomes permanent
- Debugging confusion — is the bug in the playbook or the manual config?
Always use Ansible modules:
copy,template— for file deploymentpackage,dnf— for installationsservice,systemd— for service managementfile— for permissions, ownership, symlinksget_url— for downloads
Ensure idempotency:
- Playbooks must be safe to run multiple times
- Use
createsparameter for shell commands that should run once - Use declarative state (
state: present,state: started) - Check before change with conditionals
# GOOD: Idempotent with creates
- name: Install from URL
shell: wget https://example.com/install.sh && bash install.sh
args:
creates: /usr/bin/installed_binary
# BAD: Will fail on second run
- name: Install from URL
shell: wget https://example.com/install.sh && bash install.shTest playbook changes:
- Verify with
--checkor--diffflags before applying
# ✅ GOOD - Just query, don't install
dnf info package-name
pip index versions package-name
# ❌ BAD - Actually installs
pip install package-name
dnf install package-name- Fix the PLAYBOOK, not the system
- Re-run the playbook
- Test again
- Repeat until working
The playbook IS the source of truth. The system state is just a reflection of it.
- STOP immediately
- Identify the Ansible playbook that should handle this
- Verify the playbook will correctly deploy the changes
- If playbook is missing/incomplete, UPDATE THE PLAYBOOK FIRST
- Then recommend running the playbook