The Missing Bit

From Ansible to plain script

2023-10-28

Introduction

I use Ansible to manage all my infrastructure. It's about 50 bare metal servers that sit in my basement.

It works well, but I have a few issues:

  • It can be very slow
  • YAML can be very tricky to edit
  • Sometimes, it takes ages to find how to do something that seems very simple
  • The output is not helpful, it is either not detailed enough, or a huge flow of debug information

The third one is the biggest pain point. I spent sometimes hours trying to get how to use some plugin to do something simple.

Ansible provides a lot of plugins, but most of them can be replaced by simple commands.

Some, which are a bit harder to replace, like lineinfile which "ensure there is a line in a file" I try to avoid, and instead have the whole file in my devops system and replace it.

I started to replace a few playbooks with shell scripts. And it works wonders. Everything is so simple.

Example

Here is an example:

---
- name: Michi pdns configuration
  gather_facts: false
  hosts: michi
  tasks:
  - package:
      name:
        - powerdns
        - entr
      state: present

  - service:
      name: pdns_server
      state: stopped

  - copy:
      src: pdns.conf
      dest: /etc/pdns/pdns.conf

  - copy:
      src: check-dynupdates.lua
      dest: /etc/pdns/check-dynupdates.lua


  - file:
      path: /var/db/pdns/
      state: directory

  - copy:
      src: schema-sqlite3.sql
      dest: /var/db/pdns/schema-sqlite3.sql

  - file:
      path: /var/db/pdns/pdns.sqlite3
      state: absent

  - shell: sqlite3 /var/db/pdns/pdns.sqlite3 < /var/db/pdns/schema-sqlite3.sql

  - file:
      path: /var/db/pdns/schema-sqlite3.sql
      state: absent

  - service:
      name: pdns_server
      state: started
      enabled: true

  - copy:
      src: sync.sh
      dest: /etc/pdns/sync.sh
      mode: 0700

  - copy:
      src: sync_daemon.sh
      dest: /etc/pdns/sync_daemon.sh
      mode: 0700

  - copy:
      src: pdns_sync
      dest: /etc/rc.d/pdns_sync
      mode: 0700

  - service:
      name: pdns_sync
      state: started
      enabled: true


to


#!/bin/sh

set -x

cd "$(dirname "$0")"


export TERM=vt100

SSH="ssh michi doas"

$SSH pkg_add powerdns entr
$SSH rcctl stop pdns_server

cat pdns.conf | $SSH tee /etc/pdns/pdns.conf > /dev/null
cat check-dynupdates.lua | $SSH tee /etc/pdns/check-dynupdates.lua > /dev/null
$SSH mdir -p /var/db/pdns/
cat schema-sqlite3.sql | $SSH tee /var/db/pdns/schema-sqlite3.sql > /dev/null
$SSH rm /var/db/pdns/pdns.sqlite3

$SSH sh -c 'sqlite3 /var/db/pdns/pdns.sqlite3 < /var/db/pdns/schema-sqlite3.sql'

$SSH rm /var/db/pdns/schema-sqlite3.sql

$SSH rcctl start pdns_server
$SSH rcctl enable pdns_server

cat sync.sh | $SSH tee /etc/pdns/sync.sh > /dev/null
$SSH chmod 700 /etc/pdns/sync.sh

cat sync_daemon.sh | $SSH tee /etc/pdns/sync_daemon.sh > /dev/null
$SSH chmod 700 /etc/pdns/sync_daemon.sh

cat pdns_sync | $SSH tee /etc/rc.d/pdns_sync > /dev/null
$SSH chmod 700 /etc/rc.d/pdns_sync
$SSH rcctl enable pdns_sync

This is for a single host, but for multiple host, you can do something like:


HOSTS="michi kiki"

for HOST in $HOSTS
do
    SSH="ssh $HOST ls"
    $SSH <do something>
done

Conclusion

I converted a dozen playbook to scripts and here are a few point:

  • It is much, MUCH easier to write, but this would depend on your sh experience.
  • It is much faster to run. If you have many hosts, I would look into GNU parallel because now it is sequential.
  • It is more extensible without having to read documentation.
  • Scripts are much shorter and easier to read (the above script has no comment for the example, but by real scripts have extensible comments)
If you wish to comment or discuss this post, just mention me on Bluesky or email me.