Distribuer et mettre à jour des modules PowerShell en interne — PSResourceGet et le dépôt interne
· Go Komura · PowerShell, Module, Distribution, Gestion des versions, Amélioration opérationnelle, Maintenabilité, Systèmes d'information, Automatisation
« J’ai écrit un script pratique, alors je l’ai déposé sur le dossier partagé » — c’est exactement à cet instant que la dette de maintenance commence à s’accumuler en silence. Quelqu’un le copie et le modifie en local, les corrections apportées au fichier d’origine ne se répercutent jamais, et personne ne sait plus quelle version tourne où. Quelques années plus tard, le dossier partagé s’est rempli de 集計.ps1, 集計_v2.ps1, 集計_修正版_最新.ps1.
La réponse à ce problème est limpide dans l’univers PowerShell. En faire un module, lui donner une version, et le distribuer depuis un dépôt. Cela seul permet de répondre aux questions « quelle version est installée » et « est-ce que tout le monde reçoit la mise à jour ». Et à partir de PowerShell 7.4, le mécanisme prévu à cet effet (PSResourceGet) est intégré dès le départ.
Cet article explique, dans une configuration réaliste sans serveur dédié, comment transformer en module les scripts partagés en interne, mettre en place un dépôt interne, puis les distribuer et les mettre à jour. Pour la modularisation des fonctions elle-même, lire d’abord « Conception des arguments PowerShell et modularisation » facilitera la compréhension.
Public visé et environnement de référence
| Élément | Contenu |
|---|---|
| Public visé | Les équipes informatiques ou opérationnelles qui distribuent des fichiers .ps1 placés dans un dossier partagé |
| Versions concernées | Windows PowerShell 5.1 et PowerShell 7.x, les deux. Cependant, PSResourceGet n’est intégré qu’à partir de PowerShell 7.4 ; en 5.1, une installation préalable est nécessaire aussi bien côté distributeur que côté utilisateur (chapitre 5)1 |
| Coexistence avec l’existant | PSResourceGet peut coexister avec l’ancien PowerShellGet 2.2.5. Il peut être adopté sans réécrire les scripts existants1 |
| Environnement de validation des exemples | Le code d’exemple distribué en fin d’article a été exécuté et validé sous PowerShell 7.6 |
| Droits nécessaires | La création du dossier partagé du dépôt et la configuration des droits, ainsi que l’installation avec -Scope AllUsers, nécessitent des droits d’administrateur |
1. La conclusion, d’abord
- PSResourceGet (
Microsoft.PowerShell.PSResourceGet) est intégré à PowerShell 7.4. Comme il coexiste avec l’ancien PowerShellGet 2.2.5, il peut être utilisé sans casser les scripts existants. Il n’est pas intégré à Windows PowerShell 5.1, donc une installation préalable est nécessaire aussi bien côté utilisateur que côté distributeur (chapitre suivant).1 - Le dépôt interne peut démarrer avec un simple partage de fichiers. Il suffit d’indiquer un chemin UNC à
Register-PSResourceRepository. Aucun serveur dédié n’est nécessaire.2 - Considérez le manifeste (
.psd1) comme obligatoire. Sans numéro de version, ni la mise à jour ni le diagnostic ne sont possibles.3 - Ne mettez pas de caractère générique dans
FunctionsToExport: déclarez-le explicitement sous forme de tableau. Cela accélère la découverte des commandes et évite l’exposition involontaire de fonctions internes.3 - Versionnez selon le versionnage sémantique. Si les changements incompatibles ne sont pas exprimés par la version majeure, les utilisateurs ne peuvent pas mettre à jour en toute confiance.4
- La publication se fait avec
Publish-PSResource, la récupération avecInstall-PSResource, la mise à jour avecUpdate-PSResource.56 - Les chemins de recherche des modules diffèrent entre 5.1 et 7. Sans comprendre cette différence dans
$env:PSModulePath, on se heurte au problème « installé mais introuvable ».7 - Si la stratégie d’exécution est
AllSigned, la signature Authenticode de chaque fichier de script est obligatoire. La signature de catalogue sert à vérifier l’intégrité du paquet, mais ne satisfait pas la stratégie d’exécution.8 - Soyez prudent avec la mise à jour automatique des modules dont dépend une exécution sans surveillance. Il est plus sûr de valider puis de monter de version de façon planifiée.
Le mécanisme construit dans cet article prend, dans son ensemble, la forme suivante.
flowchart LR
DEV["Côté développement<br/>Le service informatique écrit le module"]
TEST["Tests et analyse statique<br/>Pester / PSScriptAnalyzer"]
REPO["Dépôt interne<br/>Partage de fichiers (chemin UNC) ou<br/>flux compatible NuGet"]
USER["PC côté utilisateur<br/>Find / Install / Update-PSResource"]
SRV["Serveur côté utilisateur<br/>Exécution sans surveillance (batch nocturne, etc.)"]
DEV --> TEST
TEST -->|"Publish-PSResource"| REPO
REPO -->|"Install-PSResource"| USER
REPO -->|"Install-PSResource -Scope AllUsers"| SRV
Les commandes portées par les flèches sont le cœur de cet article. Le dépôt s’enregistre une seule fois au départ, avec Register-PSResourceRepository, aussi bien côté distributeur que côté utilisateur (chapitre 5), puis tout tourne ensuite avec les seules commandes de publication, de récupération et de mise à jour (chapitre 6). Le seul périmètre de sécurité de ce schéma est que seul le responsable de la distribution détient les droits d’écriture.
2. Les problèmes du « .ps1 dans le dossier partagé »
Commençons par clarifier ce que l’on cherche à résoudre.
| Symptôme | Cause profonde |
|---|---|
| Impossible de savoir quelle version tourne | Pas de notion de numéro de version |
| Une correction ne se répercute pas chez tout le monde | Chacun détient sa propre copie |
| Impossible de savoir qui l’utilise | Aucune trace de récupération n’est conservée (ce point seul dépend du mode de distribution choisi, voir plus loin) |
| Certains environnements seulement cassent | Les dépendances (module requis, version de PowerShell) ne sont pas déclarées |
| On voudrait corriger mais on ne peut pas évaluer l’impact | Aucune distinction entre fonctions publiques et fonctions internes |
La modularisation et la distribution par dépôt agissent directement sur les quatre premiers points ci-dessus. Seul « qui l’utilise » dépend du mécanisme de distribution retenu. Le dépôt sur partage de fichiers présenté dans les chapitres suivants est simple à mettre en place, mais il ne conserve aucune trace de qui a récupéré quoi ni quand (Get-InstalledPSResource ne renseigne que sur l’état du poste où la commande est exécutée). Si vous devez suivre l’utilisation, combinez-le avec l’une des options suivantes.
- Activer l’audit de lecture du dossier partagé (audit d’accès aux fichiers ; voir « Examiner concrètement le journal d’événements avec Get-WinEvent »)
- Utiliser un flux compatible NuGet offrant des statistiques de téléchargement (Azure Artifacts, par exemple)
- Exécuter
Get-InstalledPSResourcesur chaque poste et agréger les résultats (voir « Introduction à PowerShell Remoting (WinRM) »)
3. La structure minimale d’un module
La forme minimale d’un module distribuable tient en trois éléments : un dossier, un .psm1 et un .psd1.
KsOps\
KsOps.psd1 ← manifeste (version, fonctions exportées, dépendances)
KsOps.psm1 ← implémentation (ou dot-sourcing depuis les dossiers Public/Private)
Public\
Get-KsShareUsage.ps1
Invoke-KsArchive.ps1
Private\
ConvertTo-KsSize.ps1
Le manifeste se construit en générant un squelette avec New-ModuleManifest, puis en renseignant les éléments nécessaires.3
$manifest = @{
Path = '.\KsOps\KsOps.psd1'
RootModule = 'KsOps.psm1'
ModuleVersion = '1.0.0'
GUID = [guid]::NewGuid().Guid
Author = 'Service informatique'
CompanyName = 'Société Exemple'
Description = 'Module commun pour les scripts d''exploitation interne (inventaire et archivage du serveur de fichiers)'
PowerShellVersion = '5.1'
CompatiblePSEditions = @('Desktop', 'Core') # Si utilisé à la fois en 5.1 et en 7
# Pas de caractère générique. Déclarer explicitement uniquement ce qui est publié
FunctionsToExport = @('Get-KsShareUsage', 'Invoke-KsArchive')
CmdletsToExport = @()
VariablesToExport = @()
AliasesToExport = @()
RequiredModules = @() # Déclarer ici s'il y a des dépendances
Tags = @('internal', 'operations')
ProjectUri = 'https://git.example.co.jp/it/ksops'
}
New-ModuleManifest @manifest
Le .psm1 peut s’écrire selon un modèle standard qui charge les scripts de Public/Private et n’exporte que les fonctions publiques.
# KsOps.psm1
$public = @(Get-ChildItem -Path "$PSScriptRoot\Public\*.ps1" -ErrorAction SilentlyContinue)
$private = @(Get-ChildItem -Path "$PSScriptRoot\Private\*.ps1" -ErrorAction SilentlyContinue)
foreach ($file in @($public + $private)) {
try { . $file.FullName }
catch { throw "Échec du chargement du module : $($file.FullName) ── $_" }
}
# Seules les fonctions du dossier Public sont exportées (doit correspondre à la déclaration du manifeste)
Export-ModuleMember -Function $public.BaseName
Il y a deux raisons de ne pas mettre de caractère générique dans FunctionsToExport. La première concerne la performance de la découverte des commandes : en le déclarant explicitement, PowerShell peut déterminer « quelle commande se trouve où » sans analyser le corps du module. La seconde est une raison de conception : si des fonctions d’assistance internes deviennent appelables de l’extérieur, elles deviennent de fait une API publique qu’il ne sera plus possible de modifier ensuite.3
Dans l’exemple ci-dessus, CompatiblePSEditions déclare à la fois Desktop (Windows PowerShell 5.1) et Core (PowerShell 7), mais cette seule déclaration ne suffit pas à faire fonctionner le module dans les deux éditions. La préparation nécessaire pour utiliser PSResourceGet lui-même en 5.1 est traitée au chapitre 5, et le problème « installé mais introuvable » causé par la différence de chemin de recherche des modules entre 5.1 et 7 est traité au chapitre 7. Si vous comptez distribuer pour les deux éditions, lisez d’abord ces deux chapitres.
4. Comment décider du versionnage
La capacité des utilisateurs à mettre à jour en toute confiance dépend entièrement de la façon dont les numéros de version sont attribués. Adoptez le versionnage sémantique (majeure.mineure.correctif) et exprimez systématiquement les changements incompatibles par la version majeure.4
| Nature du changement | Élément à incrémenter |
|---|---|
| Renommage d’un paramètre, suppression d’une fonction, changement de la forme de la valeur de retour | Majeure (1.2.3 → 2.0.0) |
| Ajout de fonctions ou de paramètres (l’existant continue de fonctionner tel quel) | Mineure (1.2.3 → 1.3.0) |
| Correction de bug uniquement | Correctif (1.2.3 → 1.2.4) |
Pour distribuer une version de validation, vous pouvez utiliser une version préliminaire (prerelease). En définissant dans le manifeste PrivateData.PSData.Prerelease une chaîne comme beta1 (le trait d’union séparant la version est ajouté automatiquement, ce qui donne 1.3.0-beta1), cette version n’est pas récupérée lors d’une installation normale et n’est installée que si -Prerelease est explicitement indiqué. Seuls les caractères alphanumériques ASCII et le trait d’union sont autorisés dans cette chaîne ; le point et le + ne le sont pas.4
Pour la gestion des changements incompatibles, les principes de conception d’interfaces peuvent servir de référence (voir « Compatibilité ascendante des interfaces DLL et COM »).
5. Créer le dépôt interne — un partage de fichiers suffit
PSResourceGet peut traiter un dossier situé sur un partage de fichiers comme un dépôt.2 C’est la configuration dont le coût de mise en place est le plus faible.
Vérifions d’abord le prérequis. Il est intégré à partir de PowerShell 7.4, mais il n’est pas inclus dans Windows PowerShell 5.1. Pour utiliser en 5.1 les commandes qui suivent (Register-PSResourceRepository, etc.), installez d’abord le module.1
# 【Windows PowerShell 5.1 uniquement】Installer PSResourceGet.
# Les chemins de modules chargés diffèrent entre 5.1 et 7 : exécuter depuis l'édition ciblée
if (-not (Get-Module -ListAvailable -Name Microsoft.PowerShell.PSResourceGet)) {
Install-Module -Name Microsoft.PowerShell.PSResourceGet -Scope AllUsers -Force
}
# 【À exécuter une seule fois, côté distributeur comme côté utilisateur】Enregistrer le dépôt interne
# Trusted : traité comme fiable puisqu'il s'agit d'une distribution interne / Priority : recherché en priorité par rapport à PSGallery
$repo = @{
Name = 'KsInternal'
Uri = '\\fileserver\PSRepository'
Trusted = $true
Priority = 10
}
Register-PSResourceRepository @repo
Get-PSResourceRepository | Format-Table Name, Uri, Trusted, Priority
Réglez les droits d’accès du dossier partagé sur « seul le responsable de la distribution peut écrire, les utilisateurs n’ont que la lecture ». Si ce point est trop permissif, cela devient un canal par lequel n’importe qui peut distribuer du code arbitraire à toute l’entreprise. Configurez à la fois les autorisations de partage et les autorisations NTFS (le droit effectif retenu est le plus restrictif des deux).9
# À exécuter côté serveur de fichiers (droits d'administrateur requis)
$path = 'D:\PSRepository'
$null = New-Item -Path $path -ItemType Directory -Force
# Partage : les utilisateurs n'ont que la lecture, seul le responsable de la distribution peut écrire
New-SmbShare -Name 'PSRepository' -Path $path `
-ReadAccess 'EXAMPLE\Domain Users' -ChangeAccess 'EXAMPLE\Distributeurs de modules'
# NTFS : ne pas « ajouter », mais « remplacer par une liste d'autorisation ».
# New-Item -Force réussit même sur un dossier existant ; si ce dossier a par le passé
# été publié via un autre partage, ou si quelqu'un s'est vu accorder temporairement
# un droit de modification, se contenter d'ajouter avec icacls /grant laisse subsister
# ce canal d'écriture. Comme il s'agit d'un dépôt que tous les postes enregistrent en
# Trusted, la personne restée avec ce droit pourrait distribuer du code à toute l'entreprise
$acl = Get-Acl -Path $path
$acl.SetAccessRuleProtection($true, $false) # Couper l'héritage et ne pas conserver les ACE héritées
foreach ($ace in @($acl.Access)) { # Retirer aussi les ACE accordées directement
[void]$acl.RemoveAccessRuleSpecific($ace)
}
# Ne conserver que les entités listées ici
$allow = @(
@{ Id = 'EXAMPLE\Distributeurs de modules'; Rights = 'Modify' } # Peut publier
@{ Id = 'EXAMPLE\Domain Users'; Rights = 'ReadAndExecute' } # Récupération seulement
@{ Id = 'BUILTIN\Administrators'; Rights = 'FullControl' }
@{ Id = 'NT AUTHORITY\SYSTEM'; Rights = 'FullControl' }
)
foreach ($a in $allow) {
$acl.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new(
$a.Id, $a.Rights, 'ContainerInherit, ObjectInherit', 'None', 'Allow'))
}
# Faire « vider » puis « remettre » en deux appels Set-Acl distincts crée un instant
# où personne n'a accès. Appliquer en une seule fois
Set-Acl -Path $path -AclObject $acl
icacls $path # Vérifier le résultat de la configuration. Contrôler visuellement qu'aucune entité imprévue n'apparaît
Ne vous contentez pas d’ajouter avec icacls /grant. /grant ne supprime pas les ACE existantes. Si une autre personne dispose encore d’un canal d’écriture sur ce dossier, il subsiste tel quel.9 Or ce dépôt part du principe que tous les postes l’enregistrent avec Trusted = $true. Un module placé dans un dépôt de confiance s’installe sans confirmation. La personne restée avec ce droit pourrait alors distribuer du code arbitraire à l’ensemble de l’environnement d’exécution PowerShell de l’entreprise, ce qui ruine à ce moment-là l’hypothèse selon laquelle « seul le responsable de la distribution peut publier ».
Couper l’héritage seul (SetAccessRuleProtection($true, $false)) ne suffit pas non plus. Cette méthode ne traite que les ACE héritées ; les ACE accordées directement à ce dossier n’en font pas partie.9 Comme ci-dessus, il faut donc aussi retirer les attributions directes avant de ne remettre que les entités de la liste d’autorisation. Sur un dossier nouvellement créé, le résultat serait identique, mais comme il s’agit d’un problème qui ne se manifeste que lors de la réutilisation d’un dossier existant, il est plus sûr de l’intégrer systématiquement à la procédure.
Faites du « responsable de la distribution » un groupe plutôt qu’un compte individuel. Le blocage causé par la mutation du responsable, rendant impossible toute mise à jour du module, se produit réellement en pratique. Si vous utilisez un flux compatible NuGet nécessitant une authentification, délivrez aux utilisateurs des identifiants en lecture seule, distincts des identifiants de publication (clé API). Pour la conception des droits d’accès au dossier partagé elle-même, voir aussi « Faire l’inventaire d’un serveur de fichiers avec PowerShell ».
Pour une exploitation plus poussée, enregistrez un flux compatible NuGet tel qu’Azure Artifacts ou GitHub Packages. Pour un dépôt exigeant une authentification, vous pouvez référencer les identifiants depuis un coffre SecretManagement (voir « Gérer les identifiants en toute sécurité avec PowerShell »).2
6. Publication, récupération et mise à jour
# 【Côté distributeur】Publier le module
Publish-PSResource -Path .\KsOps -Repository 'KsInternal'
# 【Côté utilisateur】Rechercher puis installer
Find-PSResource -Name 'KsOps' -Repository 'KsInternal'
Install-PSResource -Name 'KsOps' -Repository 'KsInternal' -Scope CurrentUser
# Installer en figeant la version (recommandé pour les serveurs de production)
Install-PSResource -Name 'KsOps' -Version '1.2.3' -Repository 'KsInternal' -Scope AllUsers
# Mettre à jour
Update-PSResource -Name 'KsOps' -Repository 'KsInternal'
# Vérifier ce qui est installé (uniquement l'état de « ce poste »)
Get-InstalledPSResource -Name 'KsOps' | Format-Table Name, Version, Repository, InstalledDate
# Pour connaître l'état de déploiement à l'échelle de l'entreprise, exécuter sur chaque poste et agréger
Invoke-Command -ComputerName $servers -ScriptBlock {
Get-InstalledPSResource -Name 'KsOps' -ErrorAction SilentlyContinue |
Select-Object Name, Version
} | Sort-Object PSComputerName
Le choix entre les valeurs de -Scope est important. Un module utilisé par une exécution sans surveillance du planificateur de tâches doit être installé en AllUsers (ou dans l’environnement du compte de service lui-même). La cause typique de « ça marche dans mon environnement, mais le traitement de nuit échoue avec la applet de commande n'est pas reconnue » est une installation dans le périmètre CurrentUser.67
Notez que -Scope AllUsers écrit dans un emplacement commun à tous les utilisateurs (sous Program Files), ce qui échoue si PowerShell n’est pas lancé en tant qu’administrateur.7 Si la première installation renvoie « Accès refusé », commencez par vérifier que la session est élevée.
7. Le chemin de recherche des modules et les différences entre 5.1 et 7
PowerShell recherche les modules dans les dossiers énumérés par $env:PSModulePath. Les chemins par défaut diffèrent entre Windows PowerShell 5.1 et PowerShell 7.7
| Édition | Chemin par défaut du périmètre utilisateur |
|---|---|
| Windows PowerShell 5.1 | %USERPROFILE%\Documents\WindowsPowerShell\Modules |
| PowerShell 7 | %USERPROFILE%\Documents\PowerShell\Modules |
Pour un module destiné aux deux éditions, déclarez Desktop et Core dans CompatiblePSEditions, et testez dans les deux environnements avant de distribuer. La règle PSUseCompatibleSyntax de PSScriptAnalyzer est utile pour vérifier la compatibilité (voir « Préserver la qualité des scripts PowerShell avec PSScriptAnalyzer »).
En cas de problème, vérifiez les trois points suivants.
$env:PSModulePath -split ';' # Chemins de recherche
Get-Module -Name KsOps -ListAvailable # Est-il trouvé, et quelle version ?
(Get-Module KsOps -ListAvailable).ModuleBase # Emplacement réellement chargé
8. Signature et stratégie d’exécution
Il existe deux mécanismes de signature ayant des objectifs différents, et les confondre mène à la situation « signé, mais impossible à exécuter ».8
| Mécanisme | Ce qu’il garantit | Satisfait-il la stratégie d’exécution AllSigned ? |
|---|---|---|
Signature Authenticode (Set-AuthenticodeSignature) |
L’éditeur et l’intégrité de chaque fichier de script individuel | Oui (chaque fichier doit être signé) |
Signature de catalogue (New-FileCatalog + signature) |
L’intégrité de l’ensemble du module (le paquet) | Non |
La stratégie d’exécution vérifie la signature Authenticode du fichier .ps1 / .psm1 que l’on cherche à charger, et de lui seul. Même en signant le fichier de catalogue (.cat), les fichiers de script qu’il contient restent non signés, si bien que l’exécution est bloquée dans un environnement AllSigned. Par conséquent, si vous exploitez AllSigned, il est indispensable de signer chaque fichier exécuté.
# (1) Obligatoire en environnement AllSigned : signer chaque fichier exécuté avec Authenticode
Get-ChildItem .\KsOps -Recurse -Include *.ps1, *.psm1, *.psd1 | ForEach-Object {
Set-AuthenticodeSignature -FilePath $_.FullName -Certificate $cert `
-TimestampServer 'http://timestamp.digicert.com'
}
# (2) De plus, combiner avec un catalogue pour détecter toute altération de l'ensemble du paquet
New-FileCatalog -Path .\KsOps -CatalogFilePath .\KsOps\KsOps.cat -CatalogVersion 2
Set-AuthenticodeSignature -FilePath .\KsOps\KsOps.cat -Certificate $cert `
-TimestampServer 'http://timestamp.digicert.com'
# Vérification côté utilisateur (indépendamment de la stratégie d'exécution, confirmer que le paquet distribué n'est pas altéré)
Test-FileCatalog -Path .\KsOps -CatalogFilePath .\KsOps\KsOps.cat -Detailed
La valeur du catalogue réside dans sa capacité à vérifier que « l’ensemble du module récupéré est identique à celui qui a été distribué » ; PSResourceGet propose d’ailleurs de son côté -AuthenticodeCheck pour vérifier signature et catalogue.6 Comprenez bien la répartition des rôles : la signature Authenticode conditionne l’exécution, le catalogue garantit l’intégrité du paquet distribué.
En ajoutant un horodatage, la signature reste valide même après l’expiration du certificat de signature. La vue d’ensemble de la stratégie d’exécution et de la gestion des signatures est résumée dans « Stratégie d’exécution et signature de scripts PowerShell ».
9. Ce qu’il faut fixer comme règles opérationnelles
L’exploitation compte plus que la technique. Fixez au minimum les points suivants.
- Qui peut publier ? La personne qui détient les droits d’écriture sur le dossier partagé est celle qui peut distribuer du code à toute l’entreprise
- Où consigner l’historique des changements ? Notez les changements incompatibles dans
ReleaseNotes(PrivateData.PSDatadu manifeste) ou dans un CHANGELOG - Les serveurs de production figent-ils la version ? Pour les modules dont dépend une exécution sans surveillance, il est plus sûr de figer la version et de la mettre à jour de façon planifiée
- La procédure de dépréciation. Lors de la suppression d’une fonction, augmentez la version majeure et annoncez la dépréciation à l’avance
- Faire passer les tests et le lint avant de publier. Idéalement, la publication se fait depuis la CI (voir « Mettre en place les tests PowerShell avec Pester »)
10. Les bonnes pratiques (tableau de décision)
| Point | Options | Critère de décision |
|---|---|---|
| Format de distribution | .ps1 en dossier partagé / module + dépôt |
Le point de bascule est la capacité à gérer la version et les mises à jour |
| Gestion des modules | PowerShellGet 2.x / PSResourceGet | Intégré à partir de 7.4. PSResourceGet pour tout nouveau projet1 |
| Dépôt | Partage de fichiers / flux compatible NuGet | Commencer par un partage de fichiers. Passer à un flux si authentification et audit sont nécessaires2 |
| Manifeste | Omis / Obligatoire | Sans version, l’exploitation n’est pas viable3 |
| Fonctions exportées | '*' / déclaration explicite par tableau |
Pour la performance de découverte et la non-publication des fonctions internes3 |
| Emplacement d’installation | CurrentUser / AllUsers pour l’exécution sans surveillance | Le critère est la visibilité depuis le compte de service7 |
| Mise à jour en production | Automatique / version figée + mise à jour planifiée | Pour éviter qu’un traitement de nuit tourne malgré lui avec une nouvelle version |
| Signature | Aucune / signature Authenticode de chaque fichier (+ catalogue) | En environnement AllSigned, la signature par fichier est obligatoire. Le catalogue sert à la vérification de l’intégrité8 |
11. Conclusion
- La distribution de
.ps1par dossier partagé rend ingérables la version, la mise à jour, les dépendances et le périmètre de publication. La modularisation et la distribution par dépôt résolvent ce problème. Seul « qui l’utilise » fait exception : un dépôt sur partage de fichiers ne conserve aucune trace de récupération ; combinez-le avec l’audit de lecture du dossier partagé, un flux offrant des statistiques de téléchargement, ou l’agrégation deGet-InstalledPSResourceexécuté sur chaque poste. - Le manifeste est obligatoire. Déclarez
FunctionsToExportexplicitement sous forme de tableau et ne publiez pas les fonctions internes. - Le dépôt interne peut démarrer avec un simple enregistrement du partage de fichiers par chemin UNC. La gestion des droits d’écriture constitue le périmètre de sécurité effectif.
- La publication se fait avec
Publish-PSResource, la récupération avecInstall-PSResource, la mise à jour avecUpdate-PSResource. Installez dans le périmètreAllUsersles modules utilisés par une exécution sans surveillance. - Les chemins de recherche diffèrent entre 5.1 et 7. Pour une prise en charge des deux, déclarez
CompatiblePSEditionset testez dans les deux environnements. - En environnement
AllSigned, chaque fichier de script doit porter une signature Authenticode. La signature de catalogue sert à vérifier l’intégrité du paquet distribué, ce qui est distinct de la possibilité d’exécution. Ajoutez toujours un horodatage.
Téléchargement du code d’exemple
Le code traité dans cet article est distribué sous une forme directement exécutable. Il contient un module complet distinguant fonctions publiques et privées, ainsi que la procédure de distribution vers le dépôt interne.
Télécharger le code d’exemple (zip)
Les exemples de cet article ont été réellement exécutés et validés sous PowerShell 7.6 (16 tests Pester). En exécutant Invoke-SampleTests.ps1 inclus dans le zip, vous pouvez reproduire la même validation sur votre poste.
# Analyse syntaxique + analyse statique + tests Pester
./Invoke-SampleTests.ps1
Les valeurs de configuration (chemins, noms de serveurs, ID de locataire, etc.) sont des exemples. Ne les exécutez pas telles quelles en production : adaptez-les à votre propre environnement.
Articles connexes
- Conception des arguments des scripts PowerShell et modularisation — d’un « script qui marche » à un « script que l’on peut transmettre »
- Stratégie d’exécution et signature de scripts PowerShell — sortir de la pratique du « colmatage avec Bypass »
- Préserver la qualité des scripts PowerShell avec PSScriptAnalyzer — choix des règles et intégration en CI
- Mettre en place les tests PowerShell avec Pester — un modèle pratique pour rendre les scripts d’exploitation moins fragiles
- Gérer les identifiants en toute sécurité avec PowerShell — bannir les mots de passe en clair des scripts
- Contre la dépendance à une seule personne : que le départ du créateur n’arrête rien
Domaines de conseil associés
合同会社小村ソフト (Komura Software LLC) prend en charge la modularisation des actifs de scripts internes et la mise en place d’une infrastructure de distribution, la standardisation d’opérations reposant sur une seule personne, ainsi que l’amélioration de la maintenabilité des scripts existants.
- Conseil technique et revue de conception
- Réutilisation des actifs existants et accompagnement à la migration
- Modification et maintenance de logiciels Windows existants
- Contact
Références
-
Microsoft Learn, Package management for PowerShell. Sur le fait que Microsoft.PowerShell.PSResourceGet est le module qui remplace PowerShellGet et PackageManagement, qu’il est intégré à PowerShell 7.4 en coexistant avec l’ancien PowerShellGet 2.2.5, et qu’il peut aussi être installé sous Windows PowerShell 5.1 depuis PowerShell Gallery. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Register-PSResourceRepository. Sur la possibilité d’enregistrer un dépôt en indiquant à -Uri un dossier local, un partage de fichiers (chemin UNC) ou l’URL d’un flux compatible NuGet, sur le réglage de confiance via -Trusted, sur l’ordre de recherche via -Priority, et sur la spécification des identifiants pour un dépôt nécessitant une authentification. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, How to write a PowerShell module manifest. Sur la création du manifeste avec New-ModuleManifest, sur les clés RootModule, ModuleVersion, GUID, PowerShellVersion, CompatiblePSEditions, RequiredModules, entre autres, et sur la raison (performance de la découverte des commandes) pour laquelle les déclarations d’export comme FunctionsToExport doivent être explicites plutôt qu’utiliser un caractère générique. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Prerelease module versions. Sur l’attribution de version fondée sur le versionnage sémantique, sur la spécification d’une version préliminaire via PrivateData.PSData.Prerelease, et sur le fait qu’une version préliminaire n’est pas récupérée par défaut. ↩ ↩2 ↩3
-
Microsoft Learn, Publish-PSResource. Sur la publication vers un dépôt du dossier de module indiqué par -Path, sur la spécification de la destination via -Repository, et sur l’authentification via -ApiKey. ↩
-
Microsoft Learn, Install-PSResource. Sur l’installation via -Name / -Version / -Repository, sur la spécification de l’emplacement via -Scope (CurrentUser / AllUsers), et sur l’omission de la confirmation via -TrustRepository. Voir également Update-PSResource pour la mise à jour. ↩ ↩2 ↩3
-
Microsoft Learn, about_PSModulePath. Sur le fait que PowerShell recherche les modules dans les dossiers énumérés par $env:PSModulePath, et sur la différence des chemins par défaut des périmètres utilisateur et tous-utilisateurs entre Windows PowerShell et PowerShell 7. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, New-FileCatalog. Sur la possibilité de générer un fichier de catalogue (.cat) contenant les empreintes des fichiers d’un dossier, sur la signature du catalogue avec Set-AuthenticodeSignature, et sur la détection d’altération en confrontant le catalogue aux fichiers avec Test-FileCatalog. Le fait que la stratégie d’exécution vérifie la signature Authenticode du fichier de script exécuté lui-même est traité dans about_Execution_Policies (en AllSigned, seuls les scripts signés par un éditeur de confiance peuvent s’exécuter) et dans Set-AuthenticodeSignature (application d’une signature Authenticode à un fichier, ajout d’un horodatage via -TimestampServer). ↩ ↩2 ↩3
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
Automatiser le déploiement de postes avec winget et PowerShell — Rendre le manuel de procédure exécutable
Ce guide explique comment rendre reproductible la configuration des PC des nouveaux employés : installation d'applications et export/impo...
Arrêter d'utiliser Write-Host — Conception des flux de sortie et de la journalisation dans PowerShell
Ce guide présente les six flux de sortie de PowerShell et leurs usages respectifs, les problèmes posés par Write-Host et son bon usage, l...
Introduction à Microsoft Graph PowerShell — Gérer Microsoft 365 après la mise hors service d'AzureAD et de MSOnline
Un guide pratique pour migrer la gestion de Microsoft 365 vers Microsoft Graph PowerShell après la mise hors service des modules AzureAD ...
Renforcer la sécurité de PowerShell — journalisation, AMSI, mode de langage, JEA
Ce guide rassemble la pratique pour utiliser PowerShell en toute sécurité sans l'interdire : activation de la journalisation des blocs de...
Examiner les journaux d'événements en pratique avec Get-WinEvent — La rapidité du filtrage détermine le temps d'investigation
Ce guide présente comment rendre plus efficace l'investigation des journaux d'événements Windows avec PowerShell. Il explique pourquoi fi...
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Questions fréquentes
Questions souvent posées lors d’une consultation sur le sujet de cet article.
- Quelle est la différence entre PowerShellGet et PSResourceGet ? Lequel dois-je utiliser ?
- PSResourceGet (Microsoft.PowerShell.PSResourceGet) est le nouveau mécanisme de gestion des modules qui remplace l'ancien duo PowerShellGet et PackageManagement ; il est intégré en standard dès PowerShell 7.4. Comme il peut coexister avec l'ancien PowerShellGet 2.2.5, la migration ne casse pas les scripts existants. Pour tout nouveau développement, il est recommandé d'utiliser PSResourceGet, dont les applets de commande portent le suffixe -PSResource (Install-PSResource, Publish-PSResource, etc.). Sous Windows PowerShell 5.1, il reste utilisable en l'installant depuis PowerShell Gallery.
- Faut-il un serveur dédié pour mettre en place un dépôt interne ?
- Non. La méthode la plus simple consiste à enregistrer un dossier situé sur un partage de fichiers comme dépôt : il suffit d'indiquer un chemin UNC à Register-PSResourceRepository. Aucun serveur dédié ni base de données n'est nécessaire. Si l'organisation a besoin de contrôle d'accès et d'audit, ou si des utilisateurs externes doivent aussi récupérer les modules, on passe à un flux compatible NuGet comme Azure Artifacts ou GitHub Packages. Il est réaliste de commencer par un partage de fichiers et de migrer seulement lorsque le besoin s'en fait sentir.
- Le manifeste de module (.psd1) est-il vraiment indispensable ?
- En pratique, considérez-le comme indispensable. Un fichier .psm1 seul peut être chargé comme module, mais sans manifeste, il n'a pas de numéro de version, et il devient impossible de savoir « quelle version est installée ». Sans version, ni la gestion des mises à jour ni le diagnostic en cas d'incident ne sont possibles. Le manifeste permet en outre de déclarer explicitement les fonctions exportées, les modules dont on dépend, ainsi que les versions et éditions de PowerShell prises en charge. New-ModuleManifest génère un squelette, donc le coût de création reste minime.
- Quel problème pose l'écriture de '*' dans FunctionsToExport ?
- Cela ralentit la découverte automatique des commandes et expose même des fonctions internes non destinées à être publiques. PowerShell doit savoir quelle commande se trouve dans quel module avant de charger celui-ci, et avec un caractère générique, cela n'est possible qu'en analysant le corps entier du module. En déclarant explicitement les fonctions exportées sous forme de tableau, cette analyse devient inutile. De plus, si des fonctions d'assistance internes deviennent appelables de l'extérieur, elles deviennent de fait une API publique qu'il n'est plus possible de modifier ensuite.
- Peut-on faire mettre à jour automatiquement, côté utilisateur, un module distribué ?
- Update-PSResource permet de le faire, mais la mise à jour automatique d'un module dont dépendent des scripts métier doit être conçue avec prudence, car un traitement de nuit sans surveillance pourrait se retrouver, à son insu, à s'exécuter avec une nouvelle version. En pratique, il est plus sûr de vérifier la nouvelle version dans un environnement de test avant de planifier la mise à jour en production. Si l'automatisation est absolument nécessaire, mettez en place un garde-fou comme le figeage de la version majeure lors de la mise à jour (en indiquant une plage de type -Version '1.*').
Profil de l’auteur
Page de présentation de l’auteur de l’article.
Go Komura
Représentant de KomuraSoft LLC
Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.