Crescendo, inarrêtable Powershell 2/3

Niveau : ★★★★☆

Deuxième partie pour ce sujet avec la mise en application et la création d’une nouvelle commande Powershell.

Le meilleur choix pour « amplifier » une commande est d’en choisir une qui a quelques arguments. Puisque c’est le filtrage de ces arguments qui va venir enrichir la commande Powershell.

Un peu de gymnastique avant de démarrer avec l’installation de Powershell Core 7.3.3 sur Ubuntu puis l’ajout du module Crescendo. Voilà pour la préparation de l’environnement.


Tout se passe directement depuis la machine Linux pour la création du nouveau module de commande. C’est la commande ps (liste des process) qui est choisie pour ce premier exemple simple. Elle ne compte que quelques paramètres, qui ne seront pas utilisés dans cet exemple. Attention, le choix de cette commande n’est pas franchement ce qui se fait de mieux puisqu’elle existe sous la forme d’un alias de commande (voir la partie 1 de l’article). Mais comme elle est simple à porter, ce sera très bien pour un premier exercice.

Objectif donc, une cmdlet qui viendra faire de cette commande Linux une commande Powershell.

Dans sa forme de base, voici à quoi vont ressembler les paramètres du Json :

$parameters = @{
    Verb = 'xxx'
  Noun = 'xxx'
    OriginalName = "xxx"
}

3 paramètres obligatoire. Attention, Verb n’est pas un champ libre mais doit faire partie de la liste des verbes existants. Visual Code ne laissera pas passer … Précaution supplémentaire, il faut respecter la casse (get n’est pas égal à Get).

Autre info, le paramètre OriginalName est en fait le chemin de la commande d’origine.

$parameters = @{
Verb = 'Get'
Noun = 'LinuxProcess'
OriginalName = "ps"
}
New-CrescendoCommand @parameters | Format-List *

C’est le module Crescendo installé plus haut qui va transformer cette liste de paramètres en Json en vue de la préparation de la commande. Une première commande pour charger les param dans une variable, une seconde pour « compiler » et passer tout cela dans le Json. Voilà à quoi ressemble la documentation :

La commande d’origine demande un connecteur -filename que la commande Export-CrescendoCommand ne propose pas (bizarre) …

Ce que je propose ci dessous est une adaptation de la documentation.

$CrescendoCommands = New-CrescendoCommand @parameters

Export-CrescendoCommand -command $CrescendoCommands

Voici le contenu du fichier Json généré par ces deux commandes.

En un peu plus clair :
{
"Verb": "Get",
"Noun": "LinuxProcess",
"OriginalName": "ps",
"Platform": [
"Windows",
"Linux",
"MacOS"
],
"SupportsShouldProcess": false,
"SupportsTransactions": false,
"NoInvocation": false,
"Parameters": [],
"Examples": []
}

Le moins que l’on puisse dire, c’est que c’est assez surprenant pour un Json qui devrait à minima contenir une valeur de schéma ! Comme expliqué ci dessus, la documentation comporte une erreur, j’ouvre une issue sur le Git du projet (c’est une bonne pratique, l’éditeur Microsoft est assez réactif sur le sujet et corrige en général rapidement ce type de coquille de documentation).

Ce qui est écrit dans la doc, c’est que la commande va créér un Json constitué des paramètres + du schéma générique : « $schema »: « https://aka.ms/PowerShell/Crescendo/Schemas/2022-06 »

Donc pour l’instant, dans l’attente de la correction, je propose de … le faire à la main. C’est à dire de compléter le fichier Json par les valeurs manquantes. Pas très pratique pour l’instant, mais cela ne prend que quelques secondes et le fichier généré de cette manière va pouvoir être transformé en module Powershell.

Et de passer à la suite pour créer le module Powershell. Un module est constitué de 2 fichiers, un PSD1 et un PSM1. C’est la commande d’export qui prend en charge cette transformation. Si elle remonte des erreurs, c’est que le Json n’est pas formaté correctement. Le mieux est d’utiliser un éditeur de code (comme Visual Code qui est gratuit : https://code.visualstudio.com/?WT.mc_id=AZ-MVP-5003759) pour vérifier la mise en forme.

{
    "$schema": "https://aka.ms/PowerShell/Crescendo/Schemas/2022-06",
    "Commands": [
        {
            "Verb": "Get",
            "Noun": "linuxprocess",
            "OriginalName": "ps",
            "Platform": [
                "Windows",
                "Linux",
                "MacOS"
            ],
            "OutputHandlers": [],
            "NoInvocation": false,
            "Parameters": [],
            "Examples": []
        }
    ]
}

Export-CrescendoModule -ConfigurationFile get-linuxprocess.crescendo.json -ModuleName get-linuxprocess.psm1

Reste à importer le module et à tester la commande

Import-Module ./get-linuxprocess.psd1

Get-Linuxprocess

Et voilà une nouvelle commande Powershell !

Simple (cela le sera encore plus après la correction de la commande) et efficace ! Voilà qui termine ce second chapitre. Dans la troisième partie, ce sont des paramètres plus avancés et des informations d’aide qui seront traités.

D’ici là, voilà de quoi se faire la main (et quelques convictions) sur Crescendo.

Accélérer l’écriture de ses fichiers Powershell

Lors de la présentation du couple Azure Graph / Powershell à l’occasion d’un Meetup organisé par l’équipe du Worplace Ninja France (https://www.linkedin.com/company/wpninjasfra/) et dont la vidéo est disponible à cet endroit https://www.youtube.com/watch?v=dv6_sxMzch8, l’un des participants a partagé son expérience.

Il a des soucis sur une requête qui remonte quelques milliers d’utilisateurs et qu’il envoi dans un fichier pour exploiter les résultats. Pas d’inquiétude, je n’ai aucun doute sur la vitesse d’obtention des résultats côté Graph / Powershell, mais plutôt un doute sur la façon dont les données sont envoyées dans le fichier.

La commande par défaut côté Powershell est Out-File qui récupère la sortie de commande et ajoute les résultats dans un fichier. Sous cette forme par exemple :

blablamasortie | out-file c:\temp\titimeasure.txt -append

Ça marche, c’est tout à fait utilisable sur de petits volumes de données ou le temps de création du fichier n’est pas un obstacle même s’il prend quelques secondes. Mais qu’en est-il dès qu’il s’agit de plusieurs milliers de lignes ? Pour cela, création d’un fichier de 100 000 lignes avec en complément l’utilisation de la commande Measure-Command (qui donne le temps d’exécution du script).

Voici pour le premier test :

measure-command {
$titi = 1 .. 100000
foreach ($toto in $titi)
{
$toto| out-file c:\temp\titimeasure.txt -append
}}

Résultat moyen avec + de 6 minutes 40 secondes pour écrire les 100 000 lignes dans le fichier txt.

Le second test va s’appuyer sur une autre solution avec la classe StreamWriter (Doc éditeur : https://learn.microsoft.com/en-us/dotnet/api/system.io.streamwriter?view=net-7.0/?WT.mc_id=AZ-MVP-5003759).

measure-command {
$titi = 1 .. 100000
$stream =[System.IO.StreamWriter]::new("C:\temp\titimeasure2.txt")
foreach ($toto in $titi)
{
$stream.WriteLine($toto)
}}
$stream.close()

Même demande donc avec l’envoi dans un fichier de 100 000 lignes avec des valeurs allant de 1 à 100 000.

Et ce n’est pas du tout la même chose !

184 millisecondes !

Alors pour ce genre de besoin, le couple Graph / Powershell sera efficacement épaulé par StreamWriter !