October 24, 2021 15:16
Master
Install
sudo apt install ansible
Done.
A word of warning beforehand: Try to use your local Ansible setup and NOT the global files like the one under /etc/ansible/hosts
. This is just to prevent you from running everything with root
24/7!
Create an ansible ssh-key
Create the key with ssh-keygen
(running from your home directory and therefore set the path to .ssh/ansible
). Edit ~/.ansible.cfg
and set private_key_file
to the path from before.
You install the key by using (NEVER EVER install this key into your root user, for security reasons):
ssh-copy-id -i ~/.ssh/ansible [TARGET_USER]@[TARGET_HOST]
Then test it with:
ssh ~/.ssh/ansible [TARGET_USER]@[TARGET_HOST]
And you can always use the jump-host configuration as shown below:
ssh-copy-id -i ~/.ssh/ansible -o ProxyCommand="ssh -i ~/.ssh/ansible -W %h:%p [JUMP_USER]@[JUMP_HOST]" [TARGET_USER]@[TARGET_HOST]
ssh ~/.ssh/ansible -o ProxyCommand="ssh -i ~/.ssh/ansible -W %h:%p [JUMP_USER]@[JUMP_HOST]" [TARGET_USER]@[TARGET_HOST]
Why not use -J
for the JumpHost
configuration? This option is new and does not inherit any further parameters from the parent process (like -i
). It can be really useful in other scenarios, but not for Ansible (right now).
Setup your users inventory
Add this to your .bashrc
(only if you wish to use a local inventory):
export ANSIBLE_INVENTORY=$HOME/ansible/hosts
Now create the hosts file as following…
Register hosts
Edit ~/ansible/hosts
(take a note at the excellent documentation in /etc/ansible/hosts
and there) and…
- add some hosts (at the top):
foo.example.com bar.example.com
- define a new group of hosts:
[servergroupname] one.example.com two.example.com
- define a ip variable for a specific host:
three.example.com ansible_host=[HOST_IP]
- define a username & key variable for a specific host:
four.example.com ansible_user=[USER] ansible_ssh_private_key_file=/some/other/ssh/key
- define a ssh-jump variable for a specific host (the
ProxyCommand
will be executed as the ansible user -> make sure the ansible user can access his own key, also you may omit theStrictHostKeyChecking
later on):five.example.com ansible_ssh_common_args='-o StrictHostKeyChecking=no -o ProxyCommand="ssh -i ~/.ssh/ansible -W %h:%p -q [JUMP_HOST_USER]@[JUMP_HOST_URL]"'
- define any variable for a specific host group:
[servergroupname:vars] ansible_user=[USER] ansible_ssh_private_key_file=/some/other/ssh/key
- define any variable for any host (this one variable is needed for newer installations, as the classic
/bin/python
path won’t work anymore!):[all:vars] ansible_python_interpreter=/usr/bin/python3
Test your connections
ansible all -m ping
You’ll may need to use sudo
, if the key was created inside the configuration directory.
Instead of all
you could also use any other configured group name like servergroupname
from before.
Running arbitrary commands
ansible all -a "uname -a"
Running arbitrary commands, but not everywhere
ansible 'all:!somegroup' -a "uname -a"
As root with password
ansible all -a "uname -a" --become --ask-become-pass
Define a Playbook
Create a new playbook.yml
and fill it (I would recommend to create it under ~/ansible/playbooks
):
---
- name: Playbook Demo
hosts: all
become: false
tasks:
- name: Receive hostnames
command: hostname
- name: Show uname
command: uname -a
register: out
- debug: var=out.stdout_lines
The debug
task will show the output variable - meaning the output of the module command
.
Run it:
ansible-playbook playbook.yaml
Install & remove some SSH keys
---
- name: SSH Key Demo
hosts: all
tasks:
- name: Make sure the ansible key is there
authorized_key:
user: "{{ ansible_user }}"
state: present
key: "{{ lookup('file', '/etc/ansible/ssh_key.pub') }}"
- name: Remove an untrusted (old) key
authorized_key:
user: "{{ ansible_user }}"
state: absent
key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDdOa2rCfsP6JtwMoO+3c10NgaPLasd7WA5yeYrdd5dJAmQOoHE0RL40POCd4zvq3k/8ehJ3DLcIkfcul6xj234ik2l/4lYHXMGas6Sz/VVvSjs4sfhlVkRm0cZIBXePjp5RNXPKZEtJih0D9aZEZOQ3dqOBloaPqzB2bkB1eF9lVlSLRl3NFF8xHh8vb7Il2+nqz4cvkq1XS1223aaXfNfQEJcuyk6ryAjtP8/y2oPuUlFY876YWbxd7Ct3xcGgpxVNS9ewlHBox9PKCtvK3g8DZvI2byB7bIT3nfcOrjkfA/ZP1WFGobOs/OGpb8Sh4I/Kq8fOu1MIHoaElQ/ngHBmD7I/o8PRutKIaC8c5sr3r3B10aJXkV2IHIzj08Qg8QCjJVj05/TcVg5ANkr6xy/mdSj1OOpfHW2Fk+xSj9xWSVRWxm0KOY5/7UMDfo1HjBW79xTIgk2Wa4Lx3pA6pxrv3yMX3XWhKF8oilA6QfsVLqNwElsK/Wk8XMgK2ulCojPoU= bad@key"
The authorized_key
-module - what is that?! When you are unsure what Ansible can do for you - try the documentation.
Install some applications
---
- name: APT Demo
hosts: all
become: true
tasks:
- name: Install THE BEST console text editor
apt:
name: nano
state: present
Make sure to run this one with --ask-become-pass
, as apt
can only be used as root!
Replay a stuck playbook
Just run (the .retry
file will be automatically created):
ansible-playbook playbook.yml --limit @playbook.retry
The automatic way
import os
import re
import glob
import logging
import argparse
import subprocess
"""
Run this by using crontab - should be silent if everything is unreachable, otherwise it will show outputs!
Example need? Try that:
*/10 * * * * cd $HOME/ansible && python3 ./autoretry.py
...also ensure that crontab is sending you the commands output as e.g. email!
"""
parser = argparse.ArgumentParser()
parser.add_argument('-d', '--debug', action='store_true', help='Show debug msgs')
if parser.parse_args().debug:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
files = glob.glob('./**/*.retry', recursive=True)
for playbookRetry in files:
# Setup vars
playbookPath = os.path.split(playbookRetry)
playbookExt = os.path.splitext(playbookPath[1])
playbookYml = os.path.join(playbookPath[0], playbookExt[0] + '.yml')
logger.debug(playbookPath[0])
logger.debug(playbookYml)
logger.debug(playbookRetry)
# Execute the playbook
if not os.path.isfile(playbookYml):
logger.warning('Playbook src for retry not found!')
continue
try:
# Yes, subprocess is bad - but the ansible python api is the worst. This works. Even using different versions. The api? Not.
res = subprocess.run(['ansible-playbook', playbookYml, '--limit', '@' + os.path.join(playbookPath[0], playbookExt[0] + '.retry')],
capture_output=True, cwd=os.getcwd(), timeout=60*60*2) # Timeout after 2 hours
except subprocess.TimeoutExpired:
logger.error('2h timeout on command execution')
continue
# Check if anything was successful or if we completed all
lines = res.stdout.decode().split('\n')
doneSmth = False
unreachSmth = False
for l in lines:
if re.match(r'.+:.+ok=.+changed=.+unreachable=0.+failed=.+', l):
doneSmth = True
if re.match(r'.+:.+ok=.+changed=.+unreachable=1.+failed=.+', l):
unreachSmth = True
# Show logs on success or failures
for l in lines:
l = l.strip()
if len(l) == 0:
continue
if doneSmth:
logger.info(l)
else:
logger.debug(l)
# ...else: Done nothing. Try again later...
logger.debug('doneSmth ' + str(doneSmth))
logger.debug('unreachSmth ' + str(unreachSmth))
if not unreachSmth:
logger.info('All plays finished. Removing retry file!')
os.remove(playbookRetry)
Enjoy.