Powershell: simplifier la création de fonctions avec les bons arguments

Depuis l’arrivée de SharePoint 2010, l’utilisation de PowerShell est devenue presque incontournable. Pour industrialiser certaines procédures, il n’est pas rare de créer des fonctions utilitaires, pour modulariser ses scripts et parfois même étendre la console PowerShell avec des modules personnalisés.

L’article d’aujourd’hui a pour objectif de vous montrer comment créer des fonctions plus simples à utiliser, en jouant avec le typage des paramètres.

Tout d’abord, prenons une fonction « classique », sans enrichissement des paramètres :

[code language= »powershell »]
function Get-ListCount($web){
$web.Lists.Count
}

$web = Get-SPWeb http://localhost
Get-ListCount $web
# résultat : 29 sur mon site de test

[/code]

PREMIER PROBLÈME: LES ARGUMENTS NE SONT PAS VÉRIFIÉS

Vous pouvez passer n’importe quoi à cette fonction, mais sans avoir d’information sur éventuelle erreur. Tout ces appels vont « passer » :

[code language= »powershell »]
Get-ListCount http://localhost
# résultat: rien

Get-ListCount azerty
# résultat: rien

Get-ListCount $null
# résultat: rien

Get-ListCount -web $null
# résultat: rien

[/code]

Toutefois, aucun de ces appels ne va montrer que quelque chose ne va pas.

Modifions notre fonction pour lui ajouter la définition des paramètres et rendre obligatoire le paramètre $web :

[code language= »powershell »]
function Get-ListCount{
param(
[Parameter(Mandatory=$true)]
$web
)
$web.Lists.Count
}
[/code]

Le résultat des mêmes appels que précédement :

[code language= »powershell »]

$web = Get-SPWeb http://localhost
Get-ListCount $web
# résultat : 29 sur mon site de test

Get-ListCount http://localhost
# résultat: rien

Get-ListCount azerty
# résultat: rien

Get-ListCount $null
# résultat: erreur : Get-ListCount : Impossible de lier l’argument au paramètre « web », car il a la valeur Null.

Get-ListCount -web $null
# résultat: erreur : Get-ListCount : Impossible de lier l’argument au paramètre « web », car il a la valeur Null.

[/code]

On progresse. Maintenant, la fonction ne s’exécute pas si $web vaut null.

DEUXIÈME PROBLÈME: LES ARGUMENTS NE SONT PAS TYPÉS

Comme vous l’avez vu, l’argument $web doit être présent. Mais rien n’empêche de passer autre chose qu’un SPWeb !

Modifions encore notre fonction pour ajouter le typage du paramètre :

[code language= »powershell »]
function Get-ListCount{
param(
[Parameter(Mandatory=$true)]
[Microsoft.SharePoint.SPWeb]
$web
)
$web.Lists.Count

}

$web = Get-SPWeb http://localhost
Get-ListCount $web
# résultat : 29 sur mon site de test

Get-ListCount http://localhost
# résultat: erreur : Get-ListCount : Impossible de traiter la transformation d’argument sur le paramètre « web ». Impossible de convertir la valeur « http://localhost » du type « System.String » en type « Microsoft.SharePoint.SPWeb ».
Get-ListCount azerty
# résultat: erreur : Get-ListCount : Impossible de traiter la transformation d’argument sur le paramètre « web ». Impossible de convertir la valeur « azerty » du type « System.String » en type « Microsoft.SharePoint.SPWeb ».
Get-ListCount $null
# résultat: erreur : Get-ListCount : Impossible de lier l’argument au paramètre « web », car il a la valeur Null.

Get-ListCount -web $null
# résultat: erreur : Get-ListCount : Impossible de lier l’argument au paramètre « web », car il a la valeur Null.
[/code]

On est maintenant obligé de passer un objet de type SPWeb pour que la fonction passe

TROISIÈME PROBLÈME: L’UTILISATEUR EST OBLIGÉ D’AVOIR UN OBJET DE TYPE SPWEB

Ce n’est pas vraiment un problème technique, mais une question de confort d’utilisation. L’utilisateur peut vouloir appeler cette fonction avec un objet SPWeb, ou aussi une simple chaîne de caractère contenant l’url. Comme le montre les exemples ci dessus, passer « http://localhost » n’est pas reconnu.

Heureusement Microsoft fourni, pour les principaux objets gérable dans PowerShell des équivalent « *PipeBind ». Par exemple, la classe « Microsoft.SharePoint.PowerShell.SPWebPipeBind » permet de passer différentes formes de réprésentation d’un SPWeb.

En effet, cette classe définit les constructeurs suivants :

[code language= »csharp »]
public SPWebPipeBind(SPWeb instance);
public SPWebPipeBind(Guid guid);
public SPWebPipeBind(string inputString);
public SPWebPipeBind(Uri webUri);
[/code]

On peut en conclure que l’on peut passer soit un objet SPWeb directement, soit un ID, son url sous forme de chaine ou son url sous forme d’Uri.

Notre fonction transformée deviendra donc:

[code language= »powershell »]
function Get-ListCount{
param(
[Parameter(Mandatory=$true)]
[Microsoft.SharePoint.PowerShell.SPWebPipeBind]
$web
)
$actualWeb = $web.Read()
$actualWeb.Lists.Count

}
[/code]

Notez la différence dans le corps de la fonction, notamment l’appel à la méthode « Read() » sur l’objet PipeBind

Avec comme résultats :

[code language= »powershell »]
$web = Get-SPWeb http://localhost

Get-ListCount $web
# résultat : 29 sur mon site de test

Get-ListCount http://localhost
# résultat: 29 sur mon site de test

Get-ListCount azerty
# résultat: erreur : Exception lors de l’appel de « Read » avec « 0 » argument(s) : « Impossible de trouver un objet SPWeb avec Id or Url : azerty. »

[/code]

Cette fois ci, l’appel avec la simple url du site aura fonctionné!

QUATRIÈME PROBLÈME: LA FONCTION NE PEUT ÊTRE UTILISÉE DANS LE PIPE

L’une des principales fonctionnalités de PowerShell est de proposer un pipeline. Aussi, il est naturel de vouloir utiliser la syntaxe suivante :

[code language= »powershell »]
« http://localhost » | Get-ListCount
Get-SPWeb http://localhost | Get-ListCount
[/code]

En l’état, la fonction va demander à l’utilisateur de saisir la valeur de l’argument $web en ignorant totalement le contenu du pipeline.

Modifions encore une fois notre fonction pour extraire du pipeline cet argument :

[code language= »powershell »]
function Get-ListCount{
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[Microsoft.SharePoint.PowerShell.SPWebPipeBind]
$web
)
$actualWeb = $web.Read()
$actualWeb.Lists.Count
}

[/code]

L’attribut « ValueFromPipeLine » indique à PowerShell d’extraire la valeur de l’argument à partir du Pipeline. Le résultat est alors celui escompté :

[code language= »powershell »]
« http://localhost » | Get-ListCount
# résultat : 29 sur mon site de test

Get-SPWeb http://localhost | Get-ListCount
# résultat : 29 sur mon site de test

[/code]

CONCLUSION: DES MEILLEURES FONCTIONS!

Avec un peu d’habitude, vous améliorerez la qualité de vos fonctions. Non pas dans leur corps, mais dans la façon de les appeler. Correctement définir les paramètres permet de déceler certains problèmes en amont (typage, nullité, etc.). Hors, comme vous le savez, plus tôt ça « plante » mieux on se porte 🙂

Share This