Crescendo, inarrêtable Powershell 3/3

Niveau : ★★★★☆

3 ème et dernière partie pour cet article sur Powershell Crescendo.
Partie 1 : https://thierrybtblog.wordpress.com/2023/02/24/crescendo-inarretable-powershell-1-2/
Partie 2 : https://thierrybtblog.wordpress.com/2023/03/17/crescendo-inarretable-powershell-2-3/

Si dans la seconde partie, la commande choisie présentait peu de paramètres, c’est une commande plus complète qui est utilisée dans cette dernière partie. Je n’ai pas trouvé mieux que l’outil Azcmagent (pour AzureConnectedMachineAgent) qui est présenté dans la doc officielle Powershell Crescendo (https://learn.microsoft.com/fr-fr/powershell/utility-modules/crescendo/get-started/research-tool?view=ps-modules/?WT.mc_id=AZ-MVP-5003759).

Voici quelques exemples un peu plus avancés pour « porter » cette commande et en faire une nouvelle commande Powershell. Avec des sous commandes sans paramètres puis des sous commandes avec paramètres.

Une fois l’outil installé, il est lancé dans sa version originale pour afficher les commandes dont il dispose.

Voilà un outil très intéressant puisqu’il propose un ensemble de commandes (Check, Config, Connect …etc). Et que ces commandes sont exploitées pour certaines avec des paramètres de sous commandes.

Je ne reviens pas sur la façon dont est traitée la commande pour être transformée en Applet Powershell, c’est à revoir dans la partie deux de l’article.

L’outil Azcmagent est plus complet que la commande PS de la partie précédente, il est donc possible d’aller plus loin.
Le fichier Json qui a permis de générer le nouveau module est disponible dans la documentation à cette adresse : https://learn.microsoft.com/fr-fr/powershell/utility-modules/crescendo/get-started/create-new-cmdlet?view=ps-modules#completing-the-crescendo-command-configuration/?WT.mc_id=AZ-MVP-5003759
Cette page est utilisée pour tous les exemples de cette 3 ème partie.
Attention, le Json fourni pour la création du premier module comporte une coquille, le code ne fonctionnera pas en l’état. Il faut retirer la « , » en jaune qui ferme le bloc Platform.



Et pour rappel, toutes les commandes de création se feront dans une fenêtre avec élévation de privilèges, sans cela, la création du module fonctionne mais avec des erreurs qui empêcheront de faire fonctionner le module.

Cette nouvelle commande affiche donc les informations demandées. J’utilise ici sur un poste de travail, il n’y a donc rien de concret à afficher au travers d’un agent qui n’est pas présent. Cela ne change rien à l’esprit de la démo.


Le module suivant est encore un peu plus complet puisqu’il comporte des paramètres de sous commandes et c’est donc une nouvelle fois le code de la documentation qui est utilisé.

Cette partie de paramètres est en jaune dans l’exemple.

Une remarque, il y a deux Get dans ce fichier de configuration.
Mais ce sont deux choses très très différentes. Ci-dessus, en rouge, le verbe utilisé par Powershell, en jaune, le paramètre de la commande originale qu’il faut convertir en paramètre Powershell.
Pour bien comprendre cette différence, retour à la commande de départ, non Powershell, azcmagent.exe config --help.

C’est donc bien le paramètre get de la commande azcmagent.exe config qui va être porté sur Powershell.

Il faut utiliser le Json de la doc, mais … attention, il y a … une nouvelle coquille dans cette doc 😉
Le bloc Parameters est déclaré deux fois ce qui n’est pas possible… Il faut retirer dans le Json la partie en rouge.

Puis créer et importer ce nouveau module avec paramètre Get. Il est rendu obligatoire dans le Json ("Mandatory": true,) et attend une propriété lorsqu’il est utilisé.

Reste à renseigner un propriété (ici proxy.url) pour avoir un retour pour cette toute nouvelle commande Powershell.

Il reste un petit soucis d’affichage pour lequel je ne passe pas trop de temps à trouver de quoi il vient. Peut-être la langue Française ou ma version de Powershell.

Dernière modification pour le Json, l’ajout d’exemples dans la commande d’aide. Cette partie n’est pas traitée dans la doc. Je trouve que cet ajout est particulièrement intéressant et peut simplifier l’utilisation de commandes complexes ou mal documentées. Je vais même aller plus loin, je pense que cet ajout peut même justifier de porter certaines commandes uniquement pour cette raison !
Voici le code à ajouter avant de supprimer et de régénérer le module (suppression des fichiers PSM1 et PSD1).

"Examples": [
                {
                "Command": "Get-AzCmAgentConfigProperty -Property proxy.url",
                "Description": "Affiche la propriétés proxy.url",
                "OriginalCommand": "azcmagent.exe config get proxy.url"
                }
            ]

Puis voici la façon dont cette aide est appelée par la commande Powershell. Rien de spécifique ici, toutes les commandes d’aide Powershell se traite de la même façon, l’ajout du paramètre -Examples filtre le retour d’affichage.

Get-Help Get-AzCmAgentConfigProperty -Examples

Voilà qui termine cette troisième partie de documentation Powershell Crescendo. Encore un peu jeune avec notamment quelques coquilles de documentation, le module peut se révéler utile dans certains cas.
Si vous avez des exemples ou des remarques sur cette série d’articles, n’hésitez pas à commenter pour venir discuter de ce sujet !

PS : Pour un schéma exhaustif du Crescendo, direction le Git du projet à cette adresse : https://github.com/PowerShell/Crescendo/blob/master/Microsoft.PowerShell.Crescendo/schemas/2022-06/?WT.mc_id=AZ-MVP-5003759

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 !