I’m with out a doubt not a coder, i’m a more traditional sysadmin who over the years has turned to services like Puppet and Chef to perform automated tasks on the systems I run.

The current darling of the Infrastructure Automation as Code world is Ansible and the following is my journey getting to grips with it.

The purpose of this blog is to get to a point where from a single Ansible host something useful can be done on several remote systems. This is a work in progress and it’s a learning document. I’ve found most of the howto guides I’ve followed don’t answer the questions I have at all or in a fashion which makes sense to me.

Where possible I have tried to put the issues I have had and how I resolved them.

My End Goal is to get Ansible playbooks working with Rundeck to manage remote systems.

This is NOT for production systems…

This is NOT written by an Ansible Guru…

I will answer questions on Reddit

mightywomble (u/mightywomble) – Reddit
u/mightywomble: πŸ’» Dell XPS13 – OpenSuse Tumbleweed πŸ’» HPChromebook X2 – ChromeOS πŸ’» Lenovo T430 – Windows10 πŸ“± Samsung Note 9 πŸ“± Oneplus6 πŸ“· Nikon …


0.9.0 1 June 2020  Basic Notes and framework got down Complete Playbooks at end of the post
0.9.1 4 June 2020 Added an extended simple playbook example Create a broken up Playbook with reusuable code


I have a mix of 3 server OS on my HomeLab

  • Ubuntu 18.04
  • Opensuse Leap 15.1
  • CentOS 7

They are on a flat subnet and run either on top of VMware ESXi or Proxmox, all of the servers have python2 installed, and to make life easier i’ve installed ansible on all the servers as it installs the prerequs needed to run stuff it seems.

The Ansible Host

I’ve set up an Ubuntu 18.04 server as what i’m calling my Ansible Host, its the server which all the ansible commands will be run from, with passwordless ssh access to the other servers on the Home Lan

Everything i’m running here, runs from my Ansible host

Passwordless SSH

These instructions are based on not having any ssh keys currently in the environment, these will be overwritten if they are already there.

Login to the Ansible host and run

ssh-keygen -t rsa

Connect to the remote host(s) you want to login to from the Ansible Host using passwordless ssh. and create a .ssh folder under the users home folder

ssh david@ mkdir -p .sshThe authenticity of host ' (' can't be established.RSA key fingerprint is 45:0e:28:11:d6:81:62:16:04:3f:db:38:02:la:22:4e.Are you sure you want to continue connecting (yes/no)? yesWarning: Permanently added '' (ECDSA) to the list of known hosts.david@'s password: [Enter Your Password Here]

Copy the public key over to the remote site as an authorized_keys file

cat .ssh/id_rsa.pub | ssh david@ 'cat >> .ssh/authorized_keys'david@'s password: [Enter Your Password Here]

Change the rights on the authorized_keys, this is important depending on the version of ssh server used.

ssh david@ "chmod 700 .ssh; chmod 640 .ssh/authorized_keys"david@'s password: [Enter Your Password Here]

From the Ansible host ssh to the host should no longer require a password.

ssh david@

Repeat the last 4 steps on each remote system the Ansible host will need to connect to.

Installing Ansible

In this example I’m using an Ubuntu 18.04 box to run my Ansible code from across the servers on my home lan

sudo apt install ansible

Setting up /etc/ansible/hosts Inventory file

The central location for the Ansible host to know which machines ansible should talk to is in /etc/ansible/hosts which can be edited the default install is pretty well documented. Its also possible to change the location of this inventory file, however that’s not something I need to do.

In my case I’ve used IP Addresses, resolvable DNS names can also be used.

As you can also see I’ve created 3 groups [opensuse], [ubuntu] and [centos7] as I know i’m going to be running code based on the OS not the service I’m running on them.

[opensuse]#git.lan - gitlab - media server - docker and test - mariadb - nextcloud server - Proxy server - Samba Domain controller[ubuntu]#home.lan - Andible and general jumpbox - Website and external reverse proxy - Ubuntu 20.04 Desktop on Proxmox[centos7]#rundeck.lan - Rundeck Automation server

Save and exit the file


Does Ansible see your hosts in the hosts file?

To check that Ansible is reading your hosts file correctly run

ansible all --list-hosts

should display

hosts (11):

Test with Ansible Ping

I’m going to use the following command to test i’m able to connect from my ansible host to the remote nodes I’ve listed in the Inventory hosts file.

ansible all -m ping -u USER

Note the -u USER needs to be changed to the user you can ssh using the passwordless key we setup earlier with, so in my case I’m going to use a user david

ansible all -m ping -u david

When i first ran this i got both


These look like this | SUCCESS => {"changed": false, "ping": "pong"}


Mine looked like this | FAILED! => {"changed": false, "module_stderr": "Shared connection to closed.\r\n", "module_stdout": "/bin/sh: /usr/bin/python: No such file or directory\r\n", "msg": "MODULE FAILURE", "rc": 127


Getting Errors

So obviously the errors are not correct, and a little digging came up with the reason, it seems when the Ansible application is installed it makes sure that the python modules are setup however python3 and python2 (aka python) seem to have a few path issues.

The Python problem

On the OpenSUSE Leap and Ubuntu 18.04 i’m running python3 is installed and while the command

which python3



the command

which python

doesn’t come back with anything, which proves a problem to the Ansible application when run remotely

To solve this I ran the symlink command

sudo ln -s /usr/bin/python3 /usr/bin/python

This then showed the python executable

which python/usr/bin/python

Which allowed me a 100 success run of the command

ansible all -m ping -u USER

We now have communication, time to run some remote commands

Adhoc Commands

What are these?

These are essentially a method of using Ansible to run commands on remote servers either as all or as groups.

ansible all -b --become-user=root -m shell -a 'hostname' -u david 

The switches

  • -b: Instruct ansible to become another user to run the command
  • --become-user=root: Run the command as a root user
  • -m: Declares which module is used in the command
  • -a: Declares which arguments are passed to the module

Possibly result in | FAILED! => {"changed": false, "module_stderr": "Shared connection to closed.\r\n", "module_stdout": "sudo: a password is required\r\n", "msg": "MODULE FAILURE", "rc": 1}

as we can see

sudo: a password is required

use –extra-vars with ansible_sudo_pass

ansible all -b --become-user=root -m shell -a 'hostname' -u david --extra-vars "ansible_sudo_pass=SuperSecretPassword"

will show | SUCCESS | rc=0 >>external

Problem is this shows the sudo password in clear text

How do i supply Ansible the sudo password without entering the password in the command line (or playbook)?

Option 1: Using group_vars

This option stores the passwords in a file on the Ansible Host in cleartext. While I agree this is only partially better than adding the password directly in a playbook, we can build on this method later

Create the group_vars folder

sudo mkdir /etc/ansible/group_vars/

Then for each group heading in your /etc/ansible/hosts file (the ones in mine above are [opensuse], [ubuntu], [centos7]

Create a file under /etc/ansible/group_vars with the same name as the group containing the following

Note: ENTERYOURSUDOPASSWORDHERE needs to be the sudo password not the actual words

ansible_user: davidansible_become: Trueansible_become_pass: **ENTERYOURSUDOPASSWORDHERE**

You should end up the with the following file/folder structure (your group names will be different)


Now when I run the command

ansible all -b --become-user=root -m shell -a 'hostname' -u david 

I get the result | SUCCESS | rc=0 >>external

I didn’t need to use the –extra-vars “ansible_sudo_pass=SuperSecretPassword” switch

Option 2: Using ansible-vault

I’m adding option 2 here even though at time of writing i’ve not managed to get it to work, I’ve been following the instructions at

How To Use Vault to Protect Sensitive Ansible Data on Ubuntu 16.04 | DigitalOcean
Ansible Vault is a feature that allows users to encrypt values and data structures within Ansible projects. This provides the ability to secure sensitive data that may be necessary to successfully run Ansible plays, but should not be publicly visible. Ansible is able to…

The flow is to on the Ansible Host

  • Create an encrypted file using the ansible-vault command
  • put the password in the generated file
  • Save and exit
  • create a hidden text file ~/.vault_pass with the password to Ansible Vault
  • from the command line set an environment variable

export ANSIBLE_VAULT_PASSWORD_FILE=~/.vault_pass

  • You shouldn’t need to enter the password for the vault file now

As I said, at the time of writing, i’m using Option 1 as its a home lab, and will look to figure out option 2 later.


While its great being able to run adhoc commands from the ansible command line, the fact is for this to be powerful we should be using playbooks, sets of tasks run in order from the Ansible Host.

Rather than go over how to write playbooks, what they are and how they work which the entire internet has well covered (especially if you want to install a web server), i’m going to put here some examples and notes around them.

Install Files based on OS


When I build a new machine, there are a set of standard applications I want to install and files I like to add to the system. Rather than being a manual task I’d like to make this using Ansible Playbook.

I’d like to run this as a single playbook which has outcomes based on the remote OS as I use either Ubuntu, CentOS or OpenSUSE


My initial Playbook is really nothing more than a YML task list


- hosts: all  become: yes  vars:    user:      - name: "david"    packages:      - nano      - wget      - nmap      - htop  tasks:    - name: Ensure a list of packages installed      update_cache: yes      package:        name: "{{ packages }}"        state: latest    - name: All done!      debug:        msg: Packages have been successfully installe

Run with the line

ansible-playbook postinstall.yml 


For package to use the zypper installer on OpenSUSE python-xml needs installing

sudo zypper in python-xml

I’ve used the Ansible package module as its cross linus OS compatible so supports in my example Apt, RPM and Zypper

Install Files based on OS Extended

Building on the above code I’ve build a longer playbook.yaml file to install apps from the package manager and from repos


#This section adds the repo for the OpenSUSE version of Cockpit#I extracted the URL for the repository out of the .repo file#its set to run only on the OpenSUSE hosts- hosts: opensuse  become: yes  vars:    user:      - name: "david"  tasks:    - name: Adding Cockpit repo on Opensuse      zypper_repository:        name: Cockpit        repo: 'https://download.opensuse.org/repositories/systemsmanagement:/cockpit/openSUSE_Leap_15.1/'    auto_import_keys: yes    overwrite_multiple: yes    state: present    runrefresh: yes#This section uses the ansible package installer 'package' to install software across CentOS (yum), Ubuntu (apt) and OpenSuse (zypper)#this only works for packages where the package name is the same across the distros      - hosts: all  become: yes  vars:    user:      - name: "david"    packages:      - nano      - wget      - nmap  - htop  - cockpit  - git  - nodejs  tasks:    - name: Ensure a list of packages installed      package:        name: "{{ packages }}"        state: latest    - name: All done!      debug:        msg: Packages have been successfully installed- hosts: all  become: yes  vars:    user:      - name: "david"  tasks:    - name: Install Netdata      git:        repo: 'https://github.com/firehol/netdata.git'        dest: /opt/netdata/        clone: yes        update: yes    - name: to run the shell script      shell: bash netdata-installer.sh --dont-wait      creates: /opt/netdata/installed      args:        chdir: /opt/netdata  handlers:    - name: to restart netdata      service:        name: netdata        state: restarted- hosts: all  become: yes  vars:    user:      - name: "david"  tasks:    - name: Link Netdata locally to netdata cloud      shell: netdata-claim.sh -token=random_datat -rooms=random room number -url=https://app.netdata.cloud      creates: /opt/netdata/claimed- hosts: all  become: yes  vars:    user:       - name: "david"  tasks:    - name: ensure file exists      copy:        content: ""        dest: /opt/netdata/claimed        force: no        group: sys        owner: root        mode: 0555


Some points to note here

  • I start the playbook using zypper_repository because unlike Ubuntu or CentOS, Opensuse doesn’t have Cockpit in its coore repository. Because I classify my devices by OS this is simple by using the hosts: opensuse command
  • To install the software from the Repos, I’ve used the Ansible Package module which utilises the OS’s built in package module which has its pro’s and cons. The pro is the use of one Ansible and not so much repeating code. The con is that packages are not always named the same across distros.
  • I’ve used the git module to pull down the netdata code from the internet locally on the box. This will upgrade Netdata as well,
  • Finally i’ve dropped out to a shell to run a Netdata command to link the host to my Netdata cloud account, at the end of this i have used the module ensure file exists to confirm if the shell command has ben run before.


Update a file in a folder




Manage /etc/ansible/hosts using git pull




Backup my Ghost CMS Site




Testing with Molecule



A step by step guide to Ansible (Tutorial)

SSH Passwordless Login Using SSH Keygen in 5 Easy Steps
Ansible playbook tutorial | How to write a playbook with example | GoLinuxCloud
Ansible playbook Tutorial. What is YAML. What is Ansible Playbook. Beginners guide to write playbook with example. Sample ansible playbook to install apache

By davidfield

Tech Entusiast