Le blog technique

Toutes les astuces #tech des collaborateurs de PI Services.

#openblogPI

Retrouvez les articles à la une

🐍 Interroger l’API de Rundeck avec Python : Un Guide Rapide

Rundeck est une plateforme pour l’automatisation des opérations et la gestion des exécutions. Bien que vous puissiez tout faire via l’interface web, la vraie puissance vient de son API, qui vous permet d’intégrer Rundeck dans vos scripts et vos systèmes existants.

Ce guide rapide vous montrera comment utiliser la bibliothèque standard de Python, requests, pour interagir avec l’API de Rundeck.


Prérequis

  • Un serveur Rundeck en cours d’exécution.
  • Un Jeton d’API (API Token) créé dans les paramètres de votre profil utilisateur Rundeck.
  • Python et la bibliothèque requests installés (pip install requests).

Étape 1 : Définir les Constantes de Connexion

Nous allons commencer par définir l’URL de base de votre instance Rundeck et l’en-tête d’autorisation qui contiendra votre Jeton d’API

import requests
import json

# Remplacez par l'URL de votre serveur Rundeck
RUNDECK_URL = "http://votreserveur:4440" 

# Remplacez par votre véritable Jeton d'API
API_TOKEN = "votre_jeton_api_secret"

HEADERS = {
    'X-Rundeck-Auth-Token': API_TOKEN,
    'Accept': 'application/json'  # Nous voulons une réponse au format JSON
}

Étape 2 : Interroger l’API

L’une des requêtes les plus courantes est de lister tous les projets. L’API de Rundeck a généralement un chemin qui inclut la version d’API que vous ciblez (souvent api/40 ou plus).

Pour lister les projets, le chemin d’accès est souvent /api/40/projects.

Exemple 1 : Lister les Projets

def lister_projets():
    # Chemin complet de l'API pour lister les projets
    endpoint = f"{RUNDECK_URL}/api/40/projects"
    
    print(f"-> Requête à : {endpoint}")
    
    try:
        # Envoi de la requête GET
        reponse = requests.get(endpoint, headers=HEADERS)
        reponse.raise_for_status()  # Lève une exception pour les codes d'erreur (4xx ou 5xx)

        # La réponse est une liste de projets au format JSON
        projets = reponse.json()
        
        print(f"\n✅ Nombre de projets trouvés : {len(projets)}")
        
        for projet in projets:
            print(f"- Nom : {projet['name']} (Description : {projet.get('description', 'N/A')})")
            
    except requests.exceptions.RequestException as e:
        print(f"❌ Erreur lors de la requête : {e}")

lister_projets()

Exemple 2 : Exécuter un Job

Pour une opération plus complexe comme l’exécution d’un job, vous utilisez généralement une méthode POST et vous devez cibler l’ID du job.

# Remplacez par l'ID de votre job
JOB_ID = "1a2b3c4d-5e6f-7890-abcd-ef0123456789" 

def executer_job():
    # Chemin de l'API pour l'exécution d'un job
    endpoint = f"{RUNDECK_URL}/api/40/job/{JOB_ID}/run"
    
    print(f"-> Exécution du job ID: {JOB_ID}")
    
    try:
        # Requête POST sans corps (par défaut, il n'y a pas d'options)
        reponse = requests.post(endpoint, headers=HEADERS)
        reponse.raise_for_status()

        resultat = reponse.json()
        
        # Le résultat contient l'ID de l'exécution
        execution_id = resultat['id'] 
        
        print(f"\n✅ Job lancé avec succès !")
        print(f"   ID d'exécution : {execution_id}")
        print(f"   URL de suivi : {resultat.get('permalink')}")
        
    except requests.exceptions.RequestException as e:
        print(f"❌ Erreur lors de l'exécution du job : {e}")

# executer_job() # Décommentez pour tester l'exécution

Conclusion

En utilisant la simple bibliothèque requests, vous pouvez facilement automatiser et interagir avec votre environnement Rundeck en quelques lignes de Python. Que ce soit pour récupérer des métriques, déclencher des workflows ou gérer des ressources.

Accorder un consentement pour une application au nom d’un utilisateur unique avec PowerShell

À quoi cela sert ?

Dans certaines organisations, il est parfois nécessaire d’accorder à une application des permissions pour accéder à une API uniquement au nom d’un utilisateur précis, sans étendre ce consentement à l’ensemble du tenant. Cette approche est particulièrement utile pour des scénarios de test, des comptes dédiés, ou des usages restreints nécessitant un contrôle fin des accès. Microsoft Entra ID permet cela grâce à Microsoft Graph PowerShell, en créant une délégation ciblée de permissions associée à un utilisateur unique.

Script proposé par Microsoft

Microsoft fournit un script PowerShell destiné à appliquer, pour une application, un ensemble de permissions déléguées au nom d’un utilisateur précis.

Prérequis

  • Un compte avec un rôle suffisant : Application Administrator, Cloud Application Administrator ou Privileged Role Administrator.
  • L’ID de l’application cliente, l’ID de l’API et l’UPN ou ID de l’utilisateur ciblé.
  • Le module Microsoft Graph PowerShell installé.

Script

# The app for which consent is being granted.
$clientAppId = "de8bc8b5-d9f9-48b1-a8ad-b748da725064" # Microsoft Graph Explorer

# The API to which access will be granted. Microsoft Graph Explorer makes API 
# requests to the Microsoft Graph API, so we'll use that here.
$resourceAppId = "00000003-0000-0000-c000-000000000000" # Microsoft Graph API

# The permissions to grant. Here we're including "openid", "profile", "User.Read"
# and "offline_access" (for basic sign-in), as well as "User.ReadBasic.All" (for 
# reading other users' basic profile).
$permissions = @("openid", "profile", "offline_access", "User.Read", "User.ReadBasic.All")

# The user on behalf of whom access will be granted. The app will be able to access 
# the API on behalf of this user.
$userUpnOrId = "user@example.com"

# Step 0. Connect to Microsoft Graph PowerShell. We need User.ReadBasic.All to get
#    users' IDs, Application.ReadWrite.All to list and create service principals, 
#    DelegatedPermissionGrant.ReadWrite.All to create delegated permission grants, 
#    and AppRoleAssignment.ReadWrite.All to assign an app role.
#    WARNING: These are high-privilege permissions!
Connect-MgGraph -Scopes ("User.ReadBasic.All Application.ReadWrite.All " + "DelegatedPermissionGrant.ReadWrite.All " + "AppRoleAssignment.ReadWrite.All")

# Step 1. Check if a service principal exists for the client application. 
#     If one doesn't exist, create it.
$clientSp = Get-MgServicePrincipal -Filter "appId eq '$($clientAppId)'"
if (-not $clientSp) {
   $clientSp = New-MgServicePrincipal -AppId $clientAppId
}

# Step 2. Create a delegated permission that grants the client app access to the
#     API, on behalf of the user. (This example assumes that an existing delegated 
#     permission grant does not already exist, in which case it would be necessary 
#     to update the existing grant, rather than create a new one.)
$user = Get-MgUser -UserId $userUpnOrId
$resourceSp = Get-MgServicePrincipal -Filter "appId eq '$($resourceAppId)'"
$scopeToGrant = $permissions -join " "
$grant = New-MgOauth2PermissionGrant -ResourceId $resourceSp.Id -Scope $scopeToGrant -ClientId $clientSp.Id -ConsentType "Principal" -PrincipalId $user.Id

# Step 3. Assign the app to the user. This ensures that the user can sign in if assignment
#     is required, and ensures that the app shows up under the user's My Apps portal.
if ($clientSp.AppRoles | ? { $_.AllowedMemberTypes -contains "User" }) {
    Write-Warning ("A default app role assignment cannot be created because the " + "client application exposes user-assignable app roles. You must " + "assign the user a specific app role for the app to be listed " + "in the user's My Apps portal.")
} else {
    # The app role ID 00000000-0000-0000-0000-000000000000 is the default app role
    # indicating that the app is assigned to the user, but not for any specific 
    # app role.
    $assignment = New-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $clientSp.Id -ResourceId $clientSp.Id -PrincipalId $user.Id -AppRoleId "00000000-0000-0000-0000-000000000000"
}

Conclusion

Bien que ce mécanisme soit efficace pour des besoins très ciblés, il doit être utilisé de manière prudente. Le consentement est appliqué immédiatement, sans validation de l’utilisateur, ce qui peut amplifier les risques en cas de permissions sensibles. Cette méthode n’est pas adaptée pour remplacer un consentement administrateur global dès que l’application s’adresse à un ensemble étendu d’utilisateur.

[Le saviez vous ?] – Modification du groupe « Schema Admins » quels évènements surveiller ?

Lorsqu’une modification du groupe à privilèges ‘Schema Admins‘ est réalisée dans l’Active Directory, un Event Id est déclenché dans le journal d’évènement « Security » du contrôleur de domaine.

Voilà pourquoi il est important d’activer les journaux d’audit et d’ajouter ces évènements dans votre système de monitoring (Siem, Scripts Powershell, Scom, Zabbix….).

L’ajout de membre

Lorsqu’un compte est ajouté aux membres du Groupe ‘Schema Admins‘, l’évènement 4756 apparait dans les journaux de sécurité du contrôleur de domaine, malheureusement cet Event Id ne remonte pas que les modifications du groupe ‘Schema Admins’, il vous faudra donc ajouter un peu de filtrage en parcourant le Message de l’évènement.

La suppression de membre

Lorsqu’un compte est retiré des membres du Groupe ‘Schema Admins‘, l’évènement 4757 apparait dans les journaux de sécurité du contrôleur de domaine, tout comme pour l’ajout, il vous faudra donc ajouter un peu de filtrage en parcourant le Message de l’évènement.

Attention

Les groupes « Schema Admins » et « Enterprise Admins » partagent le même Id pour l’ajout et la suppression de membres (4756 et 4757), faites donc attention à la configuration de vos filtres et alertes.