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.