Skip to content
Learni
View all tutorials
PowerShell

Comment créer un module PowerShell avancé en 2026

Introduction

En 2026, PowerShell 7+ reste l'outil incontournable pour l'automatisation cross-platform en DevOps et sysadmin. Créer un module avancé permet de réutiliser du code complexe, comme la gestion parallèle de services système, les classes orientées objets et les tests unitaires avec Pester. Pourquoi c'est crucial ? Les modules encapsulent des fonctionnalités expertes (parallélisme avec ForEach-Object -Parallel, validation stricte des paramètres, logging intégré), rendant vos scripts scalables et maintenables dans des environnements CI/CD. Ce tutoriel vous guide pas à pas pour bâtir un module 'ServiceManager' complet : monitoring multi-serveurs, redémarrages parallèles sécurisés et rapports JSON. À la fin, vous déployez sur PowerShell Gallery. Idéal pour gérer des flottes de 100+ machines sans downtime. (132 mots)

Prérequis

  • PowerShell 7.4+ installé (via winget ou PSResourceGet)
  • Visual Studio Code avec extension PowerShell
  • Module Pester 5+ (Install-Module Pester -Force)
  • Connaissances avancées en pipelines, splatting et error handling
  • Droits admin pour tester les services système

Initialiser la structure du module

init-module.ps1
$ModuleName = 'ServiceManager'
$ModulePath = "$PSScriptRoot/$ModuleName"

# Créer le dossier du module
New-Item -Path $ModulePath -ItemType Directory -Force

# Générer le manifeste
New-ModuleManifest -Path "$ModulePath/$ModuleName.psd1" -RootModule "$ModuleName.psm1" -Author 'Expert Dev' -Description 'Module avancé pour gestion services' -PowerShellVersion 7.0 -RequiredModules @() -FunctionsToExport @('Get-ServiceStatus', 'Start-ServiceParallel') -AliasesToExport @()

# Créer le fichier principal vide
New-Item -Path "$ModulePath/$ModuleName.psm1" -ItemType File -Force

Write-Output "Module $ModuleName initialisé dans $ModulePath"

Ce script crée la structure standard d'un module : dossier, manifeste PSD1 avec exports explicites et fichier PSM1 principal. Le manifeste définit les métadonnées SEO pour la Gallery, évitant les pièges comme l'export implicite qui pollue le namespace global. Exécutez-le en tant qu'admin pour tester.

Comprendre le manifeste et les exports

Le fichier PSD1 agit comme un contrat : il liste les fonctions exportées, modules requis et versions minimales. Sans FunctionsToExport, vos fonctions restent privées. Ajoutez PrivateData pour PSResourceGet en 2026.

Implémenter les fonctions avancées de base

ServiceManager.psm1
function Get-ServiceStatus {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [string[]]$ServiceName,
        [ValidateSet('Running', 'Stopped', 'All')]
        [string]$Status = 'All'
    )

    process {
        foreach ($name in $ServiceName) {
            $svc = Get-Service -Name $name -ErrorAction SilentlyContinue
            if ($svc) {
                [PSCustomObject]@{
                    Name = $svc.Name
                    Status = $svc.Status
                    StartType = $svc.StartType
                } | Where-Object { $Status -eq 'All' -or $_.Status -eq $Status }
            }
        }
    }
}

export-modulemember -Function Get-ServiceStatus

Cette advanced function gère le pipeline avec ValueFromPipeline, validation stricte et PSCustomObject pour sortie structurée. Le process {} permet le streaming, essentiel pour de gros volumes ; ErrorAction SilentlyContinue évite les crashes sur services manquants.

Pipeline et validation des paramètres

[ValidateSet] restreint les inputs, [CmdletBinding()] active splatting. Utilisez toujours PSCustomObject pour des objets cohérents, compatibles avec Export-Csv ou JSON.

Ajouter des classes pour modélisation OO

ServiceManager.psm1
class ServiceReport {
    [string]$Name
    [ServiceControllerStatus]$Status
    [ServiceStartMode]$StartType
    [DateTime]$LastCheck
    [string]$ErrorMessage

    ServiceReport([string]$name, [ServiceControllerStatus]$status, [ServiceStartMode]$startType) {
        $this.Name = $name
        $this.Status = $status
        $this.StartType = $startType
        $this.LastCheck = Get-Date
    }

    [void] AddError([string]$msg) {
        $this.ErrorMessage = $msg
    }
}

function Get-ServiceStatus {
    # ... code précédent ...

    process {
        foreach ($name in $ServiceName) {
            try {
                $svc = Get-Service -Name $name -ErrorAction Stop
                [ServiceReport]::new($svc.Name, $svc.Status, $svc.StartType)
            }
            catch {
                [ServiceReport]::new($name, 'Error', 'Unknown') | ForEach-Object { $_.AddError($_.Exception.Message); $_ }
            }
        }
    }
}

Les classes PowerShell 5+ encapsulent la logique (constructeur, méthodes). Ici, ServiceReport gère erreurs avec try/catch, rendant le module robuste. Ajoutez au PSM1 existant ; surcharge la fonction précédente pour OO sans casser la compatibilité.

Avantages des classes en PowerShell

Les classes offrent intellisense VSCode, héritage et propriétés calculées. Parfait pour modéliser des entités complexes comme des rapports agrégés.

Implémenter le parallélisme avec jobs

ServiceManager.psm1
function Start-ServiceParallel {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [string[]]$ServiceName,
        [int]$ThrottleLimit = 5,
        [scriptblock]$InitScriptBlock = { }
    )

    begin {
        $InitScriptBlock.Invoke()
    }

    process {
        $ServiceName | ForEach-Object -Parallel {
            $using:ThrottleLimit
            try {
                $svc = Get-Service -Name $_ -ErrorAction Stop
                if ($svc.Status -ne 'Running') {
                    Start-Service -Name $_ -ErrorAction Stop
                    "Started $_"
                }
            }
            catch {
                "Error starting $_ : $($_.Exception.Message)"
            }
        } -ThrottleLimit $ThrottleLimit
    }
}

export-modulemember -Function Start-ServiceParallel

ForEach-Object -Parallel (PS7+) exécute en threads .NET, avec $using: pour variables parent. SupportsShouldProcess ajoute -WhatIf/-Confirm. ThrottleLimit évite surcharge CPU ; testez sur 10+ services.

Gestion du parallélisme et throttling

En 2026, le parallélisme est natif mais consomme mémoire ; utilisez toujours $using: et limitez à 10-20 threads. Ajoutez logging avec Write-Verbose.

Créer les tests unitaires avec Pester 5

tests/ServiceManager.Tests.ps1
$Here = Split-Path -Parent $PSCommandPath
Import-Module "$Here/../ServiceManager/ServiceManager.psm1" -Force

Describe 'Get-ServiceStatus' {
    It 'Retourne un rapport pour service existant' {
        $result = Get-ServiceStatus -ServiceName 'BITS'
        $result.Status | Should -Be 'Running'
        $result | Should -BeOfType ServiceReport
    }

    It 'Gère les erreurs gracefully' {
        $result = Get-ServiceStatus -ServiceName 'NonExistant'
        $result.ErrorMessage | Should -Not -BeNullOrEmpty
    }
}

Describe 'Start-ServiceParallel' {
    It 'Supporte WhatIf' {
        { Start-ServiceParallel -ServiceName 'BITS' -WhatIf } | Should -Not -Throw
    }
}

Pester 5 utilise Should assertions modernes. Placez dans tests/ ; exécutez Invoke-Pester. Couvre happy path, erreurs et flags. Intégrez à CI avec GitHub Actions.

Script de build et déploiement

build.ps1
$ModuleName = 'ServiceManager'
$ModulePath = "$PSScriptRoot/$ModuleName"

# Build : tester et packer
Import-Module Pester -PassThru | Install-Module -Force
Invoke-Pester -Path "$ModulePath/tests" -Output Detailed

# Publish avec PSResourceGet (2026 standard)
$manifestPath = "$ModulePath/$ModuleName.psd1"
Publish-Module -Path $ModulePath -NuGetApiKey $env:PSGALLERY_APIKEY -Repository PSGallery

Write-Output "Module publié ! Importez avec: Install-Module $ModuleName"

Ce build script teste via Pester puis publie sur Gallery avec Publish-Module. Définissez $env:PSGALLERY_APIKEY ; PSResourceGet remplace PackageManagement en 2026 pour sécurité accrue.

Bonnes pratiques

  • Toujours exporter explicitement dans PSM1 pour éviter pollution.
  • Utilisez [CmdletBinding(SupportsShouldProcess)] pour toutes actions mutantes.
  • Implémentez logging : Write-Error, Write-Verbose, Write-Debug.
  • Versionnez sémantiquement dans PSD1 et taggez Git.
  • Documentez avec .EXAMPLE dans Get-Help.

Erreurs courantes à éviter

  • Oublier export-modulemember : fonctions invisibles après import.
  • Parallélisme sans ThrottleLimit : surcharge système et OOM.
  • Ignorer ErrorAction : scripts cassent sur erreurs mineures.
  • Classes sans try/catch : exceptions non gérées polluent la sortie.

Pour aller plus loin

Approfondissez avec Desired State Configuration (DSC) v3, PowerShell 7.5 threading model et intégration Azure/AWS. Consultez la doc officielle PowerShell. Découvrez nos formations Learni sur l'automatisation pour certification expert.