Skip to main content

WORKSHOP 2: Ad Hoc Commands

In deze workshop gaan we Ansible op de meest eenvoudige manier gebruiken via Ad-Hoc commands. Dit is zeker niet de meest efficiënte manier maar geeft je inzicht in hoe Ansible taken uitvoert op Remote Hosts zonder een playbook (zie later) te gebruiken.

1. Doelstelling

In deze workshop leer je:

  • Het Ansible configuratie-bestand (ansible.cfg) begrijpen
  • Een inventory aanmaken en begrijpen in INI- en YAML stijl
  • Ad-Hoc commands uitvoeren met Ansible

2. Workshop

Stap 1 - Werken met een "Inventory"

Om Ansible commands te kunnen gebruiken is er nood aan een inventory. Deze inventory bepaalt welke hosts (desktops, servers, firewalls, routers,...) beheerd kunnen worden door de Ansible Control Host. Dergelijke inventory kan opgegeven worden in INI- of YAML-stijl en bevat naast de hosts eventueel ook groepen waartoe deze hosts behoren en mogelijks ook variabelen die we later kunnen nodig hebben. (Meer info)

Voorbeeld inventory in INI-stijl
dummyhost ansible_host=1.2.3.4

[all:vars]
ansible_port=22

[web]
node1 ansible_host=X.X.X.X
node2 ansible_host=Y.Y.Y.Y
node3 ansible_host=Z.Z.Z.Z

[web:vars]
ansible_user=ubuntu
ansible_password=PASSWORD
ansible_connection=ssh

[control]
ansible-1 ansible_host=A.A.A.A

[control:vars]
ansible_user=student
ansible_password=PASSWORD
ansible_connection=local

Hosts kunnen allemaal gezamelijk op 1 niveau (vb dummyhost) of onderverdeeld worden in groepen volgens bv functie (webservers, container-nodes), geografie (Gent, Brussel,...) of andere verdelingen (vb "web" en "control" in het fragment hierboven). Op elk van deze niveaus kunnen dan ook variabelen meegegeven worden (vb all:vars of web:vars). Later zien we nog andere mogelijkheden om variabelen te definiëren.

  • Bekijk bovenstaande inventory goed en probeer er de onderverdeling op het hoogste niveau én de onderliggende groepen met hun bijhorende variabelen te begrijpen.
Voorbeeld inventory in Yaml-stijl
--- 
all:
hosts:
dummyhost:
ansible_host: 1.2.3.4
children:
web:
hosts:
node1:
ansible_host: X.X.X.X
node2:
ansible_host: Y.Y.Y.Y
node3:
ansible_host: Z.Z.Z.Z
vars:
ansible_user: ubuntu
ansible_password: PASSWORD
ansible_connection: ssh
control:
hosts:
ansible-1:
ansible_host: A.A.A.A
vars:
ansible_user: student
ansible_password: PASSWORD
ansible_connection: local
vars:
ansible_port: 22
caution

YAML-files starten steeds met 3 streepjes => "---". De hiërarchie in YAML wordt bepaald door de insprong. Insprongen mogen niet met "tabs" gemaakt worden maar moeten met spaties!!

Bij YAML-inventories moet er altijd gebruik gemaakt worden van de keywords all:, hosts: en eventueel ook van children: en vars:

  • Bekijk bovenstaande YAML-inventory goed en probeer net zoals in het vorig voorbeeld de onderverdeling op het hoogste niveau én de onderliggende groepen met hun bijhorende variabelen te begrijpen.
note

Hosts kunnen in meerdere groepen voorkomen.

note

Variabelen kunnen, zoals je in de voorbeelden ziet, op verschillende niveaus gedefinieerd worden. Het is immers mogelijk om op het hoogste niveau of per groep of zelfs per host variabelen (of parameters) mee te geven.

Dergelijke inventory (de ini- of yaml-file) kan op verschillende plaatsen opgeslagen worden. Bij installatie van Ansible werd een default inventory met de naam hosts voorzien in /etc/ansible/hosts. Als je, bij het uitvoeren van het Ansible-commando, geen inventory opgeeft, dan wordt er teruggevallen op dit exemplaar. Idealiter maak je dus een inventory aan in de locatie van je actieve Ansible-project. Inventories kunnen ook dynamisch opgebouwd worden, bijvoorbeeld met real-time info uit een databank, maar dit valt buiten de scope van deze workshops.

tip

Ik deze workshop wordt steeds verwezen voor alle commando's t.o.v. je homefolder [student@ControlHost ~]$ in de Control Host. Als je alles netjes wil bijhouden in je git-repo, dan voer je uiteraard alles uit t.o.v. die folder.

  • Maak op je Ansible Control Host een nieuwe map aan onder je home-folder met de naam ansible-workshop.

    [student@ControlHost ~]$ mkdir ansible-workshop
    [student@ControlHost ~]$ cd ansible-workshop
    [student@ControlHost ansible-workshop]$
  • Maak in deze map een inventory-bestand aan voor zowel Yaml (hostsyaml) als Ini (hostsini). Dit kan je doen met een teksteditor zoals "vim" of "nano". Je kan de inhoud voor deze respectievelijke inventory-bestanden kopiëren van hierboven of via hostsini en hostsyml. Je past de ip-adressen van je Ansible Control Host en de Nodes aan met je eigen ip-adressen. Ook de wachtwoorden vul je correct aan voor beide type hosts De dummyhost mag je negeren (laten staan).

[student@ControlHost ansible-workshop]$ nano hostsyaml
tip

Je kan eventueel ook gebruik maken van een geavanceerdere editor zoals Visual Studio Code. Via een Remote Window (Remote-SSH plugin) kan je dan rechtstreeks de bestanden in je Ansible Control Host bewerken. Het grote voordeel van dergelijke editor is dat deze met verschillende extensies kan uitgebreid worden die ondersteuning geven voor het schrijven van Ansible playbooks, YAML-bestanden (via linting), etc...

Je kan je inventory-bestand gaan bevragen en controleren via het command ansible en parameter --list-hosts.

tip

Als we in onderstaand command de inventory niet definiëren met -i, dan wordt er teruggevallen op de default inventory hosts in de map. /etc/ansible. Deze inventory is uiteraard leeg.

  • Probeer onderstaande commands uit met zowel je INI- als je YAML-inventory
[student@ControlHost ansible-workshop]$ ansible -i hostsyaml web --list-hosts
[student@ControlHost ansible-workshop]$ ansible -i hostsyaml web,control --list-hosts
[student@ControlHost ansible-workshop]$ ansible -i hostsyaml 'node*' --list-hosts
[student@ControlHost ansible-workshop]$ ansible -i hostsyaml all --list-hosts
[student@ControlHost ansible-workshop]$ ansible -i hostsyaml ungrouped --list-hosts
[student@ControlHost ansible-workshop]$ ansible -i hostsyaml web,control --list-hosts
hosts (4):
node1
node2
node3
ansible-1
[student@ControlHost ansible-workshop]$

Stap 2 - Ad Hoc Commands

Met een inventory die klaar staat kunnen we nu de eerste Ansible-opdrachten uitvoeren. Dergelijke Ansible-opdrachten worden uitgevoerd via modules (later volgt meer info). Een zeer eenvoudige module is de ping-module.

caution

Ansible maakt voor het uitvoeren van zijn opdrachten telkens een connectie (vb via SSH) om daar een script uit te voeren (meestal python-script). Dat script voert dan de gewenste taak uit. Een "ping" in de beschreven module werkt dus niet zoals een klassieke "netwerk-ping" maar voert gewoon een klein scriptje uit op de doelhost. Dat is voldoende om te controleren of de doelhost "bereikbaar" is (resultaat van de ping).

Het Ansible-command kan via de parameter -m weten welke module het moet uitvoeren. Opties (parameters) die je wil meegeven met een module doe je via -a (zie verder).

  • Voer onderstaande ad-hoc command uit.
[student@ControlHost ansible-workshop]$ ansible -i hostsyaml web -m ping

node2 | *SUCCESS* => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
// highlight-next-line
"ping": "pong"
}
node3 | *SUCCESS* => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
// highlight-next-line
"ping": "pong"
}
node1 | *SUCCESS* => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
// highlight-next-line
"ping": "pong"
}

[student@ControlHost ansible-workshop]$
caution

Krijg je een foutmelding omdat host key checking actief is, dan wordt dat veroorzaakt omdat je aan Ansible vraagt om een ssh-connectie te leggen naar een onvertrouwde host zonder dat je de key gevalideerd hebt.

Je kan dat oplossen door manueel een ssh-connectie te leggen naar de respectievelijke host, of door een config-bestand aan te maken (zie verder), maar daarin volgende setting: host_key_checking=False.
In het eerste geval zal een fingerprint toegevoegd worden aan je known_hosts in de folder .ssh . In het tweede geval zal de ontvangen key helemaal niet gecontroleerd worden.

Wij kiezen er hier voor om te werken met de setting host_key_checking=False in het ansible.cfg bestand (zie Stap 3).

Stap 3 - Ansible Configuration File

Het standaard gedrag van Ansible kan aangepast worden in een Ansible Configuration bestand. Dit ansible configuratie-bestand kan dan specifieke instellingen (bv. connectie-settings, locatie van de inventory,...) bevatten die je niet meer hoeft te definiëren in je inventory of playbook. Er is bij de installatie van ansible op het systeem een default configuratie-bestand ansible.cfg aangemaakt in de map /etc/ansible. Dit is by default echter helemaal leeg.

note

Het is best practice om een .ansible.cfg bestand aan te maken in de map waar je de Ansible commands (ad-hoc, playbooks, roles,..) zal uitvoeren met je specifieke instellingen voor dat project (tenzij de default-waarden voldoende zijn uiteraard)

[student@ControlHost ansible-workshop]$ ansible-config init --disabled > ansible.cfg
[student@ControlHost ansible-workshop]$ cat ansible.cfg

[defaults]
# (boolean) By default Ansible will issue a warning when received from a task action (module or action plugin)
# These warnings can be silenced by adjusting this setting to False.
;action_warnings=True

# (list) Accept list of cowsay templates that are 'safe' to use, set to empty list if you want to enable all installed templates.
;cowsay_enabled_stencils=bud-frogs, bunny, cheese, daemon, default, dragon, elephant-in-snake, elephant, eyes, hellokitty, kitty, luke-koala, meow, milk, moofasa, moose, ren, sheep, small, stegosaurus, stimpy, supermilker, three-eyes, turkey, turtle, tux, udder, vader-koala, vader, www

# (string) Specify a custom cowsay path or swap in your cowsay implementation of choice
;cowpath=

# (string) This allows you to chose a specific cowsay stencil for the banners or use 'random' to cycle through them.
;cow_selection=default

# (boolean) This option forces color mode even when running without a TTY or the "nocolor" setting is True.
;force_color=False
...

Stap 4 - Modules en Help

In de default installatie van Ansible werden onmiddellijk enkele basis-modules voorzien. Maar er kunnen nog steeds achteraf extra modules/plugins toegevoegd worden. Er komen immers steeds producten bij en bepaalde vendors voorzien eigen plugins. Je zou zo ook in principe je eigen plugin kunnen schrijven.

  • Om alle onmiddellijk bruikbare modules te zien gebruik je volgend command:
[student@ControlHost ansible-workshop]$ ansible-doc -l
tip

In de online documentatie van Ansible kan je een beperkt deel van de modules ook terugvinden met bijhorende uitleg over gebruik, parameters, etc... zie Ansible documentation en belangrijker (de Ansible specifieke en reeds ingebouwde modules).

  • Bekijk eens hoe je de module user (of het equivalente ansible.builtin.user) kan gebruiken (opties, parameters, voorbeelden...) Parameters voorafgegaan door een "=" zijn verplicht.
[student@ControlHost ansible-workshop]$ ansible-doc ansible.builtin.user

Stap 5 - de module command

Een van de modules waarmee we "klassieke" Linux-commando's via Ansible kunnen uitvoeren is de command-module. Dit voert simpelweg het opgegeven (bash-)commando uit op de remote host.

  • Voer onderstaande commands eens uit t.o.v. één of meerdere hosts. Op deze manier kan je makkelijk custom commands uitvoeren op je hosts die je misschien niet kan uitvoeren met een andere module.
ip-adres info opvragen
[student@ControlHost ansible-workshop]$ ansible -i hostsyaml node1 -m command -a "ip a"
kernel-versie opvragen
[student@ControlHost ansible-workshop]$ ansible -i hostsyaml web,control -m command -a "uname -r"

Stap 6 - de module copy en bijhorende "permissies"

In deze stap is het de bedoeling om met de module copy de inhoud van de motd (in /etc/motd) aanpassen. motd is de "message of the day" wat elke gebruiker te zien krijgt bij een Linux-host bij het aanmelden.

  • Bekijk het gebruik (de parameters) van deze module in de online documentatie.

  • Voer volgend ad hoc commando uit. Verwacht je wel aan een fout...

[student@ControlHost ansible-workshop]$ ansible -i hostsyaml node1 -m copy -a 'content="Host managed by Ansible@ikdoeict\n" dest=/etc/motd'

De output van bovenstaand commando geeft duidelijk aan dat er een fout is opgetreden. Waarom? De gebruiker waarmee je dit commando remote uitvoert (in dit geval de user "ubuntu") heeft niet de rechten om te schrijven naar de motd-file. Daarvoor moet je verhoogde privileges hebben (sudo-rechten).

// highlight-next-line
node1 | *FAILED*! => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"checksum": "f23c13d7b4d7ed4b057bb88bea89d7038bac9e4e",
// highlight-next-line
"msg": "Destination /etc not writable"
}

Manueel zou je in Linux nu het command laten voorafgaan door sudo. Met Ansible doe je dit door de optie -b of --become mee te geven. Daarnaast moet je het wachtwoord meegeven om die sudo-rechten te verkrijgen met --ask-become-pass (zie documentatie).

  • Voer volgend aangepast command uit:
[student@ControlHost ansible-workshop]$ ansible -i hostsyaml node1 -m copy -a 'content="Host managed by Ansible@ikdoeict\n" dest=/etc/motd' --become --ask-become-pass

BECOME password:
// highlight-next-line
node1 | *CHANGED* => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
// highlight-next-line
"changed": true,
"checksum": "4458b979ede3c332f8f2128385df4ba305e58c27",
"dest": "/etc/motd",
"gid": 0,
"group": "root",
"md5sum": "65a4290ee5559756ad04e558b0e0c4e3",
"mode": "0644",
"owner": "root",
"size": 19,
"src": "/home/ubuntu/.ansible/tmp/ansible-tmp-1617217187.197986-8098-265930493570302/source",
"state": "file",
"uid": 0
}

Om te vermijden dat je telkens het sudo-wachtwoord moet opgeven zou je ook kunnen overwegen om dit mee te geven met de inventory.

--- 
all:
hosts:
dummyhost:
ansible_host: 1.2.3.4
children:
web:
hosts:
node1:
ansible_host: X.X.X.X
node2:
ansible_host: Y.Y.Y.Y
node3:
ansible_host: Z.Z.Z.Z
vars:
ansible_user: ubuntu
ansible_password: PASSWORD
ansible_connection: ssh
ansible_become_pass: Azerty123
control:
hosts:
ansible-1:
ansible_host: A.A.A.A
vars:
ansible_user: student
ansible_password: PASSWORD
ansible_connection: local
ansible_become_pass: student
vars:
ansible_port: 22
note

Uiteraard is het geen goed idee om wachtwoorden in clear-text op te nemen in je inventory of playbook. Later zal je zien hoe we dit kunnen veilig oplossen met een ansible Vault.

  • Geef het vorige command nogmaals maar nu zonder --ask-become-pass en gebruik makend van de aangepaste inventory.
[student@ControlHost ansible-workshop]$ ansible -i hostsyaml node1 -m copy -a 'content="Host managed by Ansible@ikdoeict\n" dest=/etc/motd' --become

// highlight-next-line
node1 | *SUCCESS* => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
// highlight-next-line
"changed": false,
"checksum": "4458b979ede3c332f8f2128385df4ba305e58c27",
"dest": "/etc/motd",
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"path": "/etc/motd",
"size": 19,
"state": "file",
"uid": 0
}
note

Op eenzelfde manier is het ook mogelijk om de --become parameter te definiëren in de inventory (of in een ansible.cfg bestand)

  • Controleer met de generieke command module de inhoud van het ´motd`-bestand.
[student@ControlHost ansible-workshop]$ ansible -i hostsyaml node1 -m command -a "cat /etc/motd"

Modules: oefening

  • Ga met het commando ansible-doc aan de slag om:

    • een module te zoeken die gebruik kan maken van de package manager apt
    • te leren hoe je pakketten (software) kan installeren via deze module
  • Voer een ad-hoc commando uit die de laatste versie van squid installeert op node1

[student@ControlHost ansible-workshop]$ ansible-doc ....
[student@ControlHost ansible-workshop]$ ansible-doc ....
[student@ControlHost ansible-workshop]$ ansible -i ....
Spoiler Alert:

Oplossing hieronder....

[student@ControlHost ansible-workshop]$ ansible-doc -l | grep apt
[student@ControlHost ansible-workshop]$ ansible-doc apt
[student@ControlHost ansible-workshop]$ ansible node1 -i hostsyaml -m apt -a "name=squid state=latest" --become

Externe modules

Zoals eerder aangegeven zit sinds versie 2.10 niet langer alles vervat in een enkele installatie. Op de Ansible Galaxy kan je terecht voor externe modules die aanvullend kunnen geïnstalleerd worden.

Het is daarbij natuurlijk wel opletten voor compatibiliteit met je Ansible-versie.

  • Installeer de aws community collectie waarmee je op AWS cloud-componenten kan beheren: (dit kan eventjes duren...)
[student@control-host ansible-workshop]$ ansible-galaxy collection install community.aws

Deze collecties zijn echter niet beperkt tot wat op Ansible Galaxy te vinden is, deze kunnen bijvoorbeeld ook rechtstreeks van GitHub geïnstalleerd worden. Net zoals bij elke package manager is het uiteraard wel belangrijk om de kwaliteit en betrouwbaarheid van de bron na te gaan, zodat je geen ongewenste files installeert.

Nodige modules standaardiseren met een requirements file

Uiteraard proberen we er steeds voor te zorgen dat wat we maken herbruikbaar is op een later moment of door andere ontwikkelaars. Daar zullen we in de workshop steeds op letten. Bij het gebruik van externe collecties, kan best een file requirements.yml aangemaakt worden met een overzicht van de benodigde collecties.

---
collections:
- name: community.aws
version: 5.4.0

Die file kan je dan later gebruiken om de installatie terug identiek te maken:

ansible-galaxy collection install -r collections/requirements.yml

(C) Deze workshop werd gebaseerd op de informatie van Red Hat Ansible Automation Platform