Auto-update blog content from Obsidian: 2026-02-09 09:36:32
All checks were successful
Blog Deployment / Check-Rebuild (push) Successful in 7s
Blog Deployment / Build (push) Has been skipped
Blog Deployment / Deploy-Staging (push) Successful in 9s
Blog Deployment / Test-Staging (push) Successful in 1s
Blog Deployment / Merge (push) Successful in 6s
Blog Deployment / Deploy-Production (push) Successful in 9s
Blog Deployment / Test-Production (push) Successful in 3s
Blog Deployment / Clean (push) Has been skipped
Blog Deployment / Notify (push) Successful in 3s

This commit is contained in:
Gitea Actions
2026-02-09 09:36:32 +00:00
parent 3bf8b55f9b
commit 52cb5cf946
2 changed files with 276 additions and 19 deletions

View File

@@ -0,0 +1,259 @@
---
slug: semaphore-ui-interface-ansible-terraform
title: Semaphore UI, une excellente interface pour Ansible et Terraform
description: Démonstration de Semaphore UI, une interface web pour exécuter des playbooks Ansible, du code Terraform et bien plus. Installation avec Docker et exemples rapides.
date: 2026-02-09
draft: true
tags:
- semaphore-ui
- ansible
- terraform
- proxmox
- docker
categories:
- homelab
---
## Intro
Dans mon homelab, j'aime expérimenter avec des outils comme Ansible et Terraform. L'interface principale est le CLI, que j'adore, mais parfois une jolie interface web est juste agréable.
Après avoir configuré mon cluster OPNsense, je voulais un moyen de le tenir à jour selon un calendrier. Pour moi, l'automatisation passe par Ansible, mais comment automatiser et planifier des playbooks ?
Au travail j'utilise Red Hat Ansible Automation Platform, qui est excellent, mais overkill pour mon lab. C'est ainsi que j'ai découvert Semaphore UI. Voyons ce qu'il peut faire.
---
## Qu'estce que Semaphore UI
[Semaphore UI](https://semaphoreui.com/docs/) est une interface web élégante conçue pour exécuter de l'automatisation avec des outils comme Ansible et Terraform, et même des scripts Bash, Powershell ou Python.
Initialement créé sous le nom Ansible Semaphore, une interface web destinée à fournir un front-end simple pour exécuter uniquement des playbooks Ansible. Au fil du temps, la communauté a fait évoluer le projet en une plateforme de contrôle d'automatisation multioutils.
C'est une application autonome écrite en Go avec des dépendances minimales, capable d'utiliser différents backends de base de données, tels que PostgreSQL, MySQL ou BoltDB.
---
## Installation
Semaphore UI prend en charge plusieurs méthodes d'[installation](https://semaphoreui.com/docs/category/installation) : Docker, Kubernetes, gestionnaire de paquets ou simple binaire.
J'ai utilisé Docker pour mon installation, vous pouvez voir comment je déploie actuellement des applications dans ce [post]({{< ref "post/16-how-I-deploy-application" >}})
Voici mon fichier `docker-compose.yml` que j'ai configuré en utilisant PostgreSQL :
```yaml
services:
semaphore:
image: semaphoreui/semaphore:v2.16.45
container_name: semaphore_ui
environment:
- TZ=Europe/Paris
- SEMAPHORE_DB_USER=${POSTGRES_USER}
- SEMAPHORE_DB_PASS=${POSTGRES_PASSWORD}
- SEMAPHORE_DB_HOST=postgres
- SEMAPHORE_DB_PORT=5432
- SEMAPHORE_DB_DIALECT=postgres
- SEMAPHORE_DB=${POSTGRES_DB}
- SEMAPHORE_PLAYBOOK_PATH=/tmp/semaphore/
- SEMAPHORE_ADMIN_PASSWORD=${SEMAPHORE_ADMIN_PASSWORD}
- SEMAPHORE_ADMIN_NAME=${SEMAPHORE_ADMIN_NAME}
- SEMAPHORE_ADMIN_EMAIL=${SEMAPHORE_ADMIN_EMAIL}
- SEMAPHORE_ADMIN=${SEMAPHORE_ADMIN}
- SEMAPHORE_ACCESS_KEY_ENCRYPTION=${SEMAPHORE_ACCESS_KEY_ENCRYPTION}
- SEMAPHORE_LDAP_ACTIVATED='no'
# - SEMAPHORE_LDAP_HOST=dc01.local.example.com
# - SEMAPHORE_LDAP_PORT='636'
# - SEMAPHORE_LDAP_NEEDTLS='yes'
# - SEMAPHORE_LDAP_DN_BIND='uid=bind_user,cn=users,cn=accounts,dc=local,dc=shiftsystems,dc=net'
# - SEMAPHORE_LDAP_PASSWORD='ldap_bind_account_password'
# - SEMAPHORE_LDAP_DN_SEARCH='dc=local,dc=example,dc=com'
# - SEMAPHORE_LDAP_SEARCH_FILTER="(\u0026(uid=%s)(memberOf=cn=ipausers,cn=groups,cn=accounts,dc=local,dc=example,dc=com))"
depends_on:
- postgres
networks:
- backend
- web
labels:
- traefik.enable=true
- traefik.http.routers.semaphore.rule=Host(`semaphore.vezpi.com`)
- traefik.http.routers.semaphore.entrypoints=https
- traefik.http.routers.semaphore.tls.certresolver=letsencrypt
- traefik.http.services.semaphore.loadbalancer.server.port=3000
restart: unless-stopped
postgres:
image: postgres:14
hostname: postgres
container_name: semaphore_postgres
volumes:
- /appli/data/semaphore/db:/var/lib/postgresql/data
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
networks:
- backend
restart: unless-stopped
networks:
backend:
web:
external: true
```
Pour générer les clés d'accès chiffrées, j'utilise cette commande :
```bash
head -c32 /dev/urandom | base64
```
Avec Semaphore en fonctionnement, faisons rapidement le tour de l'UI et connectons-la à un dépôt.
---
## Discovery
Après avoir démarré la stack, je peux atteindre la page de connexion à l'URL :
![Page de connexion de Semaphore UI](img/semaphore-login-page.png)
Pour me connecter, j'utilise les identifiants définis par `SEMAPHORE_ADMIN_NAME`/`SEMAPHORE_ADMIN_PASSWORD`.
Au premier accès, Semaphore me demande de créer un projet. J'ai créé le projet Homelab :
![Page de création de projet de Semaphore UI](img/semaphore-create-project.png)
La première chose que je veux faire est d'ajouter mon dépôt _homelab_ (vous pouvez trouver son miroir sur Github [ici](https://github.com/Vezpi/homelab)). Dans `Repository`, je clique sur le bouton `New Repository`, et j'ajoute l'URL du repo. Je ne spécifie pas d'identifiants car le dépôt est public :
![Page d'ajout de dépôt de Semaphore UI](img/semaphore-add-repository.png)
Avant de continuer, je déploie 3 VM à des fins de test : `sem01`, `sem02` et `sem03`. Je les ai créées avec Terraform via [ce projet](https://github.com/Vezpi/Homelab/tree/main/terraform/projects/semaphore-vms).
Pour interagir avec ces VM, je dois configurer des identifiants. Dans le `Key Store`, j'ajoute la première donnée d'identification, une clé SSH pour mon utilisateur :
![Page d'ajout d'une nouvelle clé de Semaphore UI](img/semaphore-create-new-ssh-key.png)
Ensuite je crée un nouvel `Inventory`. J'utilise le format d'inventaire Ansible (le seul disponible). Je sélectionne la clé SSH créée précédemment et choisis le type `Static`. Dans les champs je renseigne les 3 hôtes créés avec leur FQDN :
![Page de création d'un inventaire statique de Semaphore UI](img/semaphore-create-new-static-inventory.png)
✅ Avec un projet, un repo, des identifiants et un inventaire en place, je peux avancer et tester l'exécution d'un playbook Ansible.
---
## Launching an Ansible playbook
Je veux tester quelque chose de simple : installer un serveur web avec une page personnalisée sur ces 3 VM. Je crée le playbook `install_nginx.yml` :
```yaml
---
- name: Demo Playbook - Install Nginx and Serve Hostname Page
hosts: all
become: true
tasks:
- name: Ensure apt cache is updated
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
- name: Create index.html with hostname
ansible.builtin.copy:
dest: /var/www/html/index.html
content: |
<html>
<head><title>Demo</title></head>
<body>
<h1>Hostname: {{ inventory_hostname }}</h1>
</body>
</html>
owner: www-data
group: www-data
mode: "0644"
- name: Ensure nginx is running
ansible.builtin.service:
name: nginx
state: started
enabled: true
```
Dans Semaphore UI, je peux maintenant créer mon premier `Task Template` pour un playbook Ansible. Je lui donne un nom, le chemin du playbook (depuis le dossier racine du repo), le dépôt et sa branche :
![Nouveau template de tâche Ansible dans Semaphore UI](img/semaphore-create-new-ansible-task-template.png)
Il est temps de lancer le playbook ! Dans la liste des task templates, je clique sur le bouton ▶️ :
![Lancement du template de tâche Ansible dans Semaphore UI](img/semaphore-run-test-playbook.png)
Le playbook se lance et je peux suivre la sortie en temps réel :
![Semaphore UI Ansible task output](img/semaphore-ui-ansible-task-output.png)
Je peux aussi consulter les exécutions précédentes :
![Liste des exécutions de tâches dans Semaphore UI](img/semaphore-ui-task-template-run-list.png)
✅ Enfin, je peux confirmer que le travail est fini en vérifiant l'URL sur le port 80 (http) :
![Test de l'URL après application du playbook sur les hôtes](img/semaphore-ui-test-nginx-page-playbook.png)
Gérer des playbooks Ansible dans Semaphore UI est assez simple et vraiment pratique. L'interface est très soignée.
Il existe aussi beaucoup d'options de personnalisation lors de la configuration d'un task template. Je peux utiliser des variables via un survey, spécifier un limit ou des tags. J'apprécie vraiment cela.
---
## Déploiement avec Terraform
Alors que l'exécution des playbooks Ansible était simple dès le départ, le déploiement avec Terraform sur Proxmox VE a été un peu différent. Avant de commencer, je détruis les 3 VM déployées précédemment.
Auparavant depuis le CLI, j'interagissais avec Terraform sur le cluster Proxmox en utilisant une clé SSH. Je n'ai pas réussi à le faire fonctionner depuis Semaphore UI. J'ai dû utiliser un nom d'utilisateur avec un mot de passe à la place.
Je me suis dit que c'était une bonne occasion d'utiliser Ansible pour créer un utilisateur Proxmox dédié. Ma première exécution a échoué avec :
```plaintext
Unable to encrypt nor hash, passlib must be installed. No module named 'passlib'
```
C'est apparemment un problème connu de l'environnement Python de Semaphore. Comme contournement, j'ai installé `passlib` directement dans le conteneur :
```bash
docker exec -it semaphore_ui pip install passlib
```
Avec cela en place, le playbook a réussi et j'ai pu créer l'utilisateur :
```yaml
---
- name: Create Terraform local user for Proxmox
hosts: nodes
become: true
tasks:
- name: Create terraform user
ansible.builtin.user:
name: "{{ terraform_user }}"
password: "{{ terraform_password | password_hash('sha512') }}"
shell: /bin/bash
- name: Create sudoers file for terraform user
ansible.builtin.copy:
dest: /etc/sudoers.d/{{ terraform_user }}
mode: '0440'
content: |
{{ terraform_user }} ALL=(root) NOPASSWD: /sbin/pvesm
{{ terraform_user }} ALL=(root) NOPASSWD: /sbin/qm
{{ terraform_user }} ALL=(root) NOPASSWD: /usr/bin/tee /var/lib/vz/*
```
Ensuite je crée un variable group `pve_vm`. Un variable group me permet de définir plusieurs variables et secrets ensemble :
![Nouveau groupe de variables dans Semaphore UI](img/semaphore-ui-create-variable-group.png)
Puis je crée un nouveau task template, cette fois de type Terraform Code. Je lui donne un nom, le chemin du projet Terraform, un workspace, le dépôt avec sa branche et le variable group :
![Nouveau template de tâche Terraform dans Semaphore UI](img/semaphore-task-template-terraform.png)
Lancer le template me donne quelques options supplémentaires liées à Terraform :
![Options d'exécution Terraform dans Semaphore UI](img/semaphore-running-terraform-code-options.png)
Après le plan Terraform, il me propose d'appliquer, d'annuler ou d'arrêter :
![Plan Terraform dans Semaphore UI](img/semaphore-terraform-task-working.png)
Enfin, après avoir cliqué sur ✅ pour appliquer, j'ai pu regarder Terraform construire les VM, comme avec le CLI. À la fin, les VM ont été déployées avec succès sur Proxmox :
![Déploiement Terraform terminé dans Semaphore UI](img/semaphore-ui-deploy-with-terraform.png)
---
## Conclusion
Voilà pour mes tests de Semaphore UI, j'espère que cela vous aidera à voir ce que vous pouvez en faire.
Dans l'ensemble, l'interface est propre et agréable à utiliser. Je peux tout à fait m'imaginer planifier des playbooks Ansible avec elle, comme les mises à jour OPNsense dont je parlais en intro.
Pour Terraform, je l'utiliserai probablement pour lancer des VM éphémères pour des tests. J'aimerais utiliser le backend HTTP pour tfstate, mais cela nécessite la version Pro.
Pour conclure, Semaphore UI est un excellent outil, intuitif, esthétique et pratique. Beau travail de la part du projet !

View File

@@ -11,10 +11,11 @@ tags:
- proxmox - proxmox
- docker - docker
categories: categories:
- homelab
--- ---
## Intro ## Intro
In my homelab, I like to play with tools like Ansible and Terraform. The primary interface is the CLI, which I love, but sometimes a fancy web UI is nicer. In my homelab, I like to play with tools like Ansible and Terraform. The primary interface is the CLI, which I love, but sometimes a fancy web UI is just nice.
After setting up my OPNsense cluster, I wanted a way to keep it up to date on a schedule. Automation means Ansible to me, but how do you automate and schedule playbooks? After setting up my OPNsense cluster, I wanted a way to keep it up to date on a schedule. Automation means Ansible to me, but how do you automate and schedule playbooks?
@@ -108,25 +109,23 @@ With Semaphore running, lets take a quick tour of the UI and wire it up to a
## Discovery ## Discovery
After starting the stack, I can reach the login page at the URL: After starting the stack, I can reach the login page at the URL:
![Semaphore UI login page](img/semaphore-login-page.png) ![Page de connexion de Semaphore UI](img/semaphore-login-page.png)
To log in, I use the credentials defined by `SEMAPHORE_ADMIN_NAME`/`SEMAPHORE_ADMIN_PASSWORD`. To log in, I use the credentials defined by `SEMAPHORE_ADMIN_NAME`/`SEMAPHORE_ADMIN_PASSWORD`.
On first login, Semaphore prompt me to create a project. I created the Homelab project: On first login, Semaphore prompt me to create a project. I created the Homelab project:
![Semaphore UI new project page](img/semaphore-create-project.png) ![Page de création de projet de Semaphore UI](img/semaphore-create-project.png)
The first thing I want to do is to add my *homelab* repository (you can find its mirror on Github [here](https://github.com/Vezpi/homelab)). In `Repository`, I click the `New Repository` button, and add the repo URL. I don't specify credentials because the repo is public: The first thing I want to do is to add my *homelab* repository (you can find its mirror on Github [here](https://github.com/Vezpi/homelab)). In `Repository`, I click the `New Repository` button, and add the repo URL. I don't specify credentials because the repo is public:
![Semaphore UI new repository page](img/semaphore-add-repository.png) ![Page d'ajout de dépôt de Semaphore UI](img/semaphore-add-repository.png)
Before continue, I deploy 3 VMs for testing purpose: `sem01`, `sem02` and `sem03`. I created them using Terraform with [this project](https://github.com/Vezpi/Homelab/tree/main/terraform/projects/semaphore-vms). Before continue, I deploy 3 VMs for testing purpose: `sem01`, `sem02` and `sem03`. I created them using Terraform with [this project](https://github.com/Vezpi/Homelab/tree/main/terraform/projects/semaphore-vms).
To interact with these VMs I need to configure credentials. In the the `Key Store`, I add the first credential, a SSH key for my user: To interact with these VMs I need to configure credentials. In the the `Key Store`, I add the first credential, a SSH key for my user:
![Semaphore UI new key page](img/semaphore-create-new-ssh-key.png) ![Page d'ajout d'une nouvelle clé de Semaphore UI](img/semaphore-create-new-ssh-key.png)
Then I create a new `Inventory`. I'm using the Ansible inventory format (the only one available). I select the SSH key previously created and select the type as `Static`. In the fields I enter the 3 hosts created with their FQDN: Then I create a new `Inventory`. I'm using the Ansible inventory format (the only one available). I select the SSH key previously created and select the type as `Static`. In the fields I enter the 3 hosts created with their FQDN:
![Semaphore UI new inventory page](img/semaphore-create-new-static-inventory.png) ![Page de création d'un inventaire statique de Semaphore UI](img/semaphore-create-new-static-inventory.png)
![Semaphore UI new inventory page](img/semaphore-create-new-static-inventory.png)
✅ With a project, repo, credentials, and inventory in place, I can move forward and test to run an Ansible playbook. ✅ With a project, repo, credentials, and inventory in place, I can move forward and test to run an Ansible playbook.
@@ -173,26 +172,25 @@ I want to test something simple, install a web server with a custom page on thes
``` ```
In Semaphore UI, I can now create my first `Task Template` for Ansible playbook. I give it a name, the playbook path (from the root folder of the repo), the repository and its branch: In Semaphore UI, I can now create my first `Task Template` for Ansible playbook. I give it a name, the playbook path (from the root folder of the repo), the repository and its branch:
![Semaphore UI new Ansible task template](img/semaphore-create-new-ansible-task-template.png) ![Nouveau template de tâche Ansible dans Semaphore UI](img/semaphore-create-new-ansible-task-template.png)
Time to launch the playbook! In the task templates list, I click on the ▶️ button: Time to launch the playbook! In the task templates list, I click on the ▶️ button:
![Semaphore UI launch Ansible task template](img/semaphore-run-test-playbook.png) ![Lancement du template de tâche Ansible dans Semaphore UI](img/semaphore-run-test-playbook.png)
The playbook launches and I can follow the output in real time: The playbook launches and I can follow the output in real time:
![Semaphore UI Ansible task output](img/semaphore-ui-ansible-task-output.png) ![Semaphore UI Ansible task output](img/semaphore-ui-ansible-task-output.png)
I can also review previous runs: I can also review previous runs:
![Semaphore UI tasks runs list](img/semaphore-ui-task-template-run-list.png) ![Liste des exécutions de tâches dans Semaphore UI](img/semaphore-ui-task-template-run-list.png)
✅ Finally I can confirm the job is done by checking the URL on port 80 (http): ✅ Finally I can confirm the job is done by checking the URL on port 80 (http):
![Testing URL after applying playbook on hosts ](img/semaphore-ui-test-nginx-page-playbook.png) ![Test de l'URL après application du playbook sur les hôtes](img/semaphore-ui-test-nginx-page-playbook.png)
Managing Ansible playbooks in Semaphore UI is pretty simple and really convenient. The interface is really sleek. Managing Ansible playbooks in Semaphore UI is pretty simple and really convenient. The interface is really sleek.
There are also a lot of customization available when setting the task template up. I can use variables in a survey, specify limit or tags. I really like it. There are also a lot of customization available when setting the task template up. I can use variables in a survey, specify limit or tags. I really like it.
--- ---
## Deploy with Terraform ## Deploy with Terraform
@@ -205,7 +203,7 @@ I told myself it was a good opportunity to use Ansible to create a dedicated Pro
Unable to encrypt nor hash, passlib must be installed. No module named 'passlib' Unable to encrypt nor hash, passlib must be installed. No module named 'passlib'
``` ```
This is apparently a known issue with Semaphores Python environment. As a workaround, I installed `passlib` directly in the container This is apparently a known issue with Semaphores Python environment. As a workaround, I installed `passlib` directly in the container:
```bash ```bash
docker exec -it semaphore_ui pip install passlib docker exec -it semaphore_ui pip install passlib
``` ```
@@ -235,19 +233,19 @@ With that in place, the playbook succeeded and I could create the user:
``` ```
Next I create a variable group `pve_vm`. A variable group let me define multiple variables and secrets together: Next I create a variable group `pve_vm`. A variable group let me define multiple variables and secrets together:
![Semaphore UI new variable group](img/semaphore-ui-create-variable-group.png) ![Nouveau groupe de variables dans Semaphore UI](img/semaphore-ui-create-variable-group.png)
Then I create a new task template, this time with the kind Terraform Code. I give it a name, the path of the terraform [project](https://github.com/Vezpi/Homelab/tree/main/terraform/projects/semaphore-vms), a workspace, the repository along with its branch and. the variable group: Then I create a new task template, this time with the kind Terraform Code. I give it a name, the path of the terraform [project](https://github.com/Vezpi/Homelab/tree/main/terraform/projects/semaphore-vms), a workspace, the repository along with its branch and. the variable group:
![Semaphore UI new Terraform task template](img/semaphore-task-template-terraform.png) ![Nouveau template de tâche Terraform dans Semaphore UI](img/semaphore-task-template-terraform.png)
Running the template gives me some additional options related to Terraform: Running the template gives me some additional options related to Terraform:
![Semaphore UI run Terraform task](img/semaphore-running-terraform-code-options.png) ![Options d'exécution Terraform dans Semaphore UI](img/semaphore-running-terraform-code-options.png)
After the Terraform plan, I'm proposed to apply, cancel or stop: After the Terraform plan, I'm proposed to apply, cancel or stop:
![Semaphore UI task Terraform plan](img/semaphore-terraform-task-working.png) ![Plan Terraform dans Semaphore UI](img/semaphore-terraform-task-working.png)
Finally after hitting ✅ to apply, I could watch Terraform build the VMs, just like using the CLI. At the end, the VMs were successfully deployed on Proxmox: Finally after hitting ✅ to apply, I could watch Terraform build the VMs, just like using the CLI. At the end, the VMs were successfully deployed on Proxmox:
![Semaphore UI Terraform deploy complete](img/semaphore-ui-deploy-with-terraform.png) ![Déploiement Terraform terminé dans Semaphore UI](img/semaphore-ui-deploy-with-terraform.png)
--- ---
## Conclusion ## Conclusion