Sometimes I want a server up an running as quickly as possible to test some new things out. I could go in manually and set up users, packages, ssh keys, etc, but that’ll take ages manually, especially when I spin up multiple servers! This is where Ansible comes into play, mainly the ansible-pull command. I can set up a public git repo with all the basic configs I need to hop into a server after it’s launched; bonus points if I can use cloud-init or userdata in platforms such as AWS, Digitalocean, or Hetzner.

DirectoryΒ structure

CreateΒ aΒ bareΒ repositoryΒ withΒ theΒ followingΒ structure. I’ve installed ansible via pip module so you’ll see venv and requirements.txt. Install ansible however you like!

❯ tree -I 'venv|.git' -a
β”œβ”€β”€ .gitignore <-- adds venv/ to gitignore
β”œβ”€β”€ local.yml <-- entrypoint playbook for ansible-pull
β”œβ”€β”€ requirements.txt <-- pip requires for ansible
└── venv <-- virtual environment for python
└── roles <-- roles directory
    └── bootstrap <-- bootstrap role
        β”œβ”€β”€ files
        β”œβ”€β”€ handlers
        β”œβ”€β”€ tasks
        β”‚Β Β  └── main.yml <-- main tasks to execute
        β”œβ”€β”€ templates
        └── vars

Modify local.yml file to use bootstrap role

Your local.yml file should use a local connection with localhost specified as it’s hosts. Make sure have become: true and the role specified with a tag. Sample below

- name: Bootstrap role
  hosts: localhost
  connection: local
  become: true
    - bootstrap
  tags: bootstrap

Add tasks to the bootstrap role

I’ll add a simple task that will install a few packages inside roles/bootsrap/tasks/main.yml

- name: Install common apt packages
    name: "{{ item }}"
    update_cache: yes
    state: present
  loop: "{{ common_packages }}" 

The code above uses a loop to install a list of packages from a variables files inside roles/vars/main.yml

  - apt-transport-https
  - ca-certificates
  - curl
  - htop
  - openssh-server
  - net-tools
  - neovim
  - python3
  - software-properties-common
  - sudo
  - tmux
  - vim
  - unattended-upgrades

Now the project structure should look like this

❯ tree -I 'venv|.git' -a
β”œβ”€β”€ .gitignore
β”œβ”€β”€ local.yml
β”œβ”€β”€ requirements.txt
└── roles
    └── bootstrap
        β”œβ”€β”€ files
        β”œβ”€β”€ handlers
        β”œβ”€β”€ tasks
        β”‚   └── main.yml
        β”œβ”€β”€ templates
        └── vars
            └── main.yml

9 directories, 8 files

Testing the playbook with Molecule before deploying

It’s a good practice to test these changes locally before deploying into a server. Complex deployments that modify configuration files may cause issues with your server if you’re not prepared to catch them beforehand. Check out Jeff Geerling’s Youtube video for a deep dive on Ansible + Molecule. For the sake of the blog post I’ll post my configurations on testing the role on Ubuntu 20.04 using the Docker driver.

❯ tree -I 'venv|.git' -a
β”œβ”€β”€ files
β”œβ”€β”€ handlers
β”œβ”€β”€ molecule
β”‚Β Β  └── default
β”‚Β Β      β”œβ”€β”€ converge.yml
β”‚Β Β      β”œβ”€β”€ molecule.yml
β”‚Β Β      └── verify.yml
β”œβ”€β”€ tasks
β”‚Β Β  └── main.yml
β”œβ”€β”€ templates
└── vars
    └── main.yml
- name: Converge
  hosts: all
    - name: "Include bootstrap"
        name: "bootstrap"
  name: galaxy
  name: docker
lint: |
  set -e
  yamllint .
  - name: ubuntu2004
    image: geerlingguy/docker-ubuntu2004-ansible:latest
    pre_build_image: true
    privileged: true
    command: ""
      - /sys/fs/cgroup:/sys/fs/cgroup:ro
  name: ansible
  name: ansible
  name: default
    - lint
    - syntax
    - create
    - converge
    - idempotence
    - verify
    - destroy
# This is an example playbook to execute Ansible tests.
- name: Verify
  hosts: all
  gather_facts: false
    - name: Example assertion
        that: true

Bootstrapping a server

We currently a simple Ansible playbook that will install a few package but this can be extending to whatever your heart desires! You can view my personal repo for an example of how I configure and bootstrap my servers. When provisioning a server locally in my homelab or on the cloud for lab usage I usually use a root user to run the following commands (Sample below is targeted towards Debian based distros).


apt update
apt install ansible git -y
ansible-pull -U -t server