May 21, 2024

Experimenting with Ansible to Set Up a Minimal Linux User

I had to set up multiple machines with slightly varying configs repeatedly. After having to SSH into the machines once in a while and deal with default bash, I realized that I’m seriously not used to the lack of features, aliases1, and theme(s) that zsh + oh-my-zsh provide. In fact, I feel slightly less efficient when forced to use just plain bash.

Many years ago, I read multiple comments recommending Ansible to automate tasks. Although Ansible may have been VERY overkill for this (a simple shell script would have sufficed), I decided to try my hand at it to, ya know, learn something new. Apart from the docs2, I used Lorenzo Bettini’s playbook and Gabrgomes’ playbook as references.

I’m sure I’m not doing it the best way; instead of running the playbook locally, it would make more sense to have a control node that executes these tasks remotely on targeted hosts. I also have no doubt that there are better ways to write Ansible Playbooks. But eh, this gets the job done 🤷. This was a 2-4 hour experiment so please don’t judge me. TIA.

Ensure that python3-pip is installed, optionally create a virtualenv, and pip3 install ansible. Stuff this in a shell script, or use the control node & remote hosts method.

min-user-setup.ansible.yml:

---
# run me with `ansible-playbook -K min-user-setup.ansible.yml`

- name: Setup Compooter
  hosts: localhost

  tasks:
    - name: Install git, zsh, tmux, fzf, xclip, vim
      become: true
      ansible.builtin.package:
        name:
          - git
          - zsh
          - tmux
          - fzf
          - xclip
          - vim
        state: present

    - name: Change user shell to zsh
      become: true
        name: "{{ ansible_env.USER }}"
        shell: /usr/bin/zsh

    - name: Download Oh My Zsh installation script
      ansible.builtin.get_url:
        url: https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh
        dest: /tmp/install_ohmyzsh.sh
        mode: "0777"

    - name: Run OhMyZsh installation script
      ansible.builtin.command:
        cmd: sh /tmp/install_ohmyzsh.sh

    - name: Change .zshrc theme
      ansible.builtin.replace:
        path: "{{ ansible_env.HOME }}/.zshrc"
        regexp: 'ZSH_THEME=\"robbyrussell\"'
        replace: 'ZSH_THEME="essembeh"'

    - name: Enable .zshrc DISABLE_UNTRACKED_FILES_DIRTY
      ansible.builtin.replace:
        path: "{{ ansible_env.HOME }}/.zshrc"
        regexp: '# DISABLE_UNTRACKED_FILES_DIRTY=\"true\"'
        replace: 'DISABLE_UNTRACKED_FILES_DIRTY="true"'

    - name: Disable .zshrc omz:update
      ansible.builtin.replace:
        path: "{{ ansible_env.HOME }}/.zshrc"
        regexp: "# zstyle ':omz:update' mode disabled"
        replace: "zstyle ':omz:update' mode disabled"

    - name: Change tmux config
      ansible.builtin.copy:
        dest: "{{ ansible_env.HOME }}/.tmux.conf"
        content: |
          set -g default-terminal "screen-256color"
          set -g status-bg red
          set -g status-fg black
        mode: "0664"

    - name: Create .local/bin
      ansible.builtin.file:
        path: "{{ ansible_env.HOME }}/.local/bin/"
        state: directory
        mode: "0775"

    - name: Get custom utilities
      ansible.builtin.get_url:
        url: https://...
        dest: "{{ ansible_env.HOME }}/.local/bin/custom-utils"
        mode: "0775"

    - name: Add .local/bin to path
      ansible.builtin.lineinfile:
        path: "{{ ansible_env.HOME }}/.zshrc"
        insertafter: 'export ZSH=\$HOME/\.oh-my-zsh'
        line: "path+={{ ansible_env.HOME }}/.local/bin/"

    # clipboard: `echo 'hello' | cb`
    # may have to use xsel if xclip doesn't work (xsel -ip)
    - name: Add alias cb
      ansible.builtin.lineinfile:
        path: "{{ ansible_env.HOME }}/.zshrc"
        insertafter: "EOF"
        line: 'alias cb="xclip -i"'

    - name: Add alias objd
      ansible.builtin.lineinfile:
        path: "{{ ansible_env.HOME }}/.zshrc"
        insertafter: "EOF"
        line: 'alias objd="objdump -dlS --visualize-jumps=color"'
...

Footnotes

1 Gotta love glod and glols. These aliases of git log --graph come with the oh-my-zsh git plugin. A lot of these aliases have become muscle memory at this point.

2 Ansible’s docs are pretty darn good, but holy moly that search is occasionally difficult to go through due to the sheer number of features. It’s a good thing that built-in gets ranked to the top. Although… when I was experimenting with it a week `or so ago, built-in wasn’t ranked to the top. Looks like they changed it — going through the recent release notes, it looks like they tweak the weights of pages for ordering search results. I may have been visiting the docs when there was improper weighting. Regardless, good documentation once you get the hang of it after a half-hour.