Skip to main content

Setting up a Ansible on Debian Buster

Shenzhen, China

Installing Ansible

You install Ansible on a control node, which then uses SSH (by default) to communicate with your managed nodes (those end devices you want to automate).

Using APT Packages

apt install -y ansible

Via Ubuntu PPA

Why ? EDIT: apt installed version ansible 2.7.7. Via Ubuntu PPA I received the version ansible/trusty 2.9.15.

Add the following line to /etc/apt/sources.list:

deb http://ppa.launchpad.net/ansible/ansible/ubuntu trusty main

Then run these commands:

apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 93C4A3FD7BB9C367
apt update
apt install ansible

Configuration

/etc/ansible
├── ansible.cfg
└── hosts
[defaults]

inventory = /etc/ansible/hosts
library = /usr/share/my_modules/
module_utils = /usr/share/my_module_utils/
remote_tmp = ~/.ansible/tmp
local_tmp = ~/.ansible/tmp
plugin_filters_cfg = /etc/ansible/plugin_filters.yml
forks = 5
poll_interval = 15
sudo_user = root
ask_sudo_pass = True
ask_pass = True
transport = smart
remote_port = 22
module_lang = C
module_set_locale = False

Ansible facts ~ Salt grains. Can be limited to speed up playbook executions

# plays will gather facts by default, which contain information about
# the remote system.
#
# smart - gather by default, but don't regather if already gathered
# implicit - gather by default, turn off with gather_facts: False
# explicit - do not gather by default, must say gather_facts: True
gathering = implicit

# This only affects the gathering done by a play's gather_facts directive,
# by default gathering retrieves all facts subsets
# all - gather all subsets
# network - gather min and network facts
# hardware - gather hardware facts (longest facts to retrieve)
# virtual - gather min and virtual facts
# facter - import facts from facter
# ohai - import facts from ohai
# You can combine them using comma (ex: network,virtual)
# You can negate them using ! (ex: !hardware,!facter,!ohai)
# A minimal set of facts is always gathered.
gather_subset = all

Accept all SSH hosts without prompts

# uncomment this to disable SSH key host checking
host_key_checking = False

Increase timeout for high latency Environments

# SSH timeout
timeout = 10

Log path for debugging

# logging is off by default unless this path is defined
# if so defined, consider logrotate
log_path = /var/log/ansible.log

Vault for passwords/secrets

# If set, configures the path to the Vault password file as an alternative to
# specifying --vault-password-file on the command line.
vault_password_file = /path/to/vault_password_file

Inventory

This is the default ansible hosts file It should live in /etc/ansible/hosts:

# Ex 1: Ungrouped hosts, specify before any group headers.

green.example.com
blue.example.com
192.168.100.1
192.168.100.10

# Ex 2: A collection of hosts belonging to the 'webservers' group

[webservers]
alpha.example.org
beta.example.org
192.168.1.100
192.168.1.110

# If you have multiple hosts following a pattern you can specify
# them like this:

www[001:006].example.com
db-[99:101]-node.example.com

I am going delete this templates data and add the server I am running Ansible on as my test node:

[test]
192.168.2.111 ansible_user=root

I am specifying the user that I want to be used for the SSH login. I don't have to do this in my case as the default user is set to root anyhow - see configuration file above.

Adding SSH Public Keys

We can try to verify that everything is set up correctly by pinging all hosts inside our inventory:

ansible -m ping all

192.168.2.111 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: root@192.168.2.111: Permission denied (publickey,password).",
"unreachable": true
}

This fails, since I did not specify the SSH password anywhere. You can now use Vault to add your password. Or generate a RSA key pair to have Ansible use the public key to login:

ssh-keygen -t rsa -b 2048

Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa
Your public key has been saved in /root/.ssh/id_rsa.pub

I left the passphrase empty

I set the permissions for both the pub key and the .ssh folder:

chmod 0400 $HOME/.ssh/id_rsa.pub
chmod 600 ~/.ssh/authorized_keys
chmod 0700 $HOME/.ssh/

I can now copy the generated ID to my host server (in my case they just happen to be the same PC):

ssh-copy-id root@192.168.2.111

/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
The authenticity of host '192.168.2.111 (192.168.2.111)' can't be established.
ECDSA key fingerprint is SHA256:6bWFxW4mJV/KVpdBFf8N0grugHHNqttF3jyX3cmVI9s.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@192.168.2.111's password:

Number of key(s) added: 1

Now try logging into the machine, with: "ssh 'root@192.168.2.111'"
and check to make sure that only the key(s) you wanted were added.

This will add the key of my Ansible minion to the Ansible master. You can verify that the key was added by executing:

cat ~/.ssh/authorized_keys

Running the ping again should now give us the desired result - SUCCESS:

ansible all -m ping

192.168.2.111 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}

We can also ping our generated host group ansible test -m ping or a single host with ansible 192.168.2.111 -m ping. Since I only have this one host all of those give me the identical result.

Ansible Commands

An example for an ad-hoc Ansible command is the ping command we used above - they all follow the same format:

ansible <target> -m <module> -a <arguments>

As an example, we can copy a file to the remote location using the Copy module (you can check all available modules with ansible-doc -l - or get information about the specific module ansible-doc copy):

ansible 192.168.2.111 -m copy -a "src=/tmp/test1 dest=/tmp/test2"

192.168.2.111 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": true,
"checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
"dest": "/tmp/test2",
"gid": 0,
"group": "root",
"md5sum": "d41d8cd98f00b204e9800998ecf8427e",
"mode": "0644",
"owner": "root",
"size": 0,
"src": "/root/.ansible/tmp/ansible-tmp-1605901636.6985757-12382-77236351905118/source",
"state": "file",
"uid": 0
}

Make sure that the test1 file exist on your Ansible master - touch /tmp/test1. After running the Copy command you should see both a test1 and test2 file inside the /tmp directory (as minion and master are on the same host).

Ansible Facts

Just like Salt with Grains Ansible collects system information as Facts:

ansible 192.168.2.111 -m setup

192.168.2.111 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"172.17.0.1",
"172.18.0.1",
"192.168.2.111"
],
"ansible_all_ipv6_addresses": [
"fe80::e020:79ff:fed8:34a4",
],
"ansible_apparmor": {
"status": "enabled"
}
"module_setup": true
},


...

"changed": false
}

Ansible Variables

Variables naming convention - alphanumeric, cannot start with a number and contain -, &, % or SPACES.

Variables can be used to hold configurations that you might have to change over time or are different between different hosts - a global variable that is than used by playbook, e.g. a port for a service. The variables can be defined in the Ansible Inventory, inside a playbook or inside a separate file that can be included into a playbook.

I already defined a variable inside the host inventory by setting the ansible user to root:

[test]
192.168.2.111 ansible_user=root