Lab Ansible Beginner: discover Ansible hands-on with SSH, Playbooks, Apache and MariaDB

When we talk about automation, we often refer to AnsibleHowever, it can sometimes seem complex to understand at first glance, and it is not always easy to visualize its concrete usefulness in a computer environment.

Through this first discovery labI propose that you discover Ansible through practice, by carrying out several simple and concrete actions step by step on Linux servers.

You will learn, in particular, to:

  • configure an SSH connection using keys;
  • create an Ansible inventory;
  • test the connectivity of the machines;
  • run your first playbooks;
  • automate Debian updates;
  • deploy Apache;
  • Install and configure MariaDB.

The goal of this lab is to show you that Ansible is not just for large infrastructures, but that it can quickly become a simple, powerful and very useful tool for everyday use.

Lab Instructions

Objective

Discover the basics of Ansible by administering multiple Debian servers from a control server.

Environment available

You have:

  • 1 control server Ansible was installed
  • 2 Debian clients
    • ansible-client1
    • ansible-client2

Login information

  • User: root
  • Password: formation

What you will achieve

  1. Generate an SSH key
  2. Configure customer access
  3. Install Ansible and create a YAML inventory
  4. Test the connection with ansible ping
  5. Create your first playbook
  6. Update the Debian servers
  7. Install Apache on a client
  8. Install MariaDB on another client

Advice

  • Rerun the playbooks several times to observe idempotence.
  • Take the time to read the order reviews.
  • Check the YAML syntax (indentation is important).

Resources


'; }// ── Timer ──────────────────────────────────────────────── function startTimer2(uid, expiresAt) { if (s2[uid] && s2[uid].timerInt) clearInterval(s2[uid].timerInt); var exp = new Date(expiresAt.slice(-1)==='Z' ? expiresAt : expiresAt.replace(' ','T')+'Z').getTime(); var tick = function() { var ms = Math.max(0, exp - Date.now()); var m = Math.floor(ms/60000); var ss = Math.floor((ms%60000)/1000); var txt = (ms<60000?'⚠ ':'↗ ') + m + ':' + ('0'+ss).slice(-2); lbl2(uid, txt, ms<60000?'warn':'active'); if (ms<=0) { clearInterval(s2[uid].timerInt); sessionEnd2(uid); } }; tick(); if (s2[uid]) s2[uid].timerInt = setInterval(tick, 1000); }// ── Fin session ────────────────────────────────────────── function sessionEnd2(uid) { lsDel2(uid); if (s2[uid] && s2[uid].timerInt) clearInterval(s2[uid].timerInt); delete s2[uid]; var wrap = document.querySelector('#' + uid); var lbl = wrap ? wrap.dataset.label || 'Ouvrir le terminal' : 'Ouvrir le terminal'; lbl2(uid, wrap ? wrap.querySelector('.rdp2-lbl').dataset.orig || lbl : lbl, ''); st2(uid, ''); var app = document.getElementById(uid + '-app'); if (app) app.innerHTML = ''; }// ── Stop ───────────────────────────────────────────────── function stop2(uid) { var s = s2[uid]; if (!s) return; st2(uid, 'Arrêt…'); fetch(s.conductor + '/internal/stop', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({session_id: s.sessionId}), credentials:'include' }).finally(function() { if (s.win && !s.win.closed) s.win.close(); sessionEnd2(uid); }); } window.rdp2Stop = function(uid) { stop2(uid); };// ── Liens app ──────────────────────────────────────────── function renderApp2(uid, urlApp, urlAppExtra) { var el = document.getElementById(uid + '-app'); if (!el) return; var h = ''; if (urlApp) h += '🌐 Site'; if (urlAppExtra) h += '🔧 Admin'; h += ''; el.innerHTML = h; }// ── Écrire dans la popup ───────────────────────────────── function writePopup(win, html) { try { win.document.open(); win.document.write(html); win.document.close(); } catch(e) {} }// ── Poll ───────────────────────────────────────────────── function poll2(uid) { var s = s2[uid]; if (!s || !s.win || s.win.closed) { sessionEnd2(uid); return; }fetch(s.conductor + '/internal/session/' + s.sessionId) .then(function(r) { return r.json(); }) .then(function(d) { if (d.status === 'running' && (d.url_terminal || d.url_app)) { // Rediriger la popup vers le terminal var target = d.url_terminal || d.url_app; if (!s.win.closed) s.win.location.href = target;// Boutons app + stop sous le bouton principal renderApp2(uid, d.url_app, d.url_app_extra);// Timer dans le bouton if (d.expires_at) startTimer2(uid, d.expires_at);// Sauvegarder lsSave2(uid, { sessionId: s.sessionId, conductor: s.conductor, urlTerminal: d.url_terminal, urlApp: d.url_app, urlAppExtra: d.url_app_extra, expiresAt: d.expires_at, });} else if (d.status === 'error') { if (!s.win.closed) writePopup(s.win, '' + '' + 'Erreur de démarrage — vous pouvez fermer cette fenêtre.'); st2(uid, 'Erreur'); lsDel2(uid); delete s2[uid]; } else { // Mettre à jour le message dans la popup var statusMsg = d.status === 'launching' ? 'Démarrage du conteneur…' : 'Initialisation…'; try { var el = s.win.document.getElementById('m'); if (el) el.textContent = statusMsg; } catch(e) {} st2(uid, statusMsg); setTimeout(function() { poll2(uid); }, 2000); } }) .catch(function() { setTimeout(function() { poll2(uid); }, 3000); }); }// ── Lancement ──────────────────────────────────────────── function launch2(uid) { var wrap = document.querySelector('#' + uid); if (!wrap) return;// Session active → remettre la popup au premier plan if (s2[uid] && s2[uid].sessionId) { if (s2[uid].win && !s2[uid].win.closed) { s2[uid].win.focus(); return; } // Popup fermée mais session active → réouvrir var saved = lsGet2(uid); if (saved && saved.urlTerminal) { var w2 = openPopup(uid); if (w2) { w2.location.href = saved.urlTerminal; s2[uid].win = w2; } return; } }// ── OUVRIR LA POPUP IMMÉDIATEMENT (action utilisateur directe) ── var win = openPopup(uid); if (!win) { st2(uid, 'Popups bloquées — autorisez ce site puis réessayez'); return; }// Afficher l'écran de chargement dans la popup writePopup(win, loadingHtml('Connexion au serveur…'));var conductor = wrap.dataset.conductor; s2[uid] = { win: win, sessionId: null, conductor: conductor, timerInt: null };var b = wrap.querySelector('.rdp2-btn'); if (b) { b.disabled = true; b.querySelector('.rdp2-lbl').textContent = 'Démarrage…'; }// JWT frais via admin-ajax var fd = new FormData(); fd.append('action', 'rdr_get_jwt'); fd.append('nonce', wrap.dataset.nonce || ''); fetch(window.ajaxurl || '/wp-admin/admin-ajax.php', { method:'POST', body:fd, credentials:'include' }) .then(function(r) { return r.ok ? r.json() : {}; }) .catch(function() { return {}; }) .then(function(jwtData) { if (win.closed) { delete s2[uid]; return; }var body = { stack_slug: wrap.dataset.stack }; var ttl = wrap.dataset.ttl; if (ttl && ttl !== 'null') body.ttl = parseInt(ttl); if (jwtData && jwtData.jwt) body.jwt = jwtData.jwt;// Mettre à jour le message try { var el = win.document.getElementById('m'); if(el) el.textContent = 'Lancement du stack…'; } catch(e){}fetch(conductor + '/lab/launch', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(body), credentials:'include' }) .then(function(r) { return r.json(); }) .then(function(d) { if (d.error) { writePopup(win, '' + '' + '
' + '
' + d.error + '
' + '
Vous pouvez fermer cette fenêtre
' + ''); st2(uid, d.error); delete s2[uid]; if (b) { b.disabled=false; b.querySelector('.rdp2-lbl').textContent = wrap.querySelector('.rdp2-lbl').dataset.orig || 'Ouvrir'; } return; } s2[uid].sessionId = d.session_id; st2(uid, 'Initialisation…'); poll2(uid); }) .catch(function(e) { st2(uid, 'Erreur réseau'); writePopup(win, 'Erreur réseau'); delete s2[uid]; }); }); }function openPopup(uid) { var wrap = document.querySelector('#' + uid); var w = parseInt(wrap && wrap.dataset.w) || 960; var h = parseInt(wrap && wrap.dataset.h) || 620; var left = Math.round((screen.width - w) / 2); var top = Math.round((screen.height - h) / 2); return window.open('about:blank', uid + '_w2', 'width=' + w + ',height=' + h + ',left=' + left + ',top=' + top + ',menubar=no,toolbar=no,location=no,scrollbars=yes,resizable=yes'); }// ── Reprendre après reload ─────────────────────────────── function tryRestore2(uid) { var saved = lsGet2(uid); if (!saved || !saved.expiresAt) return; var exp = new Date(saved.expiresAt.slice(-1)==='Z' ? saved.expiresAt : saved.expiresAt.replace(' ','T')+'Z').getTime(); if (Date.now() >= exp) { lsDel2(uid); return; }s2[uid] = { win: null, sessionId: saved.sessionId, conductor: saved.conductor, timerInt: null }; renderApp2(uid, saved.urlApp, saved.urlAppExtra); startTimer2(uid, saved.expiresAt); }// ── Init ───────────────────────────────────────────────── document.addEventListener('DOMContentLoaded', function() { document.querySelectorAll('.rdp2-wrap').forEach(function(el) { var lbl = el.querySelector('.rdp2-lbl'); if (lbl) lbl.dataset.orig = lbl.textContent.trim(); tryRestore2(el.id); }); });document.addEventListener('click', function(e) { var b = e.target.closest('.rdp2-btn'); if (b) { launch2(b.dataset.uid); } });})();