- Konverter funktioner til moduler PowerShell Manifestet gør det nemt at genbruge, versionere og distribuere dem.
- Pester tilbyder enheds- og integrationstest for at validere moduler og scripts, hvilket forhindrer regressioner i fremtidige ændringer.
- Integration af PowerShell, Pester og PowerShellGet i CI/CD-pipelines automatiserer testning, linting og publicering til PowerShell-galleriet.

Hvis du arbejder med PowerShell dagligt, vil du før eller siden blive træt af konstant at kopiere og indsætte de samme funktioner. Det er på det tidspunkt, det virkelig giver mening at tale om genbrugelige PowerShell-moduler og hvordan man tester dem seriøst med Pester , ligesom man ville gøre med ethvert "seriøst" udviklingsprojekt.
Selvom PowerShell blev født som et værktøj for administratorer, er det i dag et komplet automatiseringssprog, der er tværplatformet og har et massivt økosystem : moduler, avanceret scripting, automatiseret testning, CI/CD… og hvis du ved, hvordan du pakker dine funktioner i velbyggede moduler dækket af Pester, kan du versionsredigere, dele, publicere til PowerShell Gallery og implementere med selvtillid uden at skulle svede hver gang, du ændrer en linje.
Hvad er et PowerShell-modul præcist, og hvordan adskiller det sig fra et script?
Et PowerShell-modul er, kort sagt, en pakke af genbrugelige kommandoer : funktioner, cmdlets, aliasser, variabler, klasser eller DSC-ressourcer . Det, der gør et modul specielt, er, at PowerShell kan indlæse og aflæse det, kontrollere, hvad der eksponeres for brugeren, tildele det en version, forfatter, afhængigheder og så videre.
Fra et filperspektiv består et typisk modul af mindst én .psm1-fil (kode) og næsten altid en .psd1-manifestfil, der indeholder metadata . Det kan også indeholde andre scripts, ressourcer, hjælpefiler, data osv. Når det importeres ved hjælp af Import-Module eller via automatisk indlæsning, bliver dets kommandoer tilgængelige, som om de var native.
I modsætning hertil er et PowerShell- script blot en .ps1-fil, der kører "på én gang ". Det kan indeholde funktioner, men disse funktioner ligger typisk inden for scriptets omfang. Hvis du ikke bruger dot-sources eller omslutter dem i et modul, forsvinder de, så snart udførelsen er færdig.
Den praktiske forskel er betydelig: scripts er typisk "opgaver, der startes ", mens moduler er "genbrugelige biblioteker" , som du kan bruge fra andre scripts, fra konsollen eller endda fra CI/CD-pipelines uden at duplikere kode.
Scripts vs. moduler: Hvornår skal man bruge hver enkelt?
Et script (.ps1) er ideelt, når du skal udføre specifikke opgaver: en sikkerhedskopi , en rapport, en planlagt oprydning osv. Du kører det, det udfører sit job, og det afsluttes. Hvis du definerer funktioner i scriptet, forbliver disse funktioner i scriptets omfang ; når det afsluttes, kasseres omfanget, og med det dine funktioner.
Et scriptmodul (.psm1) er derimod designet til at indkapsle funktioner, som du vil genbruge mange gange, på forskellige maskiner og endda i forskellige projekter. Ved at gemme dem i et modul indlæses de i et modulområde, og du kan kun eksportere det, du har brug for, som en offentlig API.
Mange administratorer starter med et massivt script, der gør alt, og med tiden indser de, at de konstant deler dele af den kode. Det naturlige næste skridt er at udtrække disse gentagne funktioner til et modul , teste dem med Pester og derefter bruge det modul som fundament for mindre, renere scripts.
Derudover muliggør moduler noget afgørende for teams: versionsstyring, publicering og distribution . Du kan installere moduler fra PowerShell Gallery, fra private repositories, fra lokale repositories eller endda integrere dem i systembilleder, så de er tilgængelige som standard.
Dot-sourcing: det "hurtige trick" før du går over til moduler
Før du har dine funktioner i et modul, kan du have dem i et simpelt .ps1-script, som du vil genbruge . I så fald er den eneste ordentlige måde at gøre dem tilgængelige i din session at bruge dot-sourcing.
Når du kører et script med `.MyScript.ps1` , behandles indholdet inden for scriptets omfang. Hvis der er funktioner i den fil, forsvinder de, når scriptet er færdigt. Men hvis du kalder det med et punktum og et mellemrum (punktumsourcing) , udfører PowerShell det i det globale omfang, og funktionerne indlæses.
Eksempler på dot-sourcing til at indlæse funktioner i hukommelsen:
- Indlæs med relativ sti:
. .\Get-MrPSVersion.ps1 - Indlæs med fuld rute:
. C:\Demo\Get-MrPSVersion.ps1 - Indlæs ved hjælp af en stivariabel:
$Path = 'C:\'; . $Path\Get-MrPSVersion.ps1
Efter dot-sourcing kan du bekræfte, at funktionen findes i enheden Funktion: med en Get-ChildItem -Path Function:\Get-MrPSVersionfordi nu er det en del af den globale sfære.
Problemet er, at denne tilgang ikke skalerer godt: du skal huske at bruge dot-sourcing, du har ikke versionskontrol, du har ikke et manifest, og der er ingen forskel mellem offentlig og privat . Det er en nyttig genvej, men for noget seriøst og delbart er det meget bedre at tage springet til scriptmoduler.
Oprettelse af scriptmoduler og automatisk indlæsning

I PowerShell er et scriptmodul ligetil: Det er en .psm1-fil, der indeholder dine funktionerHvis du for eksempel opretter en fil MyScriptModule.psm1 med noget i retning af:
function Get-MrPSVersion { $PSVersionTable }
function Get-MrComputerName { $env:COMPUTERNAME }
og så prøver du at bruge Get-MrComputerName, det vil du se Den findes ikke, før du importerer moduletDu kan gøre det manuelt med:
Import-Module C:\MyScriptModule.psm1
PowerShell 3 introducerede magien ved automatisk indlæsning af moduler . For at få det til at virke, skal du:
- Gem dit modul i en mappe, hvis basisnavn matcher modulets navn, for eksempel MitScriptModul\MitScriptModul.psm1.
- Placer den mappe i en eller anden sti til $env:PSModulePath.
Værdien af $env:PSModulePath er en semikolonsepareret liste over stier . Du kan se den i et menneskelæsbart format med:
$env:PSModulePath -split ';'
Typiske stier inkluderer brugerens modulmappe, den globale modulmappe i Program Files og systemmodulmappen i System32 . Det er almindelig praksis at installere dine egne moduler i stien "AllUsers" (Program Files) og reservere System32-mappen udelukkende til systemmoduler.
Når modulet er på plads, og du aktiverer en af dets kommandoer for første gang, vil PowerShell automatisk indlæse det uden at skulle kalde Import-Module manuelt, hvilket er meget praktisk i scripts og interaktive sessioner.
Modulmanifester: eksport af metadata, version og funktion
For at et modul kan være veldefineret, er .psm1-filen ikke nok: den har brug for et .psd1-manifest , som ikke er andet end en PowerShell-datafil med modulmetadata (navn, version, forfatter, beskrivelse, afhængigheder, eksporterede funktioner osv.).
Standardmåden til at oprette den er ved at bruge New-ModuleManifest , hvor mindst stien til .psd1-filen og RootModule- parameteren peger på .psm1-filen. Derudover, hvis du planlægger at udgive modulet til et repository som PowerShell Gallery, er felter som Author, Description og CompanyName påkrævet.
Et tegn på, at dit modul mangler et manifest, er at tjekke dets version med Get-Module -Name MyScriptModule og se en version nummer 0.0, et tydeligt symptom på, at der kun er én løs .psm1-fil uden en tilknyttet .psd1-fil.
Et typisk mønster for at lave et manifest ville se sådan ud:
$moduleManifestParams = @{
Path = "$env:ProgramFiles\WindowsPowerShell\Modules\MyScriptModule\MyScriptModule.psd1"
RootModule = 'MyScriptModule'
Author = 'Autor del módulo'
Description = 'Funciones reutilizables para administración'
CompanyName = 'MiEmpresa'
}
New-ModuleManifest @moduleManifestParams
Senere, hvis du har brug for at ændre data, er det korrekte at bruge Update-ModuleManifest til kun at ændre det, der er nødvendigt, i stedet for at genskabe manifestet (hvilket ville generere et nyt GUID og kan ødelægge afhængigheder ).
Manifestet indeholder også nøgleafsnit som f.eks. FunctionsToExport , hvor du kan angive præcis hvilke modulfunktioner der er tilgængelige for brugeren, mens resten forbliver private funktioner interne i modulet.
Offentlige og private funktioner i dine moduler
I et modul i den virkelige verden ønsker man ikke at eksponere alt. Det er normalt at have interne hjælpefunktioner, der kun bruges af hinanden , og som man ikke ønsker, at brugerne skal kalde direkte. Der er to primære måder at kontrollere, hvad der går "ud" på:
Hvis du ikke har et manifest (et simpelt modul med kun .psm1), kan du styre synligheden ved hjælp af Export-ModuleMember i selve .psm1-filen. For eksempel:
function Get-MrPSVersion { $PSVersionTable }
function Get-MrComputerName { $env:COMPUTERNAME }
Export-ModuleMember -Function Get-MrPSVersion
I så fald vil kun Get-MrPSVersion være synlig som en modulkommando , mens Get-MrComputerName stadig vil være tilgængelig fra andre modulfunktioner, men ikke vil blive eksporteret offentligt . Du kan kontrollere, hvilke kommandoer der er synlige, med:
Get-Command -Module MyScriptModule
Hvis du har et manifest, er den reneste måde at definere den liste i FunctionsToExport- nøglen i .psd1-filen, for eksempel:
FunctionsToExport = 'Get-MrPSVersion'
Der er ingen grund til at duplikere logik ved at bruge både Export-ModuleMember i .psm1 og FunctionsToExport i .psd1 ; en af de to mekanismer er tilstrækkelig. Den manifestbaserede tilgang er normalt klarere i det lange løb.
PowerShell-galleri, krav og udgivelse af moduler og scripts
La PowerShell-galleriet Det er det primære offentlige arkiv for moduler og scripts. Derfra installerer du afhængigheder med Install-Module, du opdaterer med Update-Module og du udgiver dine egne pakker med Publish-Module o Publish-Script.
For at udgive et modul er der et par minimumskrav, du skal være opmærksom på: et unikt navn, en semantisk version, et korrekt manifest, komplette grundlæggende metadata, og hvis du bruger afhængigheder, at de også er tilgængelige. Tilsvarende skal scripts, som du vil udgive som sådan, have en header med metadata i scriptfilformat (forfatter, beskrivelse, version osv.).
Forbindelsen mellem PowerShellGet, Pakkehåndtering og NuGet Sådan fungerer det nogenlunde: PowerShellGet er det modul, du bruger (Install-Module, Publish-Module…), PackageManagement er det generiske pakkehåndteringslag, og under det hele, for Galleriet, bruger du NuGet som udbyderDet betyder, at selvom backend'en er NuGet, er det ikke det samme at bruge. nuget.exe at du bruger PowerShellGet; til de fleste PowerShell-modulopgaver vil du altid bruge PowerShellGet-cmdlet'erne.
For at udgive skal du bruge en NuGet API-nøgle (NUGET_KEY) bruges som legitimationsoplysninger. I CI/CD-miljøer gemmes den som en hemmelighed og eksponeres under byggeprocessen for at udføre en Publish-Module -NuGetApiKey. Det kan du også Gør krav på ejerskab af en pakke, reserver navne eller rapporter licenskrænkelser direkte i Galleriet, følg dets supportflows.
Hvad er Pester, og hvorfor bør du bruge det med dine moduler?
Pester er standardrammeværket for enheds- og integrationstest til PowerShell . Det er et PowerShell-modul, der definerer et meget læsbart testsprog baseret på blokke som Describe, Context, It, BeforeAll, AfterAll og assertions som Should-Be, Should-Not-Be osv.
Pesters mål er at give dig mulighed for at køre et sæt automatiserede tests, hver gang du ændrer din kode, hvilket sikrer, at du fortsætter med at opnå den forventede adfærd. Dette forhindrer mange almindelige faldgruber: at rette én ting i produktionen, blot for at ødelægge noget andet i processen, eller at en ændring af godkendelse i et stort projekt uventet lammer halvdelen af platformen.
I praksis giver Pester dig mulighed for at dække både enhedstests (isolerede funktioner, med mocks hvor det er nødvendigt) og integrationstests (dit modul kommunikerer med SharePoint , Azure, SQL osv.). For mange organisationer er test med Pester en minimumskvalitetsstandard , selv for "enkle" administrative scripts.
Udover assertions tilbyder Pester også logisk testgruppering, ekstern kommandomocking og måling af kodedækning , hvilket giver dig et godt overblik over, hvilke dele af modulet der dækkes, og hvilke der ikke er.
Installation og grundlæggende brug af Pester med moduler
Moderne versioner af Windows leveres ofte med en ældre version af Pester (v3) forudinstalleret . Den sædvanlige praksis i dag er at installere den opdaterede version fra Galleriet ved hjælp af:
Install-Module -Name Pester -Force
Pester fungerer på Windows, Linux og macOS og er kompatibel med PowerShell 3, 4, 5, 6 og 7, så du vil ikke have problemer i blandede miljøer. Når det er installeret, skal du blot oprette testfiler med suffikset .Tests.ps1 eller .Test.ps1, der beskriver dine modulers opførsel.
Den typiske struktur af en testfil indeholder blokke som:
- Før alleDen udføres én gang før alle tests (ideelt til import af modulet eller definition af fælles data).
- Beskrivgrupperer tests for en funktion eller et funktionsområde.
- Kontekst: opdeler scenarier ("når parametrene er korrekte", "når der er en fejl i legitimationsoplysningerne"...).
- ItHver It-blok er et individuelt bevis med sit sæt af påstande, der bruger Skulle.
- Trods alt: rydder op efter alle tests (lukker forbindelser, sletter midlertidige filer osv.).
For at køre testene skal du blot kalde Invoke-Pester og pege på testfilen eller -mappen. Du vil få en oversigt over antallet af vellykkede og mislykkede assertions sammen med udførelsestiden og, hvis du ønsker, detaljeret output med -Output Detailed.
Praktisk eksempel: test af en login-funktion med Pester
Forestil dig en PowerShell-funktion kaldet LoginSP , der indkapsler login til SharePoint Online ved hjælp af Plug and Play eller CSOM, og som returnerer en SharePoint-kontekst. Denne funktion findes i dit modul eller i en .ps1-fil, som du bruger i mange automatiseringsscripts.
Hvis snesevis af forretningsfunktioner afhænger af det, Du har ikke råd til, at en implementeringsændring (for eksempel at skifte fra PnP til CSOM) ødelægger alt.Det rimelige at gøre er at skrive enhedstests med Pester i en fil LoginSharePoint.Test.ps1 eller lignende.
I den testfil, i BeforeAll- blokken , dot-sourcer eller importerer du normalt modulet med LoginSP-funktionen. Derefter opretter du en eller flere Describe "Testing LoginSP" -blokke og konteksterer inden for dem for forskellige scenarier: korrekte parametre, forkerte parametre, forkerte legitimationsoplysninger osv.
Hver `It`- blok indeholder en specifik test, for eksempel: "should not return null" eller "URL'en for den returnerede kontekst matcher input-URL'en". I én test kunne du kalde funktionen direkte i `Should`-pipen, i en anden kunne du gemme resultatet i en variabel for at inspicere egenskaber og skrive yderligere information med `Write-Host`, hvis det er nødvendigt.
Udførelsen udføres med Invoke-Pester ".\LoginSharePoint.Test.ps1" Og hvis du ønsker flere detaljer, kan du tilføje -Output DetailedUdover øjeblikkelig output kan Pester integreres med kodedækning for at se hvilke dele af modulet der er blevet udført under testen.
Inputparametre i Pester-tests og containerbrug
Det er et vedligeholdelsesmareridt at indsætte legitimationsoplysninger eller URL'er direkte i hver test, og det er desuden vanskeligt at automatisere. Den elegante tilgang med Pester er at deklarere parametre i selve testfilen , præcis som man ville gøre i et PowerShell-script:
Param (
[Parameter(Mandatory=$true)] [string]$UserAccount,
[Parameter(Mandatory=$true)] [string]$UserPW,
[Parameter(Mandatory=$true)] [string]$SiteCollUrl
)
Med dette kan du bruge $UserAccount, $UserPW og $SiteCollUrl i alle tests inden for testen, uden at gentage dem. For at overføre disse værdier, når testene køres, leverer Pester PesterContainer :
$myContainer = New-PesterContainer -Path 'LoginSharePoint.Test.ps1' -Data @{
UserAccount = '[email protected]';
UserPw = 'MiMuySeguraPW';
SiteCollUrl = 'https://tenant.sharepoint.com/sites/TeamSite'
}
Invoke-Pester -Container $myContainer -Output Detailed
På denne måde kan du injicere forskellige testdata uden at røre testkoden , hvilket er perfekt til CI-pipelines, hvor legitimationsoplysninger, URL'er eller lejere ændres afhængigt af miljøet (udvikling, præ, produktion) og leveres som hemmeligheder eller beskyttede variabler.
Enheds- og integrationstest: hvad hver især dækker
I Pesters verden er det vigtigt at skelne mellem enhedstest og integrationstest . Enhedstest fokuserer på små blokke af kode isoleret , typisk ved hjælp af simuleringer af eksterne afhængigheder. For eksempel skal en funktion kaldet "New-Drawer" returnere et skuffeobjekt med fire hjørner, to skinner, et håndtag, den korrekte farve og de korrekte dimensioner.
Integrationstestning verificerer derimod, at alle disse individuelle komponenter fungerer korrekt, når de er forbundet til rigtige systemer : SharePoint, databaser , eksterne tjenester osv. Du kan opleve, at alle de individuelle komponenter bestås, og integrationen stadig er en katastrofe, hvis du ikke tester den eksplicit (for eksempel hvis det miljø, hvor "boksen" er konfigureret, ikke passer).
I PowerShell er det almindeligt, at dine moduler indkapsler al den forretningslogik, og at Pester hjælper dig med at sikre, at hver funktion gør, hvad den skal (enhedsfunktioner) , og at kombinationen af funktioner, moduler og eksterne tjenester fungerer sammen (integration).
Den store fordel er, at når du først har konfigureret testsuiten, bliver hver kodeændring et simpelt spørgsmål om at køre Pester og kontrollere resultaterne , hvilket beskytter dig mod regressioner i store, live-projekter.
PowerShell, moduler og Pester i CI/CD-pipelines
I dag er det meget almindeligt at integrere PowerShell og Pester i kontinuerlige integrations- og leveringspipelines, for eksempel med GitHub Actions . GitHubs hostede runners leveres med PowerShell 7 og Pester forudinstalleret og tilbyder også muligheden for at installere yderligere moduler fra galleriet ved hjælp af ` Install-Module`.
En typisk arbejdsgang starter med et testjob, der med hvert push kører Pester på dit modul . Et forenklet eksempel i YAML kan omfatte trin som:
- Udtjekning fra arkiv (actions/checkout@v5).
- Kør en simpel test med
Test-Path resultsfile.log | Should -Be $truefor at verificere, at der genereres et resultat. - Kørsel af en komplet testfil med
Invoke-Pester Unit.Tests.ps1 -Passthru.
Udover at køre Pester er det almindeligt Installer afhængigheder fra galleriet (f.eks. SqlServer o PSScriptAnalyzer), konfigurer PSGallery-arkivet som betroet og cache modulerne at accelerere descargas hjælp handlinger/cacheI Linux er den typiske modulcache for eksempel placeret i ~/.local/share/powershell/Modules, mens stien ændres i Windows.
Et andet meget nyttigt mønster er at bruge PSScriptAnalyzer i pipelinen til at scanne alle .ps1-filer , få en liste over problemer sorteret efter alvorlighed og fejle jobbet, hvis der er alvorlige fejl. Dette tvinger dig til at opretholde en ensartet kodningsstil og undgå åbenlyse fejl, selv før du går videre til funktionel testning med Pester.
Publicering af moduler til PowerShell Gallery fra CI
Når dine Pester-tests er bestået i CI, er det logiske næste skridt at automatisere publiceringen af dit modul til galleriet, når du opretter en ny udgivelse. I GitHub Actions gøres dette typisk med en arbejdsgang, der udløses af release:created events.
Det tilsvarende job er typisk:
- Tjek koden.
- Kør et byggescript (f.eks.
./build.ps1 -Path /tmp/samplemodule) der genererer modulets endelige struktur med .psm1, .psd1 og eventuelle yderligere ressourcer. - Læs hemmeligheden NUGET_KEY af arkivets hemmeligheder og eksponere det som en miljøvariabel.
- Ring til
Publish-Module -Path /tmp/samplemodule -NuGetApiKey $env:NUGET_KEY -Verbosefor at uploade den nye version til galleriet.
I denne arbejdsgang skrives udgivelseshemmeligheder aldrig i klartekst i scriptsene ; de administreres udelukkende af CI-udbyderen, og modulet frigives kun, hvis det tidligere har bestået Pester-tests . Dette er den reneste måde at undgå at ødelægge downstream-brugere med "ødelagte" versioner.
Som et sidste touch tilføjes der normalt et trin til upload testartefakterne produceret af Invoke-Pester (for eksempel en XML- eller CliXml-fil med resultater) ved hjælp af handlingen upload-artefaktDette giver dig mulighed for at gennemgå, hvad der gik galt, selvom jobbet er markeret som mislykket, takket være forhold som f.eks. if: ${{ always() }} der garanterer uploaden, selvom Pester returnerer fejl.
I sidste ende giver kombinationen af velpakkede PowerShell-moduler, grundig testning med Pester og CI/CD-pipelines, der automatisk kører, analyserer og publicerer, dig mulighed for at gå fra løse, skrøbelige scripts til et økosystem af genanvendelige, versionerede og pålidelige værktøjer, hvilket gør det daglige arbejde meget mere behageligt og forudsigeligt.
Passioneret forfatter om bytes-verdenen og teknologien generelt. Jeg elsker at dele min viden gennem skrivning, og det er det, jeg vil gøre i denne blog, vise dig alle de mest interessante ting om gadgets, software, hardware, teknologiske trends og mere. Mit mål er at hjælpe dig med at navigere i den digitale verden på en enkel og underholdende måde.

