Preparing hosts to use ansible... with ansible

Ansible for all

As we saw in Ansible from scratch, Ansible will allow us to manage resuources in fast, ordered and efficient way, when we start over ina  new environment, one of our first tasks is creating templates for new machines and we can create our new template prepared with accesses for Ansible, and all is quite fast. But when we are planning to deploy ansible in a new environment it is not clean reusing already created users so we need to create a new user that would be able to access by ssh and run sudo, but, this has a couple of drawbacks:

  1. We are already breaking all Ansible principles
  2. When you have lots of machines it is an enormous job.

We will see how using ansible we will be able to do that, we only need an user with ssh access to servers added to sudoers (if we are currently managing servers we might have different users on different servers).

Playbooks

I won't enter in details about what are playbooks because Ansible has a deep documentation about that, I will only point on our previous example we have been doing Ad-Hoc commands. That is something that's nice if you can avoid login to twenty servers to make the same command one each and every one of them, the chance of doing that from a single server on a single line of code should be enough for us to bend our knee and thank God for ahaving a tool like that. But, the real power of Ansible starts with playbooks. Playbooks are plain task books for Ansible in which you can make ordered tasks instead of doing one after another from command line or automating server creation just with a playbook or a set of playbooks.

Playbooks are written in YAML, the new holy grail for config files, no matter where tou look at, you will find YAML, so there should be a reason.  I won't enter on describing all the syntax since it's also on the deep documentation about that, I will only make a small brief about that.

A YAML file is like a schema of those we made back in the School to be able to study things that were related, different schema levels are managed by indentation, this way a YAML file has to be something like:

---
-Afluentes del Sil
  Por la izquierda:
    Casoio
    Bibei
    Navea
    Mao
  Por la derecha:
    Soldón
    Lor
    Cabe

If we remove a level of indentation from Navea it would be mean that Sil has tributaries from left, right and Navea, so indentations are a must.

Ahora que ya hemos repasado la geografía, podemos volver con nuestra instalación, lo que queremos es crear un grupo ansible, con un usuario ansrunner -los nombres son a gusto del artista- que nos permita acceder por ssh sin contraseña (con certificado, tampoco nos pongamos chulos) y que pueda usar sudo también sin contraseña, ya que, tanto con ansible como con ansible-playbook siempre podremos meter la contraseña de usuario y la de sudo, pero pierde un poco la gracia (y la utilidad).

Suponiendo que tenemos nuestro fichero hosts ya creado, lo primero que necesitamos es añadir las claves de los servidores en el fichero known_hosts de nuestro usuario en nuestro equipo(o servidor donde tengamos ansible) ya que, de lo contrario no nos dejará usar sshpass y nos dirá que:

fatal: [servidor1] => Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this.  Please add this host's fingerprint to your known_hosts file to manage this host.

Para añadir la clave de los equipos que nos interesen en nuestro known_hosts podemos hacer lo siguiente:

ssh-keyscan servidor1 > .ssh/known_hosts

Y ya lo tenemos todo listo, podemos editar nuestros playbook y nos quedará algo del estilo de:

---
- hosts: servidor1
  user: usuarioconacceso
  sudo: yes
  tasks:

  - name: Creamos el grupo 'ansible' si no existe
    group:
      name: ansible
      state: present

  - name: Añadimos el grupo 'ansible' a sudoers sin necesidad de contraseña
    lineinfile:
      dest: /etc/sudoers
      state: present
      regexp: '^%ansible'
      line: '%ansible ALL=(ALL) NOPASSWD: ALL'
      validate: 'visudo -cf %s'

  - name: Añadir usuario 'ansrunner' al grupo 'ansible'
    user: name=ansrunner groups=ansible append=yes state=present createhome=yes

  - name: Añadir la clave rsa al usuario 'ansrunner'

    authorized_key:
      user: ansrunner
      state: present
      key: "{{ lookup('file', '/home/miusuario/.ssh/id_rsa.pub') }}"

Este playbook ya lo podemos utilizar siempre que usuarioconacceso tenga acceso a servidor1 y pueda hacer sudo en servidor1:

miusuario@miservidor:~$ ansible-playbook playbooks/crear_usuario.yml -k -K
SSH password:
sudo password [defaults to SSH password]:

PLAY [local] ******************************************************************

GATHERING FACTS ***************************************************************
ok: [servidor1]

TASK: [Creamos el grupo 'ansible' si no existe] *******************************
changed: [servidor1]

TASK: [Añadimos el grupo 'ansible' a sudoers sin necesidad de contraseña] *****
changed: [servidor1]

TASK: [Añadir usuario 'ansrunner' al grupo 'ansible'] *************************
changed: [servidor1]

TASK: [Añadir la clave rsa al usuario 'ansrunner'] ****************************
changed: [servidor1]

PLAY RECAP ********************************************************************
servidor1                  : ok=5    changed=4    unreachable=0    failed=0

Y ahora viene lo bonito de la idempotencia, si volvemos a ejecutar el mismo Playbook ahora obtenemos:

PLAY [servidor1] ******************************************************************

GATHERING FACTS ***************************************************************
ok: [servidor1]

TASK: [Creamos el grupo 'ansible' si no existe] *******************************
ok: [servidor1]

TASK: [Añadimos el grupo 'ansible' a sudoers sin necesidad de contraseña] *****
ok: [servidor1]

TASK: [Añadir usuario 'ansrunner' al grupo 'ansible'] *************************
ok: [servidor1]

TASK: [Añadir la clave rsa al usuario 'ansrunner'] ****************************
ok: [servidor1]

PLAY RECAP ********************************************************************
servidor1                  : ok=5    changed=0    unreachable=0    failed=0

Si ahora intentamos usar ansible con ese equipo y el usuario remoto ansrunner:

miusuario@miservidor:~$ ansible servidor1 -m ping -u ansrunner
127.0.0.1 | success >> {
    "changed": false,
    "ping": "pong"
}

Ya tenemos ansible listo para hacer lo que queramos en servidor1, y lo que es más importante, cambiando los hosts - o mejor aún, añadiendo hosts o grupos - al playbook, podemos repetir las tareas en todos los equipos, o incluso con un simple all hacerlo en todos los equipos del inventario. Y es por esto, que una buena colección de Playbooks soluciona una gran parte del trabajo repetitivo de un administrador de sistemas.