API REST en PowerShell – développez avec Polaris !

API REST en PowerShell

Salut à tous, on va se faire un joli morceau aujourd’hui. On va voir comment développer une API REST en PowerShell et du comment coup piloter des scripts via du CURL sur linux, ou avec Invoke-RESTMethod en PowerShell.

Notez que cet article a été rédigé en collaboration avec ArnaudPETITJEAN de PowerShell-Scripting.com. Merci à lui !

C’est quoi une API REST ?

C’est un peu complexe à définir entièrement, d’ailleurs la définition de Wikipédia est floue au possible, mais je pense qu’on peut faire simple en disant que c’est simplement une API (bravo Sherlock… et si vous ne savez pas ce qu’est une API, je pense que c’est pas la peine d’aller plus loin 😉 ). Une API REST s’appelle non pas en important une bibliothèque dans un script (comme en C ou Python par exemple) mais en faisant une simple requête Web HTTP(S). L’action sera exécutée par le serveur qui expose l’API et vous pourrez exploiter le résultat directement dans votre script.

Les API REST sont à la mode depuis quelques années, notamment chez les DevOps. Elles présentent souvent les avantages d’être simples, interopérables, légères et accessibles à distance. Si vous cherchez des alternatives à REST, vous pouvez jeter une œil du côté de SOAP ou RPC. Mais sachez que REST est la tendance actuelle chez les développeurs, et ce pour toutes les raisons que nous venons d’évoquer.

Appeler une API REST en PowerShell

Alors c’est tout simple, vous trouverez des tas et des tas d’exemples sur le net sur le sujet. En gros, la commande Invoke-RestMethod existe depuis PowerShell V3 (avant il fallait passer par le .NET pour faire des requêtes HTTP from scratch, c’était drôle… mais vachement moins direct). Du coup maintenant, c’est tout simple, vous pouvez récupérer la météo en cours à Miami avec deux misérables appels à l’API REST de metaweather.com :

$city= "Paris"
#récupérer l'ID de la ville
$info = Invoke-RestMethod -URI ("https://www.metaweather.com/api/location/search/?query=" + $city)
#récupérer la météo
Invoke-RestMethod -URI ("https://www.metaweather.com/api/location/" + $info.woeid)

Résultats :

consolidated_weather : {@{id=579552; weather_state_name=Heavy Rain; weather_...}
time             : 2019-11-29T10:27:48.904180+01:00
sun_rise         : 2019-11-29T08:19:32.259577+01:00
sun_set          : 2019-11-29T16:57:45.517950+01:00
timezone_name    : LMT
parent           : @{title=France; location_type=Country; woeid=23424819; ...}
sources          : {@{title=BBC; slug=bbc; url=http://www.bbc.co.uk/weathe...}
title            : Paris
location_type    : City
woeid            : 615702
latt_long        : 48.856930,2.341200timezone         : Europe/Paris

Et c’est tout ! Vous allez récupérer le temps qu’il fait à Paris sous forme d’un objet PowerShell personnalisé de type [PSCustomObject] et vous n’aurez donc plus qu’à exploiter ses propriétés comme vous le souhaitez.

Si vous voulez un exemple, en cas réel, d’appel à une API REST : on en avait déjà fait un sur le site, je vous invite à jeter un œil à l’article suivant.

Développer une API REST en PowerShell V0: HttpListener 

Alors autant pour vous dire comment faire pour appeler (ou consommer dans le jargon) l’API : il y a du monde. Mais construire sa propre API REST, là on trouve déjà moins d’exemples ! Mais on en a un qui n’est pas si mal.

… Et merci à Kamal Farmer d’avoir partagé ça 

La mécanique proposée est « assez simple » puisqu’il s’agit de monter un serveur HTTP via l’objet .NET HttpListener (donc en fait un gros équivalent des modules SimpleHTTPServer ou http.server en Python 2 et 3 respectivement) et de gérer dans le code les URLs demandées à grand coup de « match » et de « if »…

C’est un poil bourrin, mais ça :

  • tient en 50 lignes ;
  • fait le job ;
  • devrait fonctionner dans vieilles versions de PowerShell.

Développer une API REST en PowerShell – V1: Polaris

On a vu la méthode des barbus quand on n’a rien sous la main, maintenant quand on peut installer les modules PowerShell que l’on veut sur un serveur. Il existe un module open source nommé Polaris qui permet de faire ça, que vous trouverez aussi sur la PowershellGallery.

Pour l’installer quand vous êtes connecté, ce n’est pas trop dur :

 Install-Module -Name Polaris 

En revanche, si vous êtes « moins connecté », il vous faudra télécharger et installer manuellement le module.

Une fois le module installé sur votre machine, exécutez le script PowerShell suivant vous montera une API Rest sur le port 8000 de votre machine en 3 seconde 30 :

#requires -module Polaris
New-PolarisGetRoute -Path /randomPassword -Scriptblock {
    $passwd = ""
    1..12 | ForEach {$passwd += [char](Get-Random -Minimum 33 -Maximum 127)} # doc
    $resp = [PSCustomObject]@{
        Password = $passwd
    } 
    $jsonResp =  $resp | ConvertTo-Json
    $Response.Send($jsonResp)
} 
Start-Polaris -Port 8000 -MinRunspaces 5 -Verbose

WARNING :

Faites attention de bien appeler votre service REST à partir d’une autre console PowerShell que celle qui vous a servi à le démarrer sous peine d’être confronté à l’échec !

Et donc si vous appelez la cmdlet suivante dans une autre console PowerShell :

Invoke-RestMethod  -URI "http://<votre.serveur.tld>:8000/randomPassword
Password
--------
P4vgsvZAdDUb

Ça marche même dans le navigateur si vous saisissez l’URL…

Et si je veux passer des paramètres ?

Fastoche, vous avez deux moyens de faire ça. Le premier consiste à passer la valeur dans l’URL après le nom de la ressource suivi du slash, exemple :

http://<votre.serveur.tld>:8443/randomPassword/16

Et du coup vous devrez adapter votre code serveur comme ça (src):

New-PolarisGetRoute -Path "/randomPassword/:size?" -Scriptblock {
    $size = 12
    if($Request.Parameters.size){
        $size = [int]$Request.Parameters.size 
    }
    $passwd = ""
    1.. $size | ForEach {$passwd += [char](Get-Random -Minimum 33 -Maximum 127)}
    [...]
}

L’autre option consiste à définir un « query parameter » et il faut modifier le code pour qu’il ressemble à ça :

New-PolarisGetRoute -Path "/randomPassword" -Scriptblock {
    $size = 12
    if($Request.Query['size']){
        $size = [int]$Request.Query['size'] 
    }
    $passwd = ""
    1.. $size | ForEach {$passwd += [char](Get-Random -Minimum 33 -Maximum 127)}
    [...]
}

Et cette fois, il faut appeler votre API comme ça :

https://<votre.serveur.tld>:8000/randomPassword?size=24

Elle est pas belle la vie ? Nous ne dirons pas laquelle des deux techniques est la meilleure car nous ne sommes pas des développeurs. Vous trouverez pléthore d’informations sur le net au sujet des bests practices de design des APIs REST.

Nan, moi je veux que le service il soit chiffré et authentifié…

Genre on vous a mal habitués ? Polaris propose différents modes d’authentification tous documentés, ce qui vous permet d’avoir nativement les modes :

  • Basic ;
  • Digest ; 
  • IntegratedWindowsAuthentication ;
  • Negotiate ;
  • NTLM.

Bon basic et digest, je passe c’est du classique en HTTP. Les 3 suivants sont classiques en environnement Windows Active Directory, et ne méritent pas que qu’on s’y attarde ici.

Côté HTTPS, c’est moins top mais ça se fait : Polaris ne permet pas, actuellement, de spécifier quel certificat utiliser et il utilise le certificat Machine Windows par défaut (le plus souvent autosigné…) Bref, ce n’est pas le top mais vous serez quand même chiffré. Néanmoins, si vous avez absolument besoin de certificat spécifique pour votre service ça se bricole pas si mal comme on peut le voir ici ou en modifiant le code source du module

J’aime Pode abord 

Vous n’aimez pas Polaris ? Pas de soucis, il existe Pode aussi, disponible sur la PowerShellGallery lui-aussi, qui lui gère les certificats pré-installés nativement, mais dont l’authentification est un bête binding LDAP et qui du coup est moins clean que Polaris.

Si vous recherchez une solution « entreprise-ready » nous vous invitons à regarder du côté de PowerShell Universal Dashboard (dont il existe une version communautaire gratuite et une version entreprise). Nous vous ferons l’article de ce produit dans un prochain billet.

Conclusion

Voilà, nous n’allons plus nous étendre là dessus pour aujourd’hui et nous vous laissons explorer tout ça par vous mêmes. Nous avons quelques autres projets en tête ou il va falloir développer des API REST en PowerShell. Nous allons donc en reparler à l’occasion. D’ici là, geekez bien !

4 commentaires on “API REST en PowerShell – développez avec Polaris !

  1. Bonjour Etienne,

    Merci pour tes explications très clair. J’avais testé cette méthode également avec la méthode des Invoke-Command, mais pas de argumentList.
    Mais ta méthode fonctionne, j’ai du dans ma fougue de tester une solution me planter dans le code.

    Merci pour ton blog, qui m’a été très utile.
    Fanch

  2. Bonjour Fanch,

    Les portée des scriptblock sont est effectivement un des « problèmes » de Polaris (on va carrément dire limitation, hein). On peut tout de même contourner partiellement ce problème en faisant charger au début des scriptblock des fichiers externes et partagés entre les scriptblock, ce qui permet d’avoir une bibliothèque commune (mais pas de partager un runspace en mémoire dans les scriptblock).
    Exemple :
    . .\lib\MyModule.ps1 #ou en tout cas ça fonctionnait lors de mes tests.
    C’est pas super performant, mais c’est fonctionnel sur des petits dev. 🙂

    Le concurrent Pode à une manière un peu plus élégante de gérer ça avec la cmdlet Import-PodeModule
    mais en contrepartie d’autres limitations : notamment au niveau de l’authentification qui est quand même moins beaucoup moins clean que dans Polaris.

    La solution qui met tout le monde d’accord c’est Powershell Universal Dashboard qu’on tente au boulot en ce moment pour le coup, mais en prod c’est une solution payante, même si elle reste carrément abordable en entreprise.

    Voilà, désolé : pas de solution magique mais quelques pistes quand même. J’espère que ça t’aide, et je reste disponible si besoin.

    @+

  3. Bonjour,
    Je ne comprends pas un point et l’intérêt d’une chose. Les appels de traitement des requêtes API se font via des scriptblock. Mais comment fait on pour par exemple suite à POST faire un appel à une fonction qui se trouve a l’extérieur pour par exemple connexion à une base de données exécution d’une requête.
    Car le ScriptBlock a son propre context.

    Merci d’avance de ton aide.
    Fanch

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.