PI Services

Le blog des collaborateurs de PI Services

Powershell : Introduction à la gestion d'événements

Introduction

Le thème abordé est la gestion d'événements sous Powershell. Il s'agit d'une notion peu connue dans ce langage de scripting mais permettant d'interagir automatiquement avec le système si un événement est détecté. Ceux-ci peuvent être : 

  • Le démarrage d'un process
  • Un changement sur un dossier
  • La fin d'un timer
  • Un changement d'état dans un job Powershell
  • ...

Dans un premier, nous verrons comment connaître les objets pouvant être observés et comment gérer les événements via la Cmdlet Register-ObjectEvent, ensuite nous traiterons le cas particulier des événements WMI, puis celui des événements liés à la console Powershell. Enfin, un exemple d'utilisation sera montré (détection des changements dans un dossier).

Avant de débuter, il faut savoir que les événéments gérés, ne le sont que dans la session Powershell en cours (c'est à dire que la remontée d'événements s'arrête dès qu'on quitte la session Powershell). Il existe toutefois une méthode pour créer une gestion d'événements permanente, mais elle ne fonctionne que pour les événements en WMI. Pour faciliter sa mise en place, je vous invite à vous tourner vers l'excellent module PowerEvents : http://powerevents.codeplex.com/.

Les événements des objets Powershell

Pour connaître si un objet Powershell peut être géré via des événements, il suffit d'utiliser la commande Get-Member.

En exécutant la commande suivante :

 

Il est aussi possible de les obtenir via la méthode GetEvents :

 

On obtient le résultat :

image

On remarque que 2 de ces membres sont de type Event (Elapsed et disposed). Ces derniers vont donc pouvoir être utilisé via la cmdlet Register-ObjectEvent.

Exemple pour l’événement Elapsed (fin d’un timer) :

Lorsque l'on lance cette commande, elle crée un job Powershell (similaire à ceux lancés par la commande Start-Job). Dans cet exemple nous voulons monitorer la fin d'un timer pour récupérer les données transmises par un job Powershell à chaque seconde. On crée un objet de type Timer qui a un interval de 1 seconde (en millisecondes). Ce dernier tourne en boucle grâce à l'attribut autoreset. L'événements à surveiller est "Elapsed". On le retrouve dans la commande Register-ObjectEvent avec le paramètre EventName. Le paramètre Action définit le processus exécuté lorsque l'événment est détecté. Il s'agit d'un scriptblock (entre accolades, ici notre variable “ActionTimer”).

Il est possible de passer des données pour qu'elles soient traiter dans l'action. Cela se fait via le paramètre MessageData (N'importe quel type d'objet peut être consommé). Ces données sont ensuite accessible via la variable $event.MessageData. Nous récupérons donc la propriété Id de l'objet Job Powershell afin de recevoir les données via la commande Receive-Job. Pour que l'événement soit remonté , il est obligatoire de fournir l'objet Timer en tant qu'InputObject. Enfin, SourceIdentifier est simplement le nom du job.

Si l'on souhaite arrêter d'observer les événements d'un objet Powershell, il faut utiliser la commande Unregister-ObjectEvent conjointement à Get-EventSubscriber qui récupère l'ensemble des événements observés.

 

Cette commande arrête la remontée de tous les événements. On peut bien entendu filtrer les événements à ne plus gérer via la cmdlet Where-Object par exemple.

Les événements en WMI

En WMI, il existe de nombreuses classes WMI permettant la gestion d'événements. Pour toutes les récupérer et retrouver celle qui vous intéresse, utilisez cette commande :
 

On remarque entre autre des classes pour récupérer :

  • les changements dans les clés de registre
  • les démarrages et arrêts de processus
  • l'arrêt ou la fermeture de session d'un utilisateur
  • l'ajout d'un lecteur (disque dur, clé usb)
  • ...

Pour surveiller un événements en WMI on utilise cette fois la commande Register-WMIEvent.

Exemple de détection de lancement d'un nouveau processus :

La requête effectue une recherche toutes les 2 secondes sur la classe __InstanceCreationEvent. Elle est basé sur le langage WQL dont on peut retrouvé la syntaxe sur le lien suivant :
http://msdn.microsoft.com/en-us/library/windows/desktop/aa394606(v=vs.85).aspx

On retrouve les paramètres sourceIdentifier et Action vues précédemment avec la commande Register-ObjectEvent.

Les événements de la console Powershell

On peux écouter deux événements sur la console Powershell. Lorsqu'elle est fermée (Exiting) et lorsqu'elle est en état Idle (OnIdle). Ainsi, on peut exécuter des actions quand l'un de ces états est atteint. Cette opération s'effectue via la commande Register-EngineEvent :

 

La syntaxe est semblable à celle de la commande Register-ObjectEvent sauf que c'est le paramètre SourceIdentifier qui contient le non de l'événement (on aura donc 2 SourceIdentifier possibles : Powershell.Exiting et Powershell.OnIdle).

NB : Depuis Powershell 3.0, un troisième événement semble exister : WorkflowJobStartEvent. Cependant, il n'est à l'heure actuelle (au 23/05/2014) pas documenté.

Exemple d'utilisation :

Le script commenté ci-dessous déplace les fichiers d'un répertoire à un autre lorsqu'un nouveau fichier apparaît. Un premier événement sur l'objet de type FileSystemWatcher est récupéré : Created. Lors de la remontée de cet événement (à chaque création de fichier) le chemin du fichier est retourné. Un second objet de type Timer est surveillé. A chaque seconde, il récupère les résultats du Job précédent (les chemins de fichiers). Il les ajoute à une liste puis il traîte cette liste en essayant de déplacer les fichiers. Deux résultats sont alors possibles :

  • Si le transfert réussi, alors le chemin initial est supprimé de la liste des fichiers à transférer.
  • Si le déplacement échoue (fichier en cours d'écriture par exemple) alors il retentera de le déplacer au prochain déclenchement de l'événement (après 60 tentatives le transfert n'est plus réalisé).

Les différentes opérations sont logguées dans l'observateur d'événements.

NB : Si le fichier existe déjà à la destination, un horodatage est ajouté en suffixe du nom du fichier.