J’ai testé Scaleway, la « Disruptive Cloud Platform »

Dans l’optique de repenser ma plateforme Cloud personnelle et de changer de serveur car désormais surdimensionné par rapport à mes besoins, j’ai essayé Scaleway, qui est une filiale d’Online, qui propose des serveurs virtuels et physiques à des prix plutôt intéressants avec une interface simple et mettant en avant la flexibilité et la rapidité.

L’offre de Scaleway est plutôt simple à comprendre :

  • Les gamme Start, Pro et ARM sont basées sur des serveurs entièrement virtuels, cela signifie que les coeurs et RAM sont en partage avec d’autres utilisateurs sur le même serveur physique ;
  • La gamme BareMetal représente les serveurs physiques dédiés.
J’ai testé la gamme Start avec 2 types de serveurs :  le XS (1 core et 1 Go de RAM, pour 25 Go de disque) et M (4 cores, 4 Go de RAM et 100 Go de disque de base). 
Au niveau des CPU, il s’agit d’Intel Atom C3955 ; peu performants en soi par rapport à ce que l’on peut trouver dans des serveurs mais suffisants pour bien des usages et permettant d’offrir un prix plancher puisque le XS coûte 2,40€ par mois TTC maximum, la facturation étant à l’heure, un serveur qui est détruit dans le mois ne coûte que les heures où il a fonctionné. 
Concernant le stockage, c’est déjà un peu plus flou : il y a la notion de LSSD et de DSSD. Tous les disques chez Scaleway sont des SSD ; la différence entre le L et D SSD réside dans l’emplacement dudit stockage : le LSSD est en fait une allocation disque sur une baie de stockage en réseau, donc non-physiquement présente sur le serveur ou l’hyperviseur hébergeant votre machine, tandis que le DSSD est bel et bien un disque intégré au serveur. Ce qui est dommage, c’est que seul le plus gros serveur BareMetal comprend un DSSD ; pour le reste on est obligé de passer par la case disque réseau. Les performances sont cependant au rendez-vous, mais il faut garder à l’esprit qu’en fonction du noeud sur lequel notre serveur est installé cela peut fluctuer :
Seul le plus petit modèle héberge un LSSD de 25 Go, les autres serveurs offrent au minimum 50 Go de disque, extensibles par tranche de 50 Go, soit sur le même disque, soit en disque supplémentaires si l’on souhaite plusieurs points de montage. Dommage que la partition système ne soit pas extensible et soit figée à 50 Go. Par ailleurs, cela n’est pas précisé mais certains types de serveurs ont une limite de disques rattachables en nombre mais pas en taille ; si 1 To est le maximum de stockage pouvant être accolé à un serveur, au final sur un S on ne peut attacher qu’un disque, sur un M il n’est pas possible de rattacher un troisième disque, et ce qui est quelque peu idiot est qu’une fois un disque rattaché il n’est pas possible d’en augmenter sa taille (quelle raison, étant donné que ce ne sont que des allocations sur une baie ?)…
Le concept de flexibilité chez Scaleway est plutôt important car au final, le serveur n’est qu’un objet au même titre que les IP ou le stockage ou les images de déploiement ; on peut monter ou démonter nos LSSD sur n’importe quel serveur, supprimer un serveur sans perdre le stockage associé, etc. L’interface de gestion est plutôt claire et agréable à utiliser, même si potentiellement déroutante au départ pour qui ne parle pas anglais et/ou est néophyte. Quelque part le prix plancher des offres se paye par le support limité malgré une documentation relativement fournie par rapport aux services offerts.
La gestion d’un serveur est plutôt minimaliste mais a le bon goût d’offrir un accès console, permettant quand même de maniper au cas où le SSH est par terre ou bien que nous sommes connectés depuis une IP habituellement rejetée par SSH. On retrouve le nom du serveur, son emplacement géographique (Paris ou Amsterdam), son IP publique, son IP sur le réseau interne (qui change à chaque fois que vous éteignez ou rallumez votre serveur), et d’autres informations, y compris les disques. Par défaut, la connexion à un serveur se fait avec votre clé SSH renseignée dans votre compte.
La création d’un serveur est plutôt rapide, il doit se passer à tout casser 2 minutes entre le moment où l’on arrive sur la page de création et le moment où celui-ci est effectivement disponible :
On choisit la taille du serveur désiré, la distribution ou application pré-installée (par exemple WordPress, PrestaShop, OpenVPN…) ou encore un déploiement par rapport à une image que vous avez créé vous-même (et non pas un ISO personnalisé…) et c’est parti. Le choix de distributions comprend l’essentiel et se limite au monde GNU/Linux ; la virtualisation se base sur KVM et il n’y a pas de Windows disponibles.
Au niveau réseau, pas de grosse surprise, sur 300 Mbps annoncés :
Lorsqu’on souhaite éteindre un serveur, le stockage est déplacé de la baie de stockage vers des « archives » et le serveur n’est plus accessible immédiatement. Ensuite, lorsqu’on souhaite le rallumer, le stockage redescend vers la baie et les coeurs sont de nouveau alloués ; cependant il arrive que le processus de « rallumage » de serveur prenne du temps car les ressources ne sont pas disponibles ; il convient donc de laisser allumé le serveur 24/7 à moins de vraiment vouloir tirer jusqu’au bout la notion de facturation à l’heure… et pour rendre le serveur indisponible on pourra plutôt couper la patte réseau afin qu’il ne communique plus via la console intégrée.
Actuellement, il ne me reste chez Scaleway que le miroir du blog (basé sur un lighttpd tout simple) ; j’y avais 3 autres serveurs que j’ai fini par bouger ailleurs sur un hyperviseur basé sur Proxmox. En effet, la puissance un peu légère des processeurs pour faire fonctionner Plex et quelques autres applications était problématique, et la gestion du stockage méritait d’être améliorée car en total décalage avec la flexibilité annoncée et plutôt réelle du reste des éléments du cloud. En l’état, Scaleway est une très bonne plateforme pour y monter des serveurs peu critiques ou pour se monter des labos de tests ou encore une petite infrastructure cloud légère ; en gros pour des besoins particuliers. Pour un usage professionnel ou plus sérieux, d’autres fournisseurs me semblent plus adaptés.
Si je devais faire un bilan de mon expérience chez Scaleway en deux semaines :
  • Points forts : tarifs intéressants et à l’heure, rapidité de build d’un serveur, large panel de configurations matérielles, performances disques et réseau suffisantes dans une majorité de situations, flexibilité
  • Points faibles : gestion du stockage, support limité en version de base, interface uniquement en anglais, disponibilité de l’infrastructure lors de la remise en route d’un serveur incertaine.

Quelques billes pour crypter des stations avec BitLocker

Je travaille en ce moment sur BitLocker et après quelques jours de recherches, de tests et d’usure intensive de disques durs, je suis enfin arrivé à un résultat satisfaisant pour obtenir une GPO définissant des paramètres de sécurité rassurants. Je n’ai pas pour prétention d’avoir trouvé la formule magique vous permettant de crypter dès demain en quelques heures 25,000 stations mais ces quelques notes que je vais partager peuvent constituer une bonne base de travail ou d’appréhension de la technologie. Dans mon exemple, je pars sur des postes clients en Windows 10 équipés d’une puce TPM et avec un AD en niveau 2008 R2.

Il s’agit donc de définir proprement la GPO qui sera récupérée par les postes éligibles et ensuite de lancer le cryptage. Tous les paramètres se retrouvent dans l’arborescence suivante : Computer Configuration > Administrative Templates > Windows Components > BitLocker Drive Encryption. Il y a 3 politiques à définir : l’une concernant le disque (au sens logique du terme), la deuxième pour les disques internes et la troisième pour les disques externes.

Concernant les disques hébergeant l’OS :

  • Require additional authentication at startup propose plusieurs réglages et c’est ici que l’on définit ce qui est nécessaire pour continuer le boot du système : la puce TPM simple, un code PIN et la puce TPM, une clef USB avec la puce TPM avec informations de démarrage, ou les 3 à la fois. Attention, un seul de ces 4 paramètres peut être en « requis » (paramètre strict).
  • Si la solution choisie est celle du PIN, il est possible de modifier la longueur minimale de celui-ci dans la très claire propriété Configure minimum PIN length at startup. Par défaut, il ne s’agit que de caractères numériques mais en activant le paramètre dans Allow enhanced PINs for startup, alors les caractères alphanumériques seront autorisés.
  • Dans Enforce Drive encryption type on operating system drive on peut choisir ce qui est crypté sur le disque : l’espace utilisé ou tout le disque. Ce paramètre existe pour les disques locaux non-systèmes.
  • Choose how BitLocker-protected operating system drives can be recovered est un paramètre intéressant car c’est ici que l’on va définir comment récupérer la boulette d’un utilisateur ayant perdu son PIN ou sa clef de déverrouillage. Cela peut se faire soit par un mot de passe de 48 caractères généré aléatoirement lors du cryptage du disque, soit par une clef 256-bits (ou les 2). Ce qui est intéressant ici, c’est que l’on peut stocker dans AD DS ces informations, ce qui fait que l’utilisateur n’a pas à les noter (sous-entendu à écrire la clef de récupération sur un post-it collé sous le laptop ou sur une feuille volante dans la sacoche) et d’un point de vue administrateur, cela permet de tout avoir centré dans un dispositif déjà existant. Afin de stocker ces informations dans l’AD, il y a plusieurs pré-requis, je vous renvoie à cette documentation Microsoft pour plus d’informations. Pour plus de robustesse, il est possible de rendre impossible l’encryption du disque si la sauvegarde des moyens de récupération ne peut pas être effectuée dans l’AD.

Concernant les disques locaux non-système :

  • En fonction de la sécurité à apporter à la station, il est possible de refuser toute écriture sur un disque qui n’est pas crypté par BitLocker dans Deny write access to fixed drives not protected by BitLocker.
  • Les disques non-système ne pouvant évidemment pas demander un PIN, ils peuvent être protégés par un mot de passe. Avec Configure use of passwords for fixed data drives, on définit une politique par rapport à ces mots de passe : longueur et complexité ; la complexité adoptée pour le mot de passe du disque étant la même que celle pour les mots de passe du domaine. Attention à l’éventuel côté pénible d’avoir à se souvenir de « 5847562369 » comme PIN de démarrage et de « @nt1c0n$TitUt10n€II3mEn|- » pour déverrouiller le disque des données…
  • Tout comme les disques système, les moyens de récupération des disques locaux peuvent être stockés dans l’ActiveDirectory grâce aux mêmes réglages offerts dans Choose how BitLocker-protected operating system drives can be recovered.

Concernant les disques amovibles :

  • Une liberté peut être donnée à l’utilisateur de crypter ou non une clef USB ou un disque externe grâce à Control use of BitLocker on removable drives.
  • Et l’on retrouve les mêmes réglages que pour les disques locaux usuelsConfigure use of passwords for fixed data drives et Choose how BitLocker-protected operating system drives can be recovered.

De manière générale :

  • La sauvegarde des mots de passe de récupération et des clefs 256-bits de décryptage s’active grâce au paramètre Store BitLocker recovery information in Active Directory Domain Services. En activant l’option et en demandant de stocker ces informations dans AD DS, la sécurité est augmentée et le travail des administrateurs facilité. Il est également possible de stocker la clef dans un emplacement réseau dans Choose default folder for recovery password.
  • En fonction de l’OS cible, divers algorithmes de cryptages sont disponibles. Avec Windows 10, on peut choisir XTS-AES 256-bit qui semble être le plus sécurisé, avec un inconvénient : cet algorithme n’est pas pris en charge sur les versions inférieures. Les disques internes des stations n’ayant pas pour but d’être démontées et changées de station dans un modèle et avec un OS différent tous les jours, cela ne pose pas de problème en soi ; concernant les périphériques amovibles, la question se pose car il peut être branché sur divers appareils exécutant des OS différents : on peut donc se rabattre sur un algorithme AES-CBC.

Ensuite, côté poste de travail, c’est l’outil manage-bde qui permet d’appliquer le cryptage BitLocker sur les stations ; à noter qu’il faut préalablement que BitLocker ait été activé dans le Panneau de configuration sans quoi manage-bde sera incapable de voir l’intégralité des disques. Il est important de saisir que manage-bde crypte et possède des fonctionnalités basés sur la GPO définie plus haut. Vous ne pourrez par exemple pas crypter la station sans PIN ou ne pas obtenir de mot de passe de récupération si ils sont requis ou crypter avec un algorithme différent de celui renvoyé par la GPO.

Par exemple, si je souhaite crypter mon disque système et qu’un code PIN ainsi que mot de passe de récupération sont nécessaires, cela va se dérouler ainsi dans un cmd en administrateur :

manage-bde -on c: -tpmandpin -rp

Le PIN est demandé après la validation de l’instruction. Dans le cadre d’un disque non-système :

manage-bde -on d: -rp -password

Le mot de passe est demandé après l’envoi de la commande. Bien évidemment, l’outil manage-bde offre d’autres possibilités avec d’autres arguments, je vous renvoie à la documentation de MS à ce sujet.

A noter que l’utilisateur peut ensuite changer son PIN ou le mot de passe de déverrouillage de son disque. D’autres paramètres sont accessibles si l’utilisateur est administrateur du poste.

Une fois le cryptage commencé, on peut retrouver dans l’AD sur les propriétés d’un objet Ordinateur les informations de récupérations définies dans la GPO grâce à l’onglet BitLocker Recovery.

Oui, j’ai réalisé de nombreux tests sur mon poste pilote.

Cet onglet est visible une fois que les outils de management BitLocker ont été déployés sur le contrôleur de domaine (ou sur la machine hébergeant les outils de management).

Powershell : création d’interfaces graphiques – deuxième partie

Après une première partie traitant des bases de la conception d’une UI en Powershell, voici un deuxième article pour aller un peu plus loin. Je vais aborder dans cet article les points suivants :

  • L’ajout d’un menu en haut de la fenêtre grâce au contrôle MenuStrip
  • L’ajout de contrôles GroupBox pour mieux organiser les contrôles
  • L’activation ou désactivation de contrôles
  • L’ajout de bulles d’info au passage de la souris sur les contrôles
Si vous n’avez pas lu le premier article, je vous invite à le faire car je ne vais pas revenir sur les points qui ont été abordés précédemment.

Création d’une barre de menus

Tout d’abord, il s’agit de définir la fenêtre :

$Form = New-Object System.Windows.Forms.Form
$Form.ClientSize = ‘500,500’
$Form.Text = « Mon UI en PS ep. II »
$Form.FormBorderStyle = ‘Fixed3D’
$Form.MaximizeBox = $false

FormBorderStyle réglé sur Fixed3D permet d’éviter à l’utilisateur de prendre le coin de la fenêtre et l’étendre ; ce qui fausserait tout le design de l’interface puisque nous nous basons sur des coordonnées par rapport à une taille de fenêtre définie dans le code. MaximizeBox sur False permet de désactiver le bouton d’agrandissement de la fenêtre. Avec ces simples paramètres, nous barrons déjà la possibilité pour l’utilisateur de nuire à sa propre expérience par deux fois.
Ensuite, nous allons nous servir du contrôle MenuStrip pour définir un menu en haut de notre fenêtre.

$Menu = New-Object System.Windows.Forms.MenuStrip
$Menu.Location = New-Object System.Drawing.Point(0,0)
$Menu.ShowItemToolTips = $True

Le menu est bien ajouté et visible :
Notre fenêtre vide avec son menu en haut. Ne reste qu’à ajouter des éléments sur ce menu.
Pour ajouter des sections dans ce menu, il faut utiliser l’objet ToolStripMenuItem, qu’on va instancier comme n’importe quel autre objet. Avec ce code, je vais donc ajouter une section « Fichier » qui aura un élément « Quitter », et une section « A propos » qui n’aura aucun élément.

$MenuFile = New-Object System.Windows.Forms.ToolStripMenuItem
$MenuFile.Text = « &Fichier »
$MenuAbout = New-Object System.Windows.Forms.ToolStripMenuItem
$MenuAbout.Text = « &A propos »
$MenuFileQuit = New-Object System.Windows.Forms.ToolStripMenuItem
$MenuFileQuit.Text = « &Quitter »

En ajoutant le symbole & au début de chaque titre, je permets au contrôle de réagir par raccourci clavier. Un ALT + F ira donc ouvrir la section « Fichier ». Une fois ces trois objets créés, il faut indiquer que je souhaite avoir mon « Quitter » dans la section « Fichier » ; c’est réalisable avec la méthode DropDownItems.Add :

$MenuFile.DropDownItems.Add($MenuFileQuit)

Et enfin, nous allons rattacher les deux ToolStripMenuItem à notre MenuStrip :

$Menu.Items.AddRange(@($MenuFile,$MenuAbout))

Voici le résultat :
Nous allons rajouter une info-bulle et ajouter un EventHandler sur « Quitter ».

$MenuFileQuit.ToolTipText = « Infobulle d’aide »

$MenuFileQuit.Add_Click({
$Form.Close()
})

Nous obtenons ceci :

Et lorsque nous cliquons sur « Quitter », la fenêtre et le script se terminent.

Mieux organiser ses contrôles avec des GroupBox

Le contrôle GroupBox permet d’encadrer simplement plusieurs contrôles à des fins visuelles. Je vais ajouter à la Form quelques contrôles Label, Button et deux GroupBox pour les séparer. Je vais donc créer un Label qui va afficher le nom de l’utilisateur sur clic du bouton associé et son pendant pour le nom de la machine exécutant le script ; en rangeant tout cela dans des GroupBox. Afin de les définir proprement, il faut spécifier la hauteur, largeur et la légende. A noter qu’il est important de définir les GroupBox après les contrôles que l’on souhaite placer dedans, sans quoi la GroupBox les masquera au lieu de les intégrer.

$LabelLogUser = New-Object System.Windows.Forms.Label
$LabelLogUser.Location = New-Object System.Drawing.Point(40,70)
$LabelLogUser.AutoSize = $true 

$LabelCompName = New-Object System.Windows.Forms.Label
$LabelCompName.Location = New-Object System.Drawing.Point(300,70)
$LabelCompName.AutoSize = $true 

$BoutonGetUser = New-Object System.Windows.Forms.Button
$BoutonGetUser.Location = New-Object System.Drawing.Point(40,150)
$BoutonGetUser.Width = 125
$BoutonGetUser.Text = « Nom user » 

$BoutonGetComp = New-Object System.Windows.Forms.Button
$BoutonGetComp.Location = New-Object System.Drawing.Point(300,150)
$BoutonGetComp.Width = 125
$BoutonGetComp.Text = « Nom station » 

$Form.controls.AddRange(@($LabelLogUser,$LabelCompName))
$Form.controls.AddRange(@($BoutonGetComp,$BoutonGetUser))

$GroupBoxUser = New-Object System.Windows.Forms.GroupBox
$GroupBoxUser.Location = New-Object System.Drawing.Point(20,50)
$GroupBoxUser.Width = 180
$GroupBoxUser.Height = 220
$GroupBoxUser.Text = « User » 

$GroupBoxComp = New-Object System.Windows.Forms.GroupBox
$GroupBoxComp.Location = New-Object System.Drawing.Point(250,50)
$GroupBoxComp.Width = 180
$GroupBoxComp.Height = 220
$GroupBoxComp.Text = « Station »

$Form.controls.AddRange(@($GroupBoxUser,$GroupBoxComp))

Exécutons :

N’ayant pas ajouté de propriété Text à mes Label, ils n’apparaissent donc pas pour l’instant, mais en ajoutant des EventHandlers aux boutons, ils apparaîtront. Je vais coder 2 handlers sur les boutons :

$BoutonGetUser.Add_Click({
$LabelLogUser.Text = $env:username
})

$BoutonGetComp.Add_Click({
$LabelCompName.Text = hostname
})

Et mon Label apparaît suite au clic sur le bouton pour récupérer le nom de la station :

Activation et désactivation de contrôles

Avec ces deux boutons, nous allons pouvoir voir la propriété Enabled qui existe sur beaucoup de contrôles : elle permet de rendre (in)-utilisable ledit contrôle par l’utilisateur. Dans le cadre d’un bouton, il sera grisé. Je vais changer le texte des boutons puis modifier les EventHandlers liés aux boutons pour qu’un clic sur le premier le désactive en activant le second et réciproquement. Par défaut, la propriété Enabled d’un contrôle est à True, ce qui signifie qu’à moins de la passer à False à un moment dans votre code, ce contrôle sera toujours actif.

$BoutonGetUser.Add_Click({
$BoutonGetUser.Enabled = $false
$BoutonGetComp.Enabled = $true
$BoutonGetUser.Text = « Inactif »
$BoutonGetComp.Text = « Actif »
})

$BoutonGetComp.Add_Click({
$BoutonGetComp.Enabled = $false
$BoutonGetUser.Enabled = $true
$BoutonGetComp.Text = « Inactif »
$BoutonGetUser.Text = « Actif »
})

Voyons le résultat :

Ajout de tooltips au passage de la souris

Certains contrôles comme le MenuStrip évoqué plus haut gèrent « nativement » les bulles d’information. D’autres ont besoin d’un objet, simplement nommé ToolTip. L’ajout d’une info-bulle sur un contrôle se fait en deux phases : la première consiste en la création de l’objet et la deuxième du rattachement de cet objet au contrôle en question. Ce snippet va créer deux objets ToolTip et les associer aux boutons.

$BoutonGaucheTooltip = New-Object System.Windows.Forms.ToolTip
$BoutonDroitTooltip = New-Object System.Windows.Forms.ToolTip
$BoutonGaucheTooltip.SetTooltip($BoutonGetUser, »Ce bouton permettait d’afficher le nom de l’utilisateur. »)
$BoutonDroitTooltip.SetTooltip($BoutonGetComp, »Ce bouton permettait d’afficher le nom de la machine. »)

Et le résultat :

Voici donc pour la deuxième partie de ma suite d’articles sur la création d’interfaces graphiques avec Powershell.

Liens

Télécharger le script de l’article
Développement d’une UI en Powershell : première partie – troisième partie
Documentation et référence des classes : MenuStripToolStripMenuItemGroupBox –  ToolTip

Powershell : création d’interfaces graphiques – première partie

Après quelques semaines de développement de ma Lightweight ActiveDirectory Toolbox, je vais commencer à faire une petite série d’articles afin d’expliquer comment développer une interface graphique avec Powershell, en utilisant Windows Forms. Cela est bien plus simple qu’il n’y paraît car nous allons écrire « beaucoup » de code ; une fois la logique acquise tout se fait très vite. J’ai la chance d’avoir fait quelques développements durant mes études et mon temps personnel il y a quelques années en VB.NET et C# avec WinForms, ce qui m’a facilité la tâche pour m’y remettre.

Dans cette première partie, nous allons aborder :

  • La conception d’une fenêtre ;
  • L’ajout de contrôles simples sur une fenêtre : label, button, textbox ;
  • L’ajout de EventsHandlers sur ces contrôles.
Si au début je développais avec Powershell ISE qui est intégré à Windows, j’ai fini par le remettre au placard suite à des freezes incessants et visiblement son incapacité à gérer des scripts qui ont quelques centaines de lignes. Sous Windows, je vous conseille l’excellent Notepad++ qui fera parfaitement le travail une fois le langage Powershell sélectionné en édition.
Quelques explications avant de se lancer, que vous pouvez passer si vous êtes déjà familiers avec WinForms ou le développement .NET en général :
  • Un contrôle est un élément visible de l’interface graphique. Il peut s’agir d’un bouton, d’une étiquette, d’un champ à remplir, etc.
  • Un EventHandler est une procédure qui sera déclenchée en fonction d’une action sur un contrôle : un clic, le passage de la souris, une modification de valeur, etc.
  • Une classe permet de définir un objet. Nous allons faire référence à des classes dans le code, à partir desquelles nous allons créer des objets. Par exemple, la classe Bouton va nous permettre de créer des boutons. Pour simplifier, la classe est le modèle et l’objet ce que nous modifions directement (taille, couleur, effet souhaité, etc.)
  • Il est important de bien nommer les variables : si cela ne pose pas de soucis d’avoir Bouton1, Bouton2 pour quelques lignes, un script ayant 40 ou 50 contrôles deviendra une plaie à écrire si l’on ne sait plus quel contrôle fait référence à Label4.
  • La documentation de Microsoft concernant WinForms est très riche et doit être le premier point de recherche en cas de doute.

Conception de la fenêtre de base

Tout d’abord, il faut savoir que Powershell tout seul ne sait pas créer d’interfaces graphiques avec WinForms. Il faut lui spécifier que nous allons appeler des classes propres à WinForms. En tout début de code, nous ajoutons ceci :

Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()

Puis nous pouvons définir notre toute première fenêtre :

$Form = New-Object System.Windows.Forms.Form

Voilà, notre fenêtre existe. Mais elle n’a ni taille, ni titre, ni propriété aucune… Et elle ne s’affiche même pas ! Corrigeons cela :

$Form.ClientSize = ‘300,300’
$Form.Text = « Mon UI en PS »
$Form.ShowDialog()

A l’exécution, nous obtenons donc ceci :
Une belle fenêtre – vide – avec ses contrôles habituels pour la redimensionner, l’agrandir, la fermer, etc.

Ajout de contrôles supplémentaires

On va l’agrémenter d’un bouton et d’un label (élément de texte non modifiable par l’utilisateur). On reprend donc les mêmes idées que pour notre Form principale, c’est à dire création d’un objet de la classe Button et Label.

$Bouton = New-Object System.Windows.Forms.Button

Mais ceci ne suffira pas. Il nous faut au moins lui indiquer une taille, le texte apposé dessus et surtout… l’endroit où on veut le placer ! Pour définir l’emplacement d’un contrôle, il faut créer un objet avec les coordonnées. Windows dessinera alors le contrôle en partant des paramètres données. Ici, nous souhaitons donc dessiner le bouton à partir de 30 pixels du côté gauche de la fenêtre et 50 pixels du côté haut.

$Bouton.Location = New-Object System.Drawing.Point(30,50)

Et plus simplement, il ne reste plus qu’à lui donner ses attributs : une largeur, une hauteur et le texte écrit dessus.

$Bouton.Width = 80
$Bouton.Height = 40
$Bouton.Text = « Cliquez moi! »

Si vous exécutez maintenant le script, vous ne verrez pas votre bouton. En effet, comment Powershell peut-il savoir que le bouton que vous avez créé doit être affiché sur la Form ? Si vous ne lui dites pas, il peut pas. On va donc gentiment lui indiquer :

$Form.controls.Add($Bouton)

Ce qui va nous donner ceci :

Ajoutons une TextBox qui va servir à saisir du texte, puis une Label.

$TextBox = New-Object System.Windows.Forms.TextBox
$TextBox.Location = New-Object System.Drawing.Point(30,110)
$TextBox.Width = 70
$Label = New-Object System.Windows.Forms.Label
$Label.Location = New-Object System.Drawing.Point(30,150)
$Label.Text = « bonjour »

Etant donné que nous avons plusieurs contrôles désormais sur notre Form, plutôt que d’ajouter un contrôle par instruction, nous allons utiliser la variante AddRange et non Add de $Form.controls.

Ainsi, nous remplaçons $Form.controls.Add($Bouton) par :

$Form.controls.AddRange(@($Bouton,$Label,$Textbox))

Et à l’exécution, tout apparaît :

Attention à bien renseigner les coordonnées des contrôles car si ils se chevauchent, ils peuvent ne plus être visibles et vous faire chercher à comprendre pourquoi ce satané Label ne s’affiche pas alors qu’il est « coincé » sous le bouton…

Tout cela est bien beau, mais n’offre aucune interactivité. Nous allons faire en sorte qu’un clic sur le bouton affiche sur le Label le texte saisi dans notre TextBox.

Ajout d’EventHandlers

C’est ici que le véritable développement Powershell se fait. C’est sur ces EventHandlers que l’on place le code qui est traité par le script. Chaque objet possède ses propres EventHandlers mais pas mal sont communs entre eux, dont le Add_Click que je vais présenter. Celui-ci permet de réagir lorsque l’utilisateur clique sur le contrôle. Nous allons donc écrire un EventHandler sur le bouton :

$Bouton.Add_Click({ })

Et glisser le code entre les accolades. Dans cet exemple, je veux prendre le contenu de la TextBox et l’affiche sur le label. J’obtiens donc simplement ceci :

$Bouton.Add_Click({
Label.Text = TextBox.Text
})

Il est important de toujours garder en dernière ligne l’ouverture de la Form car le script se met en pause de déroulement lorsque la fenêtre est activée (en attente d’un EventHandler, justement). Afin que tout soit bien interprété, on placera nos EventHandler avant.
Sur un clic du bouton, le Label prend le texte indiqué dans la TextBox. Réalisons un deuxième bouton qui viendra écrire la date du jour dans le label lors du clic.

$Bouton2 = New-Object System.Windows.Forms.Button
$Bouton2.Location = New-Object System.Drawing.Point(150,50)
$Bouton2.Width = 80
$Bouton2.Height = 40
$Bouton2.Text = « date du jour, bonjour! »

On ne manquera pas d’ajouter $Bouton2 dans le $Form.Controls.AddRange afin qu’il apparaisse sur la Form. Puis, on écrit son EventHandler :

$Bouton2.Add_Click({
$Label.Text = Get-Date
})

Ce qui nous donne ceci :
Voici donc pour cette première partie qui illustre les bases de la création d’une interface graphique en Powershell grâce à Windows Forms.

Liens

Télécharger le script de l’article
Développement d’une UI en Powershell : deuxième partie – troisième partie
Documentation et référence des classes : FormButtonLabelTextBox