Ansible

Auf dieser Seite möchte ich mir einen kleinen Merkzettel zum Thema Ansible anlegen, da ich immer wieder mal wichtige Dinge dazu vergesse. Ich mach das hier in Stichpunkten und ergänze es jeden Tag mal. Dies ist keine Dokumentation sondern ein kleines Notizbuch dazu.

  • Die ansible.cfg kann in folgenden Verzeichnis gezogen werden. Im aktuellen Verzeichnis, mit der Variable ANSIBLE_CONFIG, im Homeverzeichnis (~/.ansible.cfg) oder im /etc/ansible Folder. Der einfachste Aufbau kann wie folgt ausehen im aktuellen Verzeichnis.
[defaults]
inventory = inventories/devel/inventory
private_key_file = ~/.ssh/ansible
remote_user = ansible
log_path = log/ansible.log
vault_identity_list = save@/home/jonnybravo/bin/vault-keyring-client.pl, security@/home/jonnybravo/bin/vault-keyring-client.pl
force_handlers = true
nocows = true
retry_files_enabled = true
retry_files_save_path = retry-fils
#Cachen on facts erlauben
gathering = smart
#roles_path = ./roles
fact_caching = jsonfile
fact_caching_connection = fact_cache
fact_caching_timeout = 86400
[ssh_connection]
pipelining = true
  • Retry Files kann wie folgt gestartet werden “ ansible-playbook playbooks/command.yaml --limit @error/retry-fils/command.retry“ Ein einfaches Inventory. :
[test]
debian ansible_host=xxx
ubuntu ansible_host=xxx

[test:vars]
ansible_python_interpreter=/usr/bin/python3
ansible_ssh_common_args='-o StrictHostKeyChecking=no'

[debian_hosts]
debian

[debian_hosts:vars]
ansible_user=ansible
ansible_become=true
ansible_become_pass=ansible

[ubuntu_hosts]
ubuntu

[ubuntu_hosts:vars]
ansible_user=ansible
ansible_become=true
  • Ansible bietet eine Menge von Adoc Kommandos Hier eine kleine Liste von möglichen einsätzen -m ist das module
Befehl Beschreibung
ansible all -a „ls lisa /home“Dieses ist ein einfacher Shell aufruf auf allen Zielsystemen
ansible all -m pingIst ein Ping mit der Namensauflösung aus dem Inventory
ansible all -m command -a „df -h“Das Module Command um eine Komandozeile auszuführen. Das ist das selbe wie -a
  • Ein simples Beispiel für einen Command Aufruf über einen Task. Register speichert denn Aufruf in eine variable und Output stdout_lines oder stdout geben dann den output aus.
---
- hosts: all
  gather_facts: true
  tasks:
    - name: Platzbedarf
      command: df -h /
      changed_when: false
      register: df_cmd
    - debug:
        msg: '{{df_cmd.stdout_lines}}'
  • Jeden Task kann man Tags geben, das hat den vorteil das man nicht immer alles aufrufen muss. Hier ein einfaches Beispiel mit dem entsprechenden Aufruf.
---
- hosts: all
  gather_facts: true
  tasks:
    - name: yes
      debug:
        msg: "yes"
      tags:
        - "yes"

    - name: no
      debug:
        msg: "no"
      tags:
        - "no"
        - "nein"
ansible-playbook playbooks/tag-beispiel.yml -t "nein"
  • Mit dem Command ansibe-playbook werden die Plays ausgeführt. Es gibt drei wichtige Optionen die ich immer wieder vergesse
start-at-taskansible-playbock <Play.yml –start-at-task „Name des Tasks *“führt das Playbook ab einem Task aus
check und diffansible-playbook <Play-yml> –check –diffcheckt und zeigt die änderung an die bei einem Play ausgeführt wird.
vard ansible-playbook <Play.yaml> -e var=wertÜbergeben eine Variable
  • Ab der Version 2.2 ist es möglich bei den handlers listen zu definieren.
  • Es gibt auch vars prompt abfrage einer varibale
---
- hosts: localhost
  gather_facts: false
  vars_prompt:
    - name: "name"
      prompt: "Wie ist dein Name"
      default: "Meister Lampe"
      private: false
    - name: "passwort"
      prompt: "dein neues Passwort"
      private: true
  tasks:
    - debug:
        msg: "Deine Name ist {{name}} und dein Passwort {{passwort}}"
ansible-playbook playbooks/vars_prompt.yml -e name=Daniel -e passwort=Start1234!
  • Mit dem task assert kann man prüfen ob alle variablen im Play vorhanden sind
hosts: debian
gather_facts: no tasks:

    assert:
    that:
        hostname is defined and hostname != ''
        fail_msg: Die Variable hostname ist nicht vorhanden oder leer
    name: Hostname setzen
    hostname:
    name: "{{ hostname }}"
  • Es gibt varibalen die immer da sind unabhängig vom fact.
inventory_hostnameder Name des Hosts im Inventory
inventory_hostname_shortder Name ohne FQDN
groupsInventory Gruppe
hostvarsHostvariablen aller Hosts (inkl. facts)
playbook_dirVerzeichnis in dem das aktuelle Playbook liegt
inventory_dir,inventory_fileVerzeichnis bzw. File vom Inventory
role_path,role_nameDatei bzw. Name der aktuellen Rolle
ansible_versionVersion von ansible
  • Man kann eigne facts anlegen wenn man auf dem Zielhost in dem Verzeichnis /etc/ansible/facts.d/ Datein mit der endung .facts anlegt im json Format. Ausführungsrechte müssen vorhanden sein chmod -x <datei.fact>
{
"Name" : "Mein fact"
}
ansible debian -m setup -a 'filter=ansible_local'
{{ansible_local.preferences.MeineName}}
  • when kann ein größer kleiner und gleich. Der Aufruf kann ie folgt aussehen. ansible_distribution_version is version(‚19.10‘, ‚>=‘) Hier noch ein Beispiel.
  vars:
    apache:
      Debian:
        package_name: apache2
        service_name: apache2
      RedHat:
        package_name: httpd
        service_name: httpd
  tasks:
    - name: Apache installieren
      package:
        name: "{{apache[ansible_os_family].package_name}}"

    - name: Dienst starten und in Bootprozess integrieren
      service:
        name: "{{apache[ansible_os_family].service_name}}"
        state: started
        enabled: yes

oder per include

  tasks:
    - include_vars: apache_{{ansible_os_family}}.yml

    - name: Apache installieren
      package:
        name: "{{apache_package_name}}"

    - name: Dienst starten und in Bootprozess integrieren
      service:
        name: "{{apache_service_name}}"
        state: started
        enabled: yes
  • Ansible kann schleifen in mehreren Variationen. Hier ein paar Beispiele.
  vars:
    Comic:
      - Spiderman
      - Hulk
      - IronMan
  tasks:
    - name: Schleife über Liste
      debug: msg="Marvel comic {{ item }}!"
      with_items: "{{Comic}}"
  vars:
    tiere:
      - [Spiderman, Hulk, IronMan]
      - [SuperSchlaubi, Dumbo]

  tasks:
    - name: Schleife über verschachtelte Liste
      debug: msg="Marvel comic {{ item }}!"
      with_items: "{{tiere}}"
  vars:
    tiere:
      Hund:  Wuff
      Katze: Miau
      Maus:  Fiep

  tasks:
    - name: Schleife über Map
      debug: msg="{{ item.key }} macht {{ item.value }}."
      with_dict: "{{tiere}}"
  tasks:
    - name: Schleife über eine Folge
      debug: msg="{{item}}"
      with_sequence: start=6 end=12 stride=2 format=Ich_bin_die_Zahl_%02d
  tasks:
    - name: Würfeln
      shell: "echo $(( $RANDOM % 6 + 1))"
      register: wurf
      args:
        executable: /bin/bash
      until: "'6' in wurf.stdout"
      retries: 10
      delay: 1

    - debug: msg="Eine 6 wurde gewürfelt!"
  vars:
    users:
      - name: testuser
        home: /home/testuser
        pass: Test
      - name: schlaubi
        shell: /bin/bash
        pass: supergeheim

  tasks:
    - name: Benutzer anlegen
      user:
        name:     '{{item.name}}'
        shell:    '{{item.shell | default("/bin/bash")}}'
        home:     '{{item.home  | default(omit)}}'
        password: '{{item.pass  | password_hash("sha512") }}'
        update_password: on_create
      with_items: '{{users}}'
#loop_control mit label zeigt im output nur noch den item.name an nicht das passwort
      loop_control:
        label: 'item.name'
  • Zur Fehlerbeandlung kann mit dem failed_when erfolgen oder wird einfach mit ignore_errors:true umgangen.
  • Seit ansible 2.0 kann man auch blocks erstellen. Diese können dann auch einen gewünschten Resuce ausführen bei einem Fehler. Beispiel
  tasks:
    - block:
      - name: Verzeichnis anlegen
        file:
          path: /tmp/download
          state: directory

      - name: Dateien von benachbartem Server herunterladen
        get_url:
          url: http://192.168.150.20/{{item}}
          dest: /tmp/download/{{item}}
        with_items: [index.html, test1.txt]

      rescue:
      - debug: msg="Beim Herunterladen ging was schief"

      - name: Aufräumen
        file:
          path: /tmp/download
          state: absent
      when:
        - ansible_os_family == "Debian"
  • Für ein Asynchrone ausführung gibt es die Parameter asnyc und poll. async ist die maximale Laufzeit und poll gibt den abfrage Intervall an.
  • local_action führt den entsprechenden Task auf dem host aus und mit delegate_to: xxx kann ein Task delikiert werden (auch localhost).
  • Manchmal ist es nötige eine Umgebungsvaribale für den Zielhost zu definieren. Das funktioniert dann mit dem Parameter „environment“.
  • Mit dem Kommando ansible-inventory lassen sich Informationen aus dem Inventory gewinnen. Beispiele :
ansible-inventory –graph Zeigt das aktuelle inventory grafisch
ansible-inventory –list –yamlZeigt das inventory in yaml
  • Wenn man hosts hat und diese noch nicht im inventory sind können diese on-the-fly hinzugefügt werden. In dem Beispiel heißt die inventory gruppe temp_groups
- hosts: localhost
  tasks:
    - name: Hosts bekanntmachen
      add_host:
        hostname: "{{item.ip}}"
        groups: temp_group
        ansible_become:        yes
        ansible_become_method: "{{item.become_method |default(omit)}}"
        ansible_become_pass:   "ansible"
        ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
      with_items:
        - ip: 192.168.150.10
        - ip: 192.168.150.20
          become_method: su

- hosts: temp_group
  tasks:
    - debug: msg="Hallo {{inventory_hostname}}"
    - command: head -1 /etc/shadow
  • Des weiteren gibt es dynamische Gruppen im Inventory. Dieser werde mit group_by angelegt. Beispiel.
- name: Gruppieren nach Systemfamilien
  hosts: all 
  tasks:
    - group_by: key=os_{{ ansible_os_family }}
- name: Alle aus der Debian-Familie abarbeiten
  hosts: os_Debian
  tasks:
    - debug: msg="Hallo {{ inventory_hostname }}"
- name: Und nun der Rest
  hosts: all:!os_Debian
  tasks:
    - debug: msg="Hallo {{ inventory_hostname }
  • die Struktur von Rollen beinhaltet folgende Unterverzeichnise. Mit dem Commando ansibe-galaxy init <Rolle> werden die Verzeichnise automatisch angelegt.
<Role>/tasks/main.ymlDie Tasks der Rolle
<Role>/files/*Datein zum Hochladen
<Role>/templatesJinia-Templates
<Role>/handlers/mainHandlers für die Rolle
<Role>/vars/main.ymlVariablen mit hoher Priorität
<Role>/defaults/main.ymlVariablen mit schwacher Priorität
<Role>/meta/main.yamlMetainformation z.B. Anhängigkeiten von anderen Rollen
  • Eine weitere möglochkeit für Modularisierung sind imports „import_tasks:“ und includes „include_tasks:“. Imports sind statisch und includes dynamisch. Beispiel Dynamisches laden von variablen.
- name: Include OS-specific setings
  include_vars: 
  with_first:found:
    - {{ansible_os_family}}.yml
    - default.yml  
  • Wichtige Module
  • mit ansible-vault können Daten verschlüsselt werden mit einer vault-id oder das ganze per vault-file
ansible-vault encrypt --vault-id security@prompt inventories/devel/host_vars/debian.yml
entschlüsseln
ansible-vault decrypt --vault-id security@prompt inventories/devel/host_vars/debian.yml
editieren
ansible-vault edit --vault-id film@prompt inventories/devel/host_vars/debian.ym
packagegemeinsamer Nenner von allen package Modulen (apt,yum,usw)
firewalldFirewallsverwalten
metaBeendet ein Play
paclage_factsPaketinformationen
copyum Daten zu kopieren
tempate für Jinia templates
fileDatein verwalten
lineinfileZeilen in Text verwalten
blockinfileTextpassagen in Dateien verwalten
stat Infomationen über Datein
replacesuchen und ersetzen
unarchivehochladen und auspacken
service, service_factsService Starte/Stoppen, Service Information
croncronjobs verwalten
hostnameHostname ändern
user,groupBenutzer und Gruppenverwaltung
rebootReboot ausführen
get_urlDownload of Ressourcen
uriKommunikation mit ebservices
gitGit
wait_for, wait_for_connectionauf Ereigniss warten, warten bis der host wieder erreichbar ist
assertsicherstellen das gewisse Bedingungen erfüllt sind
set_factvariablen in der Laufzeit setzen
  • hinter der vault-id kann folgendes stecken. <ID>@prompt Passwort interaktiv einlesen <ID>@DATEI Passwort von Datei einlesen und <ID>@PROGRAM Passwort aus Progamm einlesen. Hier nun ein Beispiel von Herrn Miesen mit gpg und pass. In der variable ANSIBLE_VAULT_IDENTITY_LIST sucht ansible nach dem Password.
$gpg --gen-key # UserID ist zum identifizierne
$pacman -S pass
$ pass init "<Real name von gpg>"
$ pass insert ansible/vault/security
$ ansible-playbook deployments/apache-site.yml --vault-id=security@vault-keyring-client.pl
$ export ANSIBLE_VAULT_IDENTITY_LIST=security@vault-keyring-client.pl
#security@vault-keyring-client.pl
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;

my $pass_prefix = "ansible/vault";

my $id;
GetOptions("vault-id=s" => \$id);

exec "pass" , "$pass_prefix/$id";
                             
  • mit dem Aufruf -f können die FORKS die definiert werden. Forks sind die laufenden Prozesse (default=5) in großen umgebungen und mit einem schnell Linux kann man schon 20 wählen oder mehr 🙂
  • Es gibt drei Strategien für das Orchstrieren, die mit der Variable strategy gesetzt werden.
linearDefault wert für das nach und nach abarbeiten
freeein Host kann sofort zum nächsten Task übergehen
host_pinnedähnlich wie free (bei niedriegen fork wir die anzahl nicht überschritten)
  • Ansible kann auch gut mit Docker. Hier die wichtigsten Module :
docker_host_infoHost informationen abfragen.
docker_containerContainer verwalten
docker_container_infoContainer information
docker_networkbenutzer definierte verwaltung
docker_network_infoNetzwerkinformationen abfragen
docker_volumesverwalten von docker volumes
docker_volume_infoInfos der Volumes
docker_loginregistry Login
docker_pruneaufräumen
docker_imagesImages verwalten
docker_compose Schnittstelle zu docker-compose
  • Ein random Password kann mit dem module password erstellt werden. Hier ein einfaches Beispiel um User anzulegen. Auf den host werden diese dann unter /tmp/credentials gespeichert.
- hosts: debian
  vars:
    users:
      - name: jonnybravo
        fullname: Jonny Bravo
      - name: mustermann
        fullname: Max Mustermann
      - name: winzig
        fullname: Willi Winzig
  tasks:
    - name: Benutzer anlegen
      user:
        name: "{{item.name}}"
        shell: /bin/bash
        password: "{{ lookup('password', 
                  '/tmp/credentials/pass_' 
                  + item.name 
                  + '.txt length=50 encrypt=sha512_crypt') }}"
      with_items: "{{users}}"

Den Output kann mit entweder mit der Variable ANSIBLE_STDOUT_CALLBACK oder in der ansible.cfg defnieren. Es gibt folgende Plugins die bereits im ansible vorhanden sind (2.9).

default Die normale Ausgabe
nullkeine Bildschirmausgabe
yaml Ausgabe im YAML Format
jsonAusgabe in json
unixyAusgabe im UNIX Stil
densesehr kompakte Ausgabe
minimalAusgabe wie beim ansible KOmmando
onelineAusgabe wie bei ansible-o
debugMetainformation und reine Ausgabe
selectiveGibt nur spezielle Tasks aus
counter_enabledAusgabe mit Zählern

Ab der Version 2.9 wurden collections hinzugefügt. Collections sind eigne oder modulansammlungen von anderen Entwicklern. Installiert werden diese über den galaxy befehl.

ansible-galaxy collection install community.window

Diese Beispiel installiert die community.windows collection die auf der Ansible Website :

https://docs.ansible.com/ansible/latest/collections/community/windows/index.html

Liste alle Collections:

https://docs.ansible.com/ansible/latest/collections/index.html#list-of-collections

Ab hier werde ich Codeschnipsel zu Verfügung stellen. Für weitere Anregung und auch für mich selbst als Spickzettel : )

Dieses Beispiel fragt ab, ob ein User existiert mit dem Module getent. Diese Module ist sehr mächtig, da man da alles auf dem Linuxsystem abfragen kann.

https://docs.ansible.com/ansible/latest/collections/ansible/builtin/getent_module.html

- name: Test
  hosts: localhost
  gather_facts: true
  vars:
    testuser: "username"
  tasks:
    - name: Check if user exists
      getent:
        database: passwd
        key: "{{testuser}}"
        fail_key: False
    - debug:
        var: getent_passwd.{{testuser}}
    - debug:
        msg: "Der User existiert nicht"
      when: getent_passwd[testuser] == none
~                                             

Schreibe einen Kommentar