Le blog technique

Toutes les astuces #tech des collaborateurs de PI Services.

#openblogPI

Retrouvez les articles à la une

Python – Executer des commandes sur une machine Windows distante.

Le script ci-dessous propose d’utiliser le module pypsrp (https://pypi.org/project/pypsrp/) pour executer n’importe quelle commande (cmd/powershell) sur une machine windows distante.

Dans cet exemple l’authentification se fait via kerberos, mais le parametre auth de wsman peut utiliser basiccertificatenegotiatentlmkerberoscredssp

wsman = WSMan(target, ssl=False, auth='kerberos')

Par defaut le mode winrm utilisé (–winrm-mode) est runspacepool qui est plus compatible avec des commandes ou des blocs de code powershell.

Un test de connectivité winrm (–test-winrm-port, par defaut activé) permet de tester la connexion avant d’executer la commande.

Le paramètre –execute-remote-action etant par defaut desactivé (False), il est necessaire de l’activer (–execute-remote-action True) pour executer effectivement la commande.

help = """
SCRIPT D'EXECUTION DE COMMANDES WINRM SUR UNE MACHINE WINDOWS CIBLE DEFINIE EN ARGUMENT.

Description :
Ce script a été développé à l'occasion d'une demande de reboots récurrents de nombreuses machines Windows. Il a été implémenté de manière à pouvoir être réutilisé pour d'autre commandes `winrm` que le reboot.


ARGUMENTS:
- --get-help True: Affiche l'aide.
- --win-computer: Nom de la machine cible Windows sur laquelle exécuter la commande. (Requis)
- --execute-remote-action True: Execute les actions sur les machines distantes. (Valeur par défaut: False)
- --remote-command: Commande à exécuter sur les machines distantes. (Valeur par défaut: whoami)
- --test-winrm-port True: Test de la connexion au port WinRM des machines distantes avec le module socket. (Valeur par défaut: True)
- --winrm-mode: methode d'execution de la commande sur la machine distante. (Valeur par défaut: runspacepool)




Exemple d'utilisation:
  - Executer la commande "whoami" sur la machine "mycomputer" avec le test de connexion au port WinRM:
  python run_winrm_cmd_on_host.py --win-computer mycomputer --execute-remote-action True --test-winrm-port True --remote-command "get-process"

  

  """


import sys
import os
import argparse
from pypsrp.shell import Process, WinRS
from pypsrp.powershell import PowerShell, RunspacePool
from pypsrp.wsman import WSMan
import socket


if __name__ == '__main__':


        ###########################################
        # Arguments du script
        ###########################################

        
        parser = argparse.ArgumentParser()

        parser.add_argument(
        '--get-help',
        type=str,
        choices=['True', 'False'],
        default='False',
        help='Affiche l\'aide.'
        )

        parser.add_argument(
        '--win-computer',
        type=str,
        help='machine cible Windows'
        )

        parser.add_argument(
        '--execute-remote-action',
        type=str,
        choices=['True', 'False'],
        default='False',
        help='Execute effectivement les actions sur les machines distantes.'
        )

        parser.add_argument(
        '--remote-command',
        type=str,
        default="whoami",
        help='Commande à exécuter sur les machines distantes.'
        )

        parser.add_argument(
        '--test-winrm-port',
        type=str,
        choices=['True', 'False'],
        default='True',
        help='Test de la connexion au port WinRM des machines distantes avec le module socket.'
        )

        parser.add_argument(
        '--winrm-mode',
        type=str,
        choices=['winrs', 'runspacepool'],
        default='runspacepool',
        help='methode d\'execution de la commande sur la machine distante.'
        )


        args = parser.parse_args()
        

        if args.get_help == 'True':
                print(
                help        
                )
                sys.exit(0)


           

        ############################################################################
        ### VERIFICATION DE LA CONNEXION A LA MACHINE CIBLE AVEC LE MODULE SOCKET
        ############################################################################
        
        if args.test_winrm_port == 'True':
                print("---------------------------------------------------------------------------------------------------------------------------------------------")
                print(f"TEST DE RESOLUTION DE NOM ET CONNEXION AU PORT 5985 (WINRM) pour la machine {args.win_computer} avec le module socket")
                print("---------------------------------------------------------------------------------------------------------------------------------------------")


                try:
                                
                                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                                sock.settimeout(5)
                                sockresult = sock.connect_ex((args.win_computer,5985))

                                if not socket.gethostbyname(args.win_computer):
                                        print("!!! resolution KO pour " + args.win_computer +"!!!")
                                        wincomputer_resolved = False
                                        exit(1)
                                        
                                        

                                if not (sockresult == 0):
                                        print("!!! WinRM TCP KO pour  " + args.win_computer +"!!!")
                                        wincomputer_resolved = True
                                        winrm_port_open = False
                                        exit(1)
                                        
                                        
                                
                                else:
                                        print("resolution OK et WinRM OK pour " + args.win_computer )
                                        
                                sock.close()
                               
                except Exception as error:
                                print(f"ERREUR: {error}")
                                exit(1)
                                

                                

                print("---------------------------------------------------------------------------------------------------------------------------------------------")


        
        ########################################
        ### ACTION SUR LA MACHINE AVEC WINRM
        ########################################
        


# Action executee si argument execute_remote_action = True et si la machine est joignable
        if args.execute_remote_action == 'True':
        

                

                        ### METHODE 1: WINRS
                        if args.winrm_mode == 'winrs':

                                try:
                                        target = args.win_computer
                                        print(f"EXECUTION DE LA COMMANDE SUR LA MACHINE \"{target}\"")
                                        print("------------------------------------------------------------------------------------------------------------------------------")
                                        #creates a http connection with no encryption and kerberos auth
                                        wsman = WSMan(target, ssl=False, auth='kerberos')
                                        
                                        # # create a shell with wsman
                                        with wsman, WinRS(wsman) as shell:
                                                # execute a process with arguments in the background
                                                process = Process(shell, args.remote_command)
                                                process.begin_invoke()  # start the invocation and return immediately
                                                process.poll_invoke()  # update the output stream
                                                process.end_invoke()  # finally wait until the process is finished
                                                output = str(process.stdout, errors='ignore')
                                                print(output)
                                                print("------------------------------------------------------------------------------------------------------------------------------\r")



                                except Exception as error:
                                        print(f"ERREUR: {error}")
                                        pass

                                

                        ### METHODE 2: RUNSPACEPOOL
                        if args.winrm_mode == 'runspacepool':

                                try:
                                        target = args.win_computer
                                        print(f"EXECUTION DE LA COMMANDE SUR LA MACHINE \"{target}\"")
                                        print("------------------------------------------------------------------------------------------------------------------------------")
                                        # creates a https connection with explicit kerberos auth and implicit credentials
                                        wsman = WSMan(target, ssl=False, auth="kerberos", cert_validation=False)

                                        
                                        with wsman, RunspacePool(wsman) as pool:

                                                # POUR EXECUTER UNE COMMANDE POWERSHELL SPECIFIQUE (IL FAUT FINIR AVEC OUT-STRING POUR AVOIR UN RETOUR DE TYPE STRING)
                                                # EX: execute 'Get-Process | Select-Object Name | out-string'
                                                ps = PowerShell(pool)
                                                #ps.add_cmdlet("get-process").add_cmdlet("Select-Object").add_argument("Name").add_cmdlet("Out-String")
                                                ps.add_script(args.remote_command + " | out-string")
                                                ps.invoke()
                                                for out in ps.output:
                                                        print(out)
                                                
                                                        
                                except Exception as error:
                                        print(f"ERREUR: {error}")
                                        pass

        
       

GLPI – Connexion et utilisation de l’API avec Curl

L’API GLPI est un outil puissant pour automatiser et intégrer votre gestion de parc et de services. Que vous souhaitiez créer des tickets, mettre à jour des actifs, récupérer des informations ou synchroniser des données, l’API offre une flexibilité incroyable. Et pour interagir avec cette API, curl est votre meilleur ami en ligne de commande.

Dans cet article, nous allons explorer comment utiliser curl pour effectuer différentes requêtes à l’API GLPI, en fournissant des exemples concrets pour vous aider à démarrer.

1.Creation d’un compte d’acces a l’API, et son App Token

Dans le menu Configuration/General/API de l’interface de GLPI…

Cliquer sur ‘Ajouter un client de l’API’

  • Donner un nom explicite pour l’usage de ce client d’API
  • Choisir eventuellement d’enregistrer les connexions pour garder une trace de ce que fera ce client.
  • La connexion peut etre eventuellement restreinte a une plage d’IP
  • Laisser coché ‘Regener’ pour, a la creation du client, generer un token.

Cliquer Ajouter

Dans la barre de navigation, revenir sur ‘Clients de l’API’

Cliquer sur le client crée

un token a été generé. l’App Token.

Un App token doit etre combiné a un user token pour obtenir un session token

2.Creation d’un user et de son user token

Ce sont les droits du user qui determine les droits lors de l’utilisation de l’API

Aller dans le menu Administration/Utilisateurs

Prenons l’exemple d’un utilisateur existant nommé Report qui n’a que des droits en lecture seule (Ref: onglet Habilitation)

En bas de la page est affiché son jeton d’API, le user token.

Il peut eventuellement etre regénéré en cochant la case puis sauvegarder.

3.Obtention d’un session token avec curl

Les exemples de codes sont effectués dans un shell Linux mais l’utilisation de curl est similaire sous Windows et est inclus a l’OS depuis Windows 10

Pour plus de détails sur curl: https://www.it-connect.fr/curl-loutil-testeur-des-protocoles-divers/

curl -X GET \
  -H "Content-Type: application/json" \
  -H "App-Token: VOTRE_APP_TOKEN" \
  -H "Authorization: user_token VOTRE_USER_TOKEN" \
  "http://votre-serveur-glpi/apirest.php/initSession"

Le code ci-dessus represente l’initialisation de la session pour l’obtention d’un session token

Ce qui dans notre exemple donne:

curl -X GET \
  -H "Content-Type: application/json" \
  -H "App-Token: uItt7yYToLPwBPwDUGyiAHzcF8sRnJjlXCgG1Uha" \
  -H "Authorization: user_token pRDLA0otLAzgrr09KvavtIQMXop3lUmgTHPvNLYn" \
  "https://myglpi.francecentral.cloudapp.azure.com/apirest.php/initSession"

La reponse doit etre un session token

4. Test de requete

La commande curl differe un peu maintenant puisque on utilise toujours l’App Token, mais on utilise le session token a la place du user token, de la manière suivante. Exemple pour recuperer la liste de toutes les machines (Computer) déclarés dans GLPI

curl -X GET \
  -H "Content-Type: application/json" \
  -H "App-Token: uItt7yYToLPwBPwDUGyiAHzcF8sRnJjlXCgG1Uha" \
  -H "Session-Token: 5l9bose84th76leeq9s6u69ped" \
  "https://myglpi.francecentral.cloudapp.azure.com/apirest.php/Computer"

L’api doit renvoyer un bloc qui contiens la liste de tout les ‘Computers’ et leur propriétés.

json serait un format plus pratique pour afficher cette sortie.

Si il n’est pas déja present, installer jq avec la commande suivante (exemple avec apt)

sudo apt-get install jq
curl -X GET \
  -H "Content-Type: application/json" \
  -H "App-Token: uItt7yYToLPwBPwDUGyiAHzcF8sRnJjlXCgG1Uha" \
  -H "Session-Token: 5l9bose84th76leeq9s6u69ped" \
  "https://myglpi.francecentral.cloudapp.azure.com/apirest.php/Computer" | jq

Relancer la requete en envoyant la sortie dans jq (| jq)

Le resultat est plus facile a exploiter.

Pour plus de détail sur l’API de GLPI:

https://glpi-user-documentation.readthedocs.io/fr/latest/modules/configuration/general/api.html

Python – Connexion et Requete Active Directory de base

Le script ci-dessous presente un exemple de connexion et requete basique a Active Directory via l’utilisation du module ldap3, pour recuperer la liste des utilisateurs.

Pour plus de details sur l’utilisation du module ldap3:

https://ldap3.readthedocs.io/en/latest

from ldap3 import Server, Connection, ALL, SUBTREE
import getpass

# Paramètres de connexion
AD_SERVER = 'ldap://mydc.mydomain.com'
AD_USER = 'admin@mydomain.com'  
AD_PASSWORD = getpass.getpass(f'Enter password for {AD_USER}: ')
AD_SEARCH_BASE = 'DC=mydomain,DC=com'

# Connexion au serveur
server = Server(AD_SERVER, get_info=ALL)
conn = Connection(server, user=AD_USER, password=AD_PASSWORD, auto_bind=True)

# Requête LDAP : tous les utilisateurs
search_filter = '(objectClass=user)'
attributes = ['cn', 'mail', 'sAMAccountName']

conn.search(search_base=AD_SEARCH_BASE,
            search_filter=search_filter,
            search_scope=SUBTREE,
            attributes=attributes)

# Affichage des résultats
for entry in conn.entries:
    print(entry.samAccountName)

# Fermeture de la connexion
conn.unbind()

Avant de commencer, assurez-vous d’avoir installé le module ldap3. Vous pouvez l’installer via pip :

pip install ldap3

Détails des objets du script:

conn.entries : liste des résultats de la requête.

Server : permet de spécifier l’adresse du contrôleur de domaine (parametre AD_SERVER)

Connection : initialise une session LDAP, ici avec une authentification user/password

search_filter : définit le type d’objet recherché (user dans notre exemple).

attributes : sélectionne les attributs que l’on souhaite récupérer pour chaque entrée.