December 2, 2021 13:58
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
ProxyCommandwill be executed as the ansible user -> make sure the ansible user can access his own key, also you may omit theStrictHostKeyCheckinglater 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/pythonpath 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.