Après avoir automatisé le provisioning d’une VM Ubuntu 24.04 sur HVM, l’étape suivante consiste à automatiser les opérations courantes après le déploiement. HPE Morpheus Enterprise permet de le faire proprement avec les Operational Workflows, les Tasks, les Script Templates et les Inputs.
Dans cet article, je mets en place trois cas d’usage très concrets sur une instance Linux déjà provisionnée :
- créer un compte administrateur local ;
- réinitialiser le mot de passe d’un compte existant ;
- lancer un diagnostic Linux synthétique directement depuis Morpheus.
L’objectif n’est pas seulement d’exécuter quelques commandes Bash. L’intérêt est de transformer des opérations d’administration en actions réutilisables, paramétrables et accessibles depuis l’interface Morpheus sans ouvrir une session SSH manuelle sur la machine.
Architecture de l’automatisation
Pour ces trois cas d’usage, j’utilise toujours la même chaîne :
Inputs ↓ Operational Workflow ↓ Library Script Task ↓ Script Template Bash ↓ Instance Linux cible
Les menus utilisés sont les suivants :
- Library → Options → Inputs : création des paramètres saisis par l’opérateur ;
- Library → Templates → Script Templates : stockage des scripts Bash réutilisables ;
- Library → Automation → Tasks : création d’une Task de type Library Script ;
- Library → Automation → Workflows : assemblage de la Task et des Inputs dans un Operational Workflow ;
- Instance → Actions → Run Workflow : exécution sur une VM déjà déployée.
HPE documente bien ce modèle : un Script Template peut être utilisé dans une Library Script Task, et un Operational Workflow peut être exécuté sur une instance existante.
Point important : les Inputs sont sensibles à la casse
C’est le détail qui m’a fait perdre le plus de temps pendant les tests.
Dans Morpheus, la valeur utilisée dans le script doit correspondre exactement au Field Name de l’Input. La casse compte.
Par exemple, si l’Input possède :
Field Name : LocalUsername
le Script Template doit utiliser :
USERNAME="<%= customOptions.LocalUsername %>"
et non :
USERNAME="<%= customOptions.localUsername %>"
Avec une casse incorrecte, la variable est résolue à null. Dans mon premier test, le workflow a donc réellement créé un utilisateur nommé null. Depuis, tous mes scripts commencent par vérifier explicitement que les variables reçues ne sont ni vides ni égales à null.
Cas d’usage 1 — Créer un administrateur Linux local
Créer les Inputs
Dans Library → Options → Inputs, je crée deux Inputs :
Name : Local Username Field Name : LocalUsername Type : Text Required : Yes Name : Local Password Field Name : LocalPassword Type : Password Required : Yes
Le type Password permet de masquer la saisie dans le formulaire Morpheus. Le script ne doit évidemment jamais afficher cette valeur dans les logs.
Créer le Script Template
Dans Library → Templates → Script Templates → + Add :
Name : Create Linux Local Admin Script Type : Bash Phase : Reconfigure Sudo : Yes
Le script est volontairement idempotent sur la création de l’utilisateur : si le compte existe déjà, il n’est pas recréé. Le mot de passe est en revanche réappliqué.
#!/bin/bash
set -euo pipefail
USERNAME="<%= customOptions.LocalUsername %>"
PASSWORD="<%= customOptions.LocalPassword %>"
echo "=== Configuration du compte local ==="
if [[ -z "${USERNAME}" || "${USERNAME}" == "null" ]]; then
echo "ERROR: LocalUsername absent ou non transmis par Morpheus."
exit 1
fi
if [[ -z "${PASSWORD}" || "${PASSWORD}" == "null" ]]; then
echo "ERROR: LocalPassword absent ou non transmis par Morpheus."
exit 1
fi
echo "Utilisateur demandé : ${USERNAME}"
if id "${USERNAME}" >/dev/null 2>&1; then
echo "L'utilisateur ${USERNAME} existe déjà."
else
useradd \
--create-home \
--shell /bin/bash \
"${USERNAME}"
echo "Utilisateur ${USERNAME} créé."
fi
echo "${USERNAME}:${PASSWORD}" | chpasswd
usermod -aG sudo "${USERNAME}"
echo "Mot de passe configuré."
echo "Droits sudo configurés."
echo
echo "=== Vérification ==="
id "${USERNAME}"
echo
echo "Compte ${USERNAME} configuré avec succès."
Créer la Task
Dans Library → Automation → Tasks → + Add, je crée une Task de type Library Script et je sélectionne le Script Template précédent.
Name : Create Linux Local Admin Type : Library Script Library Script : Create Linux Local Admin Execute Target : Resource
Créer l’Operational Workflow
Dans Library → Automation → Workflows → + Add → Operational Workflow, j’ajoute :
Workflow : Create Linux Local Admin Inputs : - LocalUsername - LocalPassword Task : - Create Linux Local Admin
Depuis une instance existante, Actions → Run Workflow affiche alors un petit formulaire demandant le nom du compte et son mot de passe.
Le résultat attendu ressemble à ceci :
=== Configuration du compte local === Utilisateur demandé : ops-admin Utilisateur ops-admin créé. Mot de passe configuré. Droits sudo configurés. === Vérification === uid=1002(ops-admin) gid=1002(ops-admin) groups=1002(ops-admin),27(sudo) Compte ops-admin configuré avec succès.
Cas d’usage 2 — Réinitialiser un mot de passe local
Le second workflow reprend exactement la même mécanique. L’Input LocalUsername existe déjà : il est donc réutilisé. Il suffit d’ajouter un nouvel Input de type Password.
Name : New Password Field Name : NewPassword Type : Password Required : Yes
Le Script Template peut rester très court :
#!/bin/bash
set -euo pipefail
USERNAME="<%= customOptions.LocalUsername %>"
PASSWORD="<%= customOptions.NewPassword %>"
echo "=== Reset du mot de passe local ==="
if [[ -z "${USERNAME}" || "${USERNAME}" == "null" ]]; then
echo "ERROR: LocalUsername absent ou non transmis par Morpheus."
exit 1
fi
if [[ -z "${PASSWORD}" || "${PASSWORD}" == "null" ]]; then
echo "ERROR: NewPassword absent ou non transmis par Morpheus."
exit 1
fi
if ! id "${USERNAME}" >/dev/null 2>&1; then
echo "ERROR: L'utilisateur ${USERNAME} n'existe pas."
exit 1
fi
echo "${USERNAME}:${PASSWORD}" | chpasswd
echo "Mot de passe du compte ${USERNAME} modifié avec succès."
Je crée ensuite comme précédemment :
Script Template : Reset Linux Local Password Task : Reset Linux Local Password Workflow : Reset Linux Local Password Inputs : - LocalUsername - NewPassword
Ce cas d’usage est intéressant pour le support : l’opérateur peut réinitialiser un mot de passe sans ouvrir directement une session root sur la VM.
Cas d’usage 3 — Diagnostic Linux depuis Morpheus
Le troisième workflow ne modifie rien sur la machine. Il collecte quelques indicateurs utiles et affiche une synthèse directement dans l’historique d’exécution Morpheus.
Une première version très détaillée fonctionnait correctement, mais l’interface Morpheus tronquait l’aperçu avec .... Le résultat complet restait accessible via la fonction de copie du process output, mais pour un opérateur il est beaucoup plus pratique d’avoir immédiatement les informations essentielles.
J’ai donc retenu une version volontairement compacte.
#!/bin/bash
set -u
HOSTNAME_VALUE=$(hostname)
OS_NAME=$(awk -F= '/^PRETTY_NAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release 2>/dev/null)
KERNEL=$(uname -r)
UPTIME_VALUE=$(uptime -p)
VCPU=$(nproc)
MEM_AVAILABLE_KB=$(awk '/MemAvailable:/ {print $2}' /proc/meminfo)
MEM_TOTAL_KB=$(awk '/MemTotal:/ {print $2}' /proc/meminfo)
MEM_AVAILABLE_GB=$(awk -v kb="${MEM_AVAILABLE_KB:-0}" \
'BEGIN {printf "%.1f", kb/1024/1024}')
MEM_PERCENT=$(awk \
-v available="${MEM_AVAILABLE_KB:-0}" \
-v total="${MEM_TOTAL_KB:-1}" \
'BEGIN {printf "%.0f", ((total-available)/total)*100}')
ROOT_USAGE=$(df -P / | awk 'NR==2 {gsub("%","",$5); print $5}')
ROOT_SIZE=$(df -hP / | awk 'NR==2 {print $2}')
ROOT_FREE=$(df -hP / | awk 'NR==2 {print $4}')
PRIMARY_IF=$(ip route | awk '/^default/ {print $5; exit}')
PRIMARY_IP=$(ip -4 -o addr show "${PRIMARY_IF}" 2>/dev/null \
| awk '{print $4}' | cut -d/ -f1 | head -1)
DEFAULT_GW=$(ip route | awk '/^default/ {print $3; exit}')
DNS_SERVERS=$(resolvectl dns "${PRIMARY_IF}" 2>/dev/null \
| sed 's/.*: //' || true)
FAILED_SERVICES=$(systemctl --failed --no-legend 2>/dev/null \
| grep -c . || true)
QEMU_STATUS=$(systemctl is-active qemu-guest-agent 2>/dev/null || true)
if command -v cloud-init >/dev/null 2>&1; then
CLOUD_STATUS=$(cloud-init status 2>/dev/null \
| awk -F': ' '/status:/ {print $2}' || true)
else
CLOUD_STATUS="not-installed"
fi
LOAD_AVG=$(awk '{print $1" / "$2" / "$3}' /proc/loadavg)
echo "======================================================"
echo " MORPHEUS LINUX DIAGNOSTIC"
echo "======================================================"
printf "%-14s : %s\n" "Hostname" "${HOSTNAME_VALUE}"
printf "%-14s : %s\n" "OS" "${OS_NAME:-Unknown}"
printf "%-14s : %s\n" "Kernel" "${KERNEL}"
printf "%-14s : %s\n" "Uptime" "${UPTIME_VALUE}"
echo
printf "[OK] %-15s %s vCPU\n" "CPU" "${VCPU}"
if (( MEM_PERCENT >= 90 )); then
STATUS="CRIT"
elif (( MEM_PERCENT >= 80 )); then
STATUS="WARN"
else
STATUS="OK"
fi
printf "[%-4s] %-15s %s%% used / %s GiB available\n" \
"${STATUS}" "Memory" "${MEM_PERCENT}" "${MEM_AVAILABLE_GB}"
if (( ROOT_USAGE >= 90 )); then
STATUS="CRIT"
elif (( ROOT_USAGE >= 80 )); then
STATUS="WARN"
else
STATUS="OK"
fi
printf "[%-4s] %-15s %s%% used / %s free / %s total\n" \
"${STATUS}" "Root FS" "${ROOT_USAGE}" "${ROOT_FREE}" "${ROOT_SIZE}"
if [[ -n "${PRIMARY_IP}" ]]; then
printf "[OK] %-15s %s / %s\n" "Network" "${PRIMARY_IF}" "${PRIMARY_IP}"
else
printf "[CRIT] %-15s No IPv4 address\n" "Network"
fi
if [[ -n "${DEFAULT_GW}" ]]; then
printf "[OK] %-15s %s\n" "Gateway" "${DEFAULT_GW}"
else
printf "[CRIT] %-15s Missing\n" "Gateway"
fi
if getent hosts blog.nxway.fr/ >/dev/null 2>&1; then
printf "[OK] %-15s %s\n" "DNS" "${DNS_SERVERS:-resolution OK}"
else
printf "[WARN] %-15s Resolution failed\n" "DNS"
fi
if [[ "${FAILED_SERVICES}" -eq 0 ]]; then
printf "[OK] %-15s 0 failed\n" "Services"
else
printf "[WARN] %-15s %s failed\n" "Services" "${FAILED_SERVICES}"
fi
if [[ "${QEMU_STATUS}" == "active" ]]; then
printf "[OK] %-15s active\n" "QEMU Agent"
else
printf "[WARN] %-15s %s\n" "QEMU Agent" "${QEMU_STATUS:-inactive}"
fi
if [[ "${CLOUD_STATUS}" == "done" ]]; then
printf "[OK] %-15s done\n" "Cloud-Init"
else
printf "[WARN] %-15s %s\n" "Cloud-Init" "${CLOUD_STATUS:-unknown}"
fi
printf "[OK] %-15s %s\n" "Load Average" "${LOAD_AVG}"
echo "======================================================"
Sur une machine saine, le résultat tient en quelques lignes :
MORPHEUS LINUX DIAGNOSTIC Hostname : vm-linux-001 OS : Ubuntu 24.04 LTS Kernel : 6.8.x-generic Uptime : up 18 hours [OK] CPU 2 vCPU [OK] Memory 8% used / 7.1 GiB available [OK] Root FS 33% used / 16G free / 24G total [OK] Network eth0 / 192.0.2.20 [OK] Gateway 192.0.2.1 [OK] DNS resolution functional [OK] Services 0 failed [OK] QEMU Agent active [OK] Cloud-Init done [OK] Load Average 0.01 / 0.01 / 0.00
Les seuils simples apportent immédiatement de la lisibilité : un filesystem à plus de 80 % passe en WARN, au-delà de 90 % en CRIT. Le même principe peut ensuite être étendu à la mémoire, aux services ou à d’autres contrôles.
Operational Workflow ou Provisioning Workflow ?
La distinction est importante :
- un Provisioning Workflow accompagne le cycle de création d’une nouvelle ressource ;
- un Operational Workflow est particulièrement adapté aux actions réalisées sur une ressource déjà existante.
Pour mes trois exemples, l’Operational Workflow est donc le bon choix : créer un compte après déploiement, réinitialiser un mot de passe ou lancer un diagnostic sont des opérations d’exploitation.
Pourquoi ce modèle est intéressant
Au-delà des trois scripts présentés ici, le véritable intérêt est de construire progressivement une bibliothèque d’opérations standardisées.
On peut imaginer exactement le même mécanisme pour installer un agent de sécurité, relancer un service, étendre un filesystem LVM, poser une clé SSH, appliquer une configuration, vérifier une connectivité réseau ou déclencher une action de maintenance applicative.
Le modèle devient alors :
L'opérateur choisit une action
↓
Morpheus affiche uniquement les Inputs nécessaires
↓
Le Workflow appelle une Task contrôlée
↓
La Task exécute un Script Template versionné
↓
L'action est réalisée sur la ressource cible
↓
Le résultat reste visible dans l'historique Morpheus
C’est cette couche d’abstraction qui transforme Morpheus en véritable portail d’exploitation, et pas uniquement en outil de provisioning.
Quelques bonnes pratiques retenues
- réutiliser les Inputs existants plutôt que les dupliquer ;
- utiliser le type Password pour les secrets saisis par l’opérateur ;
- ne jamais écrire les mots de passe dans les logs ;
- tester systématiquement les variables contre une valeur vide ou
null; - respecter exactement la casse du Field Name dans
customOptions; - rendre les scripts idempotents lorsque c’est possible ;
- privilégier une sortie courte et lisible dans Morpheus pour les workflows de diagnostic ;
- utiliser les Script Templates pour éviter de dupliquer du code directement dans les Tasks.
Conclusion
Ces trois workflows sont simples, mais ils montrent déjà très bien ce que Morpheus peut apporter à l’exploitation quotidienne d’un environnement Linux.
La création d’un compte administrateur valide le passage d’Inputs vers un Script Template. Le reset de mot de passe montre comment réutiliser les mêmes composants. Enfin, le diagnostic Linux permet de transformer plusieurs commandes système en une action self-service immédiatement exploitable depuis l’interface.
À partir de cette base, il devient assez naturel de construire un véritable catalogue d’actions d’exploitation autour des instances HVM.


