Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Les lignes uniques PowerShell et les scripts qui doivent souvent être modifiés sont de bons candidats pour être transformés en fonctions réutilisables.
Écrivez des fonctions dès que vous le pouvez, car elles sont davantage orientées outils. Vous pouvez ajouter les fonctions à un module de script, placer ce module à un endroit défini dans le $env:PSModulePath
et appeler les fonctions sans avoir à localiser l’endroit où les fonctions ont été enregistrées. Avec le module PowerShellGet, vous pouvez facilement partager vos modules PowerShell dans un référentiel NuGet.
PowerShellGet est fourni avec PowerShell version 5.0 et versions ultérieures. Il est également disponible en téléchargement pour PowerShell version 3.0 et versions ultérieures.
Ne compliquez pas les choses. Privilégiez la simplicité et choisissez le moyen le plus rapide d'effectuer une tâche. N’utilisez pas d’alias ni de paramètres positionnels dans du code que vous comptez réutiliser. Mettez en forme votre code dans un souci d’en faciliter la lecture. Ne codez pas les valeurs en dur, utilisez des paramètres et des variables. N’écrivez pas de code inutile, même s’il n’a aucun effet gênant. Cela rend les choses plus complexes inutilement. L’attention aux détails est très importante lors de l’écriture d’un code PowerShell.
Nomination
Quand vous nommez vos fonctions dans PowerShell, utilisez un nom casse Pascal avec un verbe approuvé et un nom au singulier. Pour obtenir la liste des verbes approuvés dans PowerShell, exécutez Get-Verb
. Dans l’exemple suivant, les résultats de Get-Verb
sont triés par la propriété Verbe.
Get-Verb | Sort-Object -Property Verb
La propriété Group vous donne une idée de la manière dont les verbes sont censés être utilisés.
Verb Group
---- -----
Add Common
Approve Lifecycle
Assert Lifecycle
Backup Data
Block Security
Checkpoint Data
Clear Common
Close Common
Compare Data
Complete Lifecycle
Compress Data
Confirm Lifecycle
Connect Communications
Convert Data
ConvertFrom Data
ConvertTo Data
Copy Common
Debug Diagnostic
Deny Lifecycle
Disable Lifecycle
Disconnect Communications
Dismount Data
Edit Data
Enable Lifecycle
Enter Common
Exit Common
Expand Data
Export Data
Find Common
Format Common
Get Common
Grant Security
Group Data
Hide Common
Import Data
Initialize Data
Install Lifecycle
Invoke Lifecycle
Join Common
Limit Data
Lock Common
Measure Diagnostic
Merge Data
Mount Data
Move Common
New Common
Open Common
Optimize Common
Out Data
Ping Diagnostic
Pop Common
Protect Security
Publish Data
Push Common
Read Communications
Receive Communications
Redo Common
Register Lifecycle
Remove Common
Rename Common
Repair Diagnostic
Request Lifecycle
Reset Common
Resize Common
Resolve Diagnostic
Restart Lifecycle
Restore Data
Resume Lifecycle
Revoke Security
Save Data
Search Common
Select Common
Send Communications
Set Common
Show Common
Skip Common
Split Common
Start Lifecycle
Step Common
Stop Lifecycle
Submit Lifecycle
Suspend Lifecycle
Switch Common
Sync Data
Test Diagnostic
Trace Diagnostic
Unblock Security
Undo Common
Uninstall Lifecycle
Unlock Common
Unprotect Security
Unpublish Data
Unregister Lifecycle
Update Data
Use Other
Wait Lifecycle
Watch Common
Write Communications
Il est important d'utiliser un verbe approuvé pour vos fonctions PowerShell. Les modules qui contiennent des fonctions avec des verbes non approuvés génèrent un message d’avertissement lorsqu’ils sont importés dans une session PowerShell. Ce message d’avertissement donne une image peu professionnelle à vos fonctions. De plus, les verbes non approuvés rendent vos fonctions plus difficiles à détecter.
Une fonction simple
Dans PowerShell, une fonction est déclarée avec le mot-clé function, suivi du nom de la fonction et d'une accolade ouvrante et fermante ({ }
). Le code exécuté par la fonction est contenu dans ces accolades.
function Get-Version {
$PSVersionTable.PSVersion
}
La fonction illustrée dans l’exemple suivant est un exemple simple. Elle renvoie la version de PowerShell.
Get-Version
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 693
Lorsque vous utilisez un nom générique pour vos fonctions, comme Get-Version
, des conflits de nommage peuvent survenir. De futures commandes par défaut ou les commandes d’autres utilisateurs peuvent entrer en conflit avec ces noms. Préfixez la partie nom de vos noms de fonction pour éviter les conflits de noms. Par exemple : <ApprovedVerb>-<Prefix><SingularNoun>
.
Dans l’exemple suivant, le préfixe PS
est utilisé.
function Get-PSVersion {
$PSVersionTable.PSVersion
}
En dehors du nom, cette fonction est identique à la précédente.
Get-PSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 693
Même en ajoutant un préfixe, il existe toujours un risque de conflit. J'aime préfixer mes noms de fonctions avec mes initiales. Établissez une convention et respectez-la.
function Get-MrPSVersion {
$PSVersionTable.PSVersion
}
Cette fonction n’est pas différente des deux précédentes, sauf qu'elle utilise un nom plus original pour éviter les conflits de nommage avec d’autres commandes PowerShell.
Get-MrPSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 693
Une fois les fonctions chargées en mémoire, vous les voyez sur le PSDrive Function.
Get-ChildItem -Path Function:\Get-*Version
CommandType Name Version
----------- ---- -------
Function Get-Version
Function Get-PSVersion
Function Get-MrPSVersion
Si vous souhaitez supprimer ces fonctions de votre session active, supprimez-les du PSDrive Function ou fermez et rouvrez PowerShell.
Get-ChildItem -Path Function:\Get-*Version | Remove-Item
Vérifiez que les fonctions ont bien été supprimées.
Get-ChildItem -Path Function:\Get-*Version
Si les fonctions ont été chargées dans un module, vous pouvez retirer le module pour les supprimer.
Remove-Module -Name <ModuleName>
L’applet de commande Remove-Module
supprime les modules PowerShell de la mémoire de votre session PowerShell active. Il ne les supprime pas de votre système ou de votre disque.
Paramètres
N’attribuez pas de valeurs de manière statique. Utilisez plutôt des paramètres et des variables. Lorsque vous nommez vos paramètres, utilisez dans la mesure du possible le nom des applets de commande par défaut pour vos noms de paramètres.
Dans la fonction suivante, vous remarquerez que j’ai utiliséComputerName et non Computer, ServerName, ou Host pour le nom du paramètre. L'utilisation de ComputerName normalise le nom du paramètre pour qu'il corresponde au nom du paramètre et à la casse comme les cmdlets par défaut.
function Test-MrParameter {
param (
$ComputerName
)
Write-Output $ComputerName
}
La fonction suivante interroge toutes les commandes de votre système et renvoie leur nombre avec les noms de paramètres spécifiques.
function Get-MrParameterCount {
param (
[string[]]$ParameterName
)
foreach ($Parameter in $ParameterName) {
$Results = Get-Command -ParameterName $Parameter -ErrorAction SilentlyContinue
[pscustomobject]@{
ParameterName = $Parameter
NumberOfCmdlets = $Results.Count
}
}
}
Comme vous pouvez le voir dans les résultats suivants, 39 commandes ont un paramètre ComputerName. Il n’y a pas de commande avec les paramètres Computer, ServerName, Host ou Machine.
Get-MrParameterCount -ParameterName ComputerName, Computer, ServerName,
Host, Machine
ParameterName NumberOfCmdlets
------------- ---------------
ComputerName 39
Computer 0
ServerName 0
Host 0
Machine 0
Utilisez le même format de majuscules/minuscules pour vos noms de paramètres que pour les cmdlets par défaut. Par exemple, utilisez ComputerName
et non computername
. Cette convention de dénomination aide les personnes familiarisées avec PowerShell à découvrir vos fonctions et leur donner l'apparence et le comportement des applets de commande par défaut.
L’instruction param
vous permet de définir un ou plusieurs paramètres. Une virgule (,
) sépare les définitions de paramètres. Pour plus d’informations, consultez about_Functions_Advanced_Parameters.
Fonctions avancées
Il n'est pas difficile de transformer une fonction en fonction avancée dans PowerShell. L’une des différences entre une fonction et une fonction avancée tient au fait que des paramètres courants sont automatiquement ajoutés à cette dernière. Les paramètres courants comprennent des paramètres tels que Verbose et Debug.
Commencez par la fonction Test-MrParameter
qui a été utilisée dans la section précédente.
function Test-MrParameter {
param (
$ComputerName
)
Write-Output $ComputerName
}
Il existe plusieurs façons de voir les paramètres courants. L'un d'eux consiste à afficher la syntaxe à l'aide de Get-Command
.
Get-Command -Name Test-MrParameter -Syntax
Veuillez noter que la fonction Test-MrParameter
n’a aucun paramètre commun.
Test-MrParameter [[-ComputerName] <Object>]
Une autre consiste à parcourir la propriété des paramètres de Get-Command
.
(Get-Command -Name Test-MrParameter).Parameters.Keys
ComputerName
Ajoutez l'attribut CmdletBinding
pour transformer la fonction en fonction avancée.
function Test-MrCmdletBinding {
[CmdletBinding()] # Turns a regular function into an advanced function
param (
$ComputerName
)
Write-Output $ComputerName
}
Lorsque vous spécifiez CmdletBinding
, les paramètres communs sont automatiquement ajoutés.
CmdletBinding
requiert un bloc param
, mais le bloc param
peut être vide.
Get-Command -Name Test-MrCmdletBinding -Syntax
Test-MrCmdletBinding [[-ComputerName] <Object>] [<CommonParameters>]
L'examen de la propriété des paramètres de Get-Command
affiche les noms de paramètres réels, y compris les plus courants.
(Get-Command -Name Test-MrCmdletBinding).Parameters.Keys
ComputerName
Verbose
Debug
ErrorAction
WarningAction
InformationAction
ErrorVariable
WarningVariable
InformationVariable
OutVariable
OutBuffer
PipelineVariable
Prend en charge ShouldProcess
L’attribut SupportsShouldProcess
ajoute les paramètres d’atténuation des risques WhatIf et Confirm. Ces paramètres sont nécessaires uniquement pour les commandes de modification.
function Test-MrSupportsShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param (
$ComputerName
)
Write-Output $ComputerName
}
Notez qu’il existe maintenant les paramètres WhatIf et Confirm.
Get-Command -Name Test-MrSupportsShouldProcess -Syntax
Test-MrSupportsShouldProcess [[-ComputerName] <Object>] [-WhatIf] [-Confirm]
[<CommonParameters>]
Ici encore, vous pouvez utiliser Get-Command
pour renvoyer une liste des noms de paramètres réels, avec ceux des paramètres courants plus WhatIf et Confirm.
(Get-Command -Name Test-MrSupportsShouldProcess).Parameters.Keys
ComputerName
Verbose
Debug
ErrorAction
WarningAction
InformationAction
ErrorVariable
WarningVariable
InformationVariable
OutVariable
OutBuffer
PipelineVariable
WhatIf
Confirm
Validation des paramètres
La validation de l’entrée doit se faire très tôt. Ne permettez pas à votre code de continuer sur un chemin d’accès lorsqu’il ne peut pas se terminer sans entrée valide.
Spécifiez toujours un type de données pour les variables utilisées pour les paramètres. Dans l'exemple suivant, String est spécifié comme type de données pour le paramètre ComputerName. Cette validation le contraint à n'autoriser qu'un seul nom d’ordinateur unique pour le paramètre ComputerName.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[string]$ComputerName
)
Write-Output $ComputerName
}
Une erreur est générée si plusieurs noms d’ordinateurs sont spécifiés.
Test-MrParameterValidation -ComputerName Server01, Server02
Test-MrParameterValidation : Cannot process argument transformation on
parameter 'ComputerName'. Cannot convert value to type System.String.
At line:1 char:42
+ Test-MrParameterValidation -ComputerName Server01, Server02
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Test-MrParameterValidation]
, ParameterBindingArgumentTransformationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Test-MrP
arameterValidation
Le problème avec la définition actuelle est qu’il est possible d’omettre la valeur du paramètre ComputerName, alors qu’une valeur est attendue pour que la fonction puisse s’exécuter. Il s'agit d'un cas pour lequel l’attribut de paramètre Mandatory
est utile.
La syntaxe utilisée dans l’exemple suivant est compatible avec PowerShell 3.0 et versions ultérieures.
[Parameter(Mandatory=$true)]
doit être utilisé pour rendre la fonction compatible avec PowerShell version 2.0 ou versions ultérieures.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]$ComputerName
)
Write-Output $ComputerName
}
Maintenant que le nom d'ordinateur est requis, s'il n'est pas spécifié, la fonction en demande un.
Test-MrParameterValidation
cmdlet Test-MrParameterValidation at command pipeline position 1
Supply values for the following parameters:
ComputerName:
Si vous voulez autoriser plusieurs valeurs pour le paramètre ComputerName, choisissez le type de données String, mais ajoutez des crochets ([]
) pour autoriser un tableau de chaînes.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string[]]$ComputerName
)
Write-Output $ComputerName
}
Vous voudrez peut-être spécifier une valeur par défaut à utiliser pour le paramètre ComputerName quand aucune valeur n’est spécifiée.
Le problème est que l’utilisation de valeurs par défaut n’est pas possible avec des paramètres obligatoires. À la place, utilisez l’attribut de validation de paramètre ValidateNotNullOrEmpty
avec une valeur par défaut.
Même si vous définissez une valeur par défaut, évitez d’utiliser des valeurs statiques. Dans l’exemple suivant, $env:COMPUTERNAME
est utilisé comme valeur par défaut, qui est automatiquement remplacée par le nom de l’ordinateur local si aucune valeur n’est fournie.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
Write-Output $ComputerName
}
Sortie détaillée.
Les commentaires inline sont utiles si vous écrivez du code complexe, mais que les utilisateurs ne les voient pas, sauf s’ils examinent le code.
Dans l’exemple suivant, la fonction contient un commentaire inline dans la boucle foreach
. Ce commentaire particulier n’est pas difficile à localiser, mais imaginez la même situation avec une fonction contenant des centaines de lignes de code.
function Test-MrVerboseOutput {
[CmdletBinding()]
param (
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
foreach ($Computer in $ComputerName) {
#Attempting to perform an action on $Computer <<-- Don't use
#inline comments like this, use write verbose instead.
Write-Output $Computer
}
}
Il est préférable d’utiliser la fonction Write-Verbose
au lieu de commentaires inline.
function Test-MrVerboseOutput {
[CmdletBinding()]
param (
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
foreach ($Computer in $ComputerName) {
Write-Verbose -Message "Attempting to perform an action on $Computer"
Write-Output $Computer
}
}
La sortie verbeuse n'est pas affichée lorsque la fonction est appelée sans le paramètre Verbose.
Test-MrVerboseOutput -ComputerName Server01, Server02
La sortie détaillée est affichée lorsque la fonction est appelée avec le paramètre Verbose.
Test-MrVerboseOutput -ComputerName Server01, Server02 -Verbose
Entrée pipeline
Un code supplémentaire est nécessaire lorsque vous voulez que votre fonction accepte l’entrée de pipeline. Comme mentionné plus haut dans ce livre, les commandes peuvent accepter l’entrée de pipeline par valeur (par type) ou par nom de propriété. Vous pouvez écrire vos fonctions comme des commandes natives pour qu’elles acceptent l’un de ces types d’entrée ou les deux.
Pour accepter une entrée de pipeline par valeur, spécifiez l'attribut de paramètre ValueFromPipeline
pour ce paramètre particulier. Vous ne pouvez accepter une entrée pipeline par valeur qu'à partir d'un seul paramètre de chaque type de données. Si vous avez deux paramètres qui acceptent des chaînes de caractères, un seul d'entre eux peut accepter une entrée pipeline par valeur. Si vous avez spécifié par valeur pour les deux paramètres de chaîne, l’entrée ne sait pas quel paramètre associer. Ce scénario est une autre raison pour laquelle j'appelle ce type d'entrée de pipeline par type plutôt que par valeur.
L'entrée par pipeline est reçue un élément à la fois, de la même manière que les éléments sont traités dans une boucle foreach
.
Un bloc process
est nécessaire pour traiter chaque élément si votre fonction accepte un tableau comme entrée. Si votre fonction n'accepte qu'une valeur simple comme entrée, un bloc process
n’est pas nécessaire, mais il est recommandé pour la cohérence.
function Test-MrPipelineInput {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline)]
[string[]]$ComputerName
)
process {
Write-Output $ComputerName
}
}
L’acceptation de l’entrée du pipeline par le nom de propriété est similaire, sauf que vous devez la définir avec l’attribut de paramètre ValueFromPipelineByPropertyName
, et elle peut être définie pour un nombre quelconque de paramètres, quel que soit le type de données. La clé est que la sortie de la commande redirigée doit avoir un nom de propriété qui correspond au nom du paramètre ou à un alias de paramètre de votre fonction.
function Test-MrPipelineInput {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipelineByPropertyName)]
[string[]]$ComputerName
)
process {
Write-Output $ComputerName
}
}
Les blocs begin
et end
sont facultatifs.
begin
est spécifié avant le bloc process
et est utilisé pour effectuer tout travail initial avant que les éléments ne soient reçus du pipeline. Les valeurs qui sont entrées par pipeline ne sont pas accessibles dans le bloc begin
. Le bloc end
est spécifié après le bloc process
et est utilisé pour le nettoyage après que tous les éléments acheminés sont traités.
Gestion des erreurs
La fonction illustrée dans l’exemple suivant génère une exception non prise en charge quand un ordinateur ne peut pas être contacté.
function Test-MrErrorHandling {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]$ComputerName
)
process {
foreach ($Computer in $ComputerName) {
Test-WSMan -ComputerName $Computer
}
}
}
Il y a plusieurs manières de gérer les erreurs dans PowerShell.
Try/Catch
est le moyen le plus moderne de gérer les erreurs.
function Test-MrErrorHandling {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]$ComputerName
)
process {
foreach ($Computer in $ComputerName) {
try {
Test-WSMan -ComputerName $Computer
}
catch {
Write-Warning -Message "Unable to connect to Computer: $Computer"
}
}
}
}
Bien que la fonction illustrée dans l’exemple précédent utilise la gestion des erreurs, elle génère une exception non prise en charge, car la commande ne génère pas d’erreur avec fin d’exécution. Seules les erreurs avec fin d’exécution sont interceptées. Spécifiez le paramètre ErrorAction avec la valeur Stop pour transformer une erreur non terminale en erreur terminale.
function Test-MrErrorHandling {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]$ComputerName
)
process {
foreach ($Computer in $ComputerName) {
try {
Test-WSMan -ComputerName $Computer -ErrorAction Stop
}
catch {
Write-Warning -Message "Unable to connect to Computer: $Computer"
}
}
}
}
Ne modifiez pas la variable $ErrorActionPreference
globale sauf si cela est absolument nécessaire. Si vous la modifiez pour une étendue locale, elle revient à la valeur précédente lorsque vous quittez cette étendue.
Si vous utilisez du code comme .NET directement dans votre fonction PowerShell, vous ne pouvez pas spécifier le paramètre ErrorAction dans la commande elle-même. Vous pouvez modifier la variable $ErrorActionPreference
juste avant d’appeler la méthode .NET.
Aide basée sur les commentaires
L’ajout d’aide à vos fonctions est considéré comme une bonne pratique. L'aide permet aux personnes avec lesquelles vous partagez de savoir comment les utiliser.
function Get-MrAutoStoppedService {
<#
.SYNOPSIS
Returns a list of services that are set to start automatically, are not
currently running, excluding the services that are set to delayed start.
.DESCRIPTION
Get-MrAutoStoppedService is a function that returns a list of services
from the specified remote computer(s) that are set to start
automatically, are not currently running, and it excludes the services
that are set to start automatically with a delayed startup.
.PARAMETER ComputerName
The remote computer(s) to check the status of the services on.
.PARAMETER Credential
Specifies a user account that has permission to perform this action. The
default is the current user.
.EXAMPLE
Get-MrAutoStoppedService -ComputerName 'Server1', 'Server2'
.EXAMPLE
'Server1', 'Server2' | Get-MrAutoStoppedService
.EXAMPLE
Get-MrAutoStoppedService -ComputerName 'Server1' -Credential (Get-Credential)
.INPUTS
String
.OUTPUTS
PSCustomObject
.NOTES
Author: Mike F. Robbins
Website: https://mikefrobbins.com
Twitter: @mikefrobbins
#>
[CmdletBinding()]
param (
)
#Function Body
}
Lorsque vous ajoutez une aide sous forme de commentaires à vos fonctions, l'aide peut être récupérée comme pour les commandes intégrées par défaut.
Au départ, la syntaxe d’écriture d’une fonction dans PowerShell peut sembler compliquée. Si vous ne vous souvenez pas de la syntaxe d’un élément, ouvrez une deuxième instance de l’environnement d’écriture de scripts intégré de PowerShell (ISE) sur un autre moniteur et affichez l’extrait de code « Applet de commande (fonction avancée) - Terminer » tout en tapant le code de vos fonctions. Vous pouvez accéder aux extraits de code dans PowerShell ISE en appuyant sur les touches Ctrl + J.
Récapitulatif
Dans ce chapitre, vous avez appris les bases de l’écriture de fonctions dans PowerShell, en particulier comment :
- Créer des fonctions avancées
- Utiliser la validation des paramètres
- Utilisez une sortie détaillée.
- Prendre en charge l'entrée du pipeline
- Gérer les erreurs
- Créer une aide basée sur des commentaires
Révision
- Comment pouvez-vous obtenir une liste des verbes approuvés dans PowerShell ?
- Comment pouvez-vous transformer une fonction PowerShell en fonction avancée ?
- Quand devez-vous ajouter les paramètres WhatIf et Confirm à vos fonctions PowerShell ?
- Comment transformer une erreur non terminale en erreur terminale ?
- Pourquoi devez-vous ajouter une aide basée sur les commentaires à vos fonctions ?
Références
- about_Functions
- À propos des fonctions : paramètres avancés
- à_propos_des_ParamètresCommuns
- about_Functions_CmdletBindingAttribute
- about_Functions_Advanced
- à propos de Try_Catch_Finally
- à propos de l'aide basée sur les commentaires
- Vidéo : Création d'outils dans PowerShell avec les fonctions avancées et les modules de script