Capitolo 10 - Moduli di script

La trasformazione di one-liner e script di PowerShell in strumenti riutilizzabili diventa ancora più importante se si tratta di strumenti che verranno usati di frequente. Il packaging delle funzioni in un modulo di script le rende più professionali e ne semplifica la condivisione.

Funzioni dot-sourcing

Un elemento che non è stato illustrato nel capitolo precedente è la funzione dot-sourcing. Quando una funzione in uno script non fa parte di un modulo, l'unico modo per caricarla in memoria consiste nell'effettuare il dot-sourcing del file .PS1 in cui è salvata.

La funzione seguente è stata salvata come Get-MrPSVersion.ps1.

function Get-MrPSVersion {
    $PSVersionTable
}

Quando si esegue lo script, non viene eseguita alcuna operazione.

.\Get-MrPSVersion.ps1

Se si prova a chiamare la funzione, viene generato un messaggio di errore.

Get-MrPSVersion
Get-MrPSVersion : The term 'Get-MrPSVersion' is not recognized as the name of a cmdlet,
function, script file, or operable program. Check the spelling of the name, or if a path
was included, verify that the path is correct and try again.
At line:1 char:1
+ Get-MrPSVersion
    + CategoryInfo          : ObjectNotFound: (Get-MrPSVersion:String) [], CommandNotFou
   ndException
    + FullyQualifiedErrorId : CommandNotFoundException

È possibile determinare se le funzioni sono caricate in memoria controllando se esistono nel PSDrive Function.

Get-ChildItem -Path Function:\Get-MrPSVersion
Get-ChildItem : Cannot find path 'Get-MrPSVersion' because it does not exist.
At line:1 char:1
+ Get-ChildItem -Path Function:\Get-MrPSVersion
    + CategoryInfo          : ObjectNotFound: (Get-MrPSVersion:String) [Get-ChildItem],
   ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

Il problema della chiamata allo script che contiene la funzione è che le funzioni sono caricate nell'ambito Script. Al termine dello script, l'ambito viene rimosso e con esso la funzione.

La funzione deve essere caricata nell'ambito Global. Questa operazione può essere eseguita effettuando il dot-sourcing dello script che contiene la funzione. È possibile usare il percorso relativo.

. .\Get-MrPSVersion.ps1

È anche possibile usare il percorso completo.

. C:\Demo\Get-MrPSVersion.ps1

Se una parte del percorso è archiviata in una variabile, può essere combinata con il resto del percorso. Non esiste alcun motivo per usare la concatenazione di stringhe per combinare la variabile con il resto del percorso.

$Path = 'C:\'
. $Path\Get-MrPSVersion.ps1

Se a questo punto si controlla il PSDrive Function, la funzione Get-MrPSVersion esiste.

Get-ChildItem -Path Function:\Get-MrPSVersion
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        Get-MrPSVersion

Moduli di script

Un modulo di script in PowerShell è semplicemente un file che contiene una o più funzioni salvate come file .PSM1 anziché come file .PS1.

Come si crea un modulo di script? Si potrebbe ipotizzare di crearlo con un comando denominato New-Module, L'ipotesi è sbagliata. Anche se in PowerShell esiste un comando denominato New-Module, quel comando crea un modulo dinamico, non un modulo di script. Assicurarsi sempre di leggere la guida per un comando anche quando si ritiene di aver trovato il comando desiderato.

help New-Module
NAME
    New-Module

SYNOPSIS
    Creates a new dynamic module that exists only in memory.

SYNTAX
    New-Module [-Name] <String> [-ScriptBlock] <ScriptBlock> [-ArgumentList <Object[]>]
    [-AsCustomObject] [-Cmdlet <String[]>] [-Function <String[]>] [-ReturnResult]
    [<CommonParameters>]

DESCRIPTION
    The New-Module cmdlet creates a dynamic module from a script block. The members of
    the dynamic module, such as functions and variables, are immediately available in
    the session and remain available until you close the session.

    Like static modules, by default, the cmdlets and functions in a dynamic module are
    exported and the variables and aliases are not. However, you can use the
    Export-ModuleMember cmdlet and the parameters of New-Module to override the defaults.

    You can also use the AsCustomObject parameter of New-Module to return the dynamic
    module as a custom object. The members of the modules, such as functions, are
    implemented as script methods of the custom object instead of being imported into
    the session.

    Dynamic modules exist only in memory, not on disk. Like all modules, the members of
    dynamic modules run in a private module scope that is a child of the global scope.
    Get-Module cannot get a dynamic module, but Get-Command can get the exported members.

    To make a dynamic module available to Get-Module , pipe a New-Module command to
    Import-Module, or pipe the module object that New-Module returns to Import-Module .
    This action adds the dynamic module to the Get-Module list, but it does not save the
    module to disk or make it persistent.

RELATED LINKS
    Online Version: http://go.microsoft.com/fwlink/?LinkId=821495
    Export-ModuleMember
    Get-Module
    Import-Module
    Remove-Module

REMARKS
    To see the examples, type: "get-help New-Module -examples".
    For more information, type: "get-help New-Module -detailed".
    For technical information, type: "get-help New-Module -full".
    For online help, type: "get-help New-Module -online"

Nel capitolo precedente ho specificato che le funzioni devono usare i verbi approvati, in caso contrario generano un messaggio di avviso quando viene importato il modulo. Il codice seguente usa il cmdlet New-Module per creare un modulo dinamico in memoria. Questo modulo illustra l'avviso del verbo non approvato.

New-Module -Name MyModule -ScriptBlock {

    function Return-MrOsVersion {
        Get-CimInstance -ClassName Win32_OperatingSystem |
        Select-Object -Property @{label='OperatingSystem';expression={$_.Caption}}
    }

    Export-ModuleMember -Function Return-MrOsVersion

} | Import-Module
WARNING: The names of some imported commands from the module 'MyModule' include
unapproved verbs that might make them less discoverable. To find the commands with
unapproved verbs, run the Import-Module command again with the Verbose parameter. For a
list of approved verbs, type Get-Verb.

Come già indicato, anche se il cmdlet New-Module è stato usato nell'esempio precedente, non è il comando per la creazione di moduli di script in PowerShell.

Salvare le due funzioni seguenti in un file denominato MyScriptModule.psm1.

function Get-MrPSVersion {
    $PSVersionTable
}

function Get-MrComputerName {
    $env:COMPUTERNAME
}

Provare a chiamare una delle funzioni.

Get-MrComputerName
Get-MrComputerName : The term 'Get-MrComputerName' is not recognized as the name of a
cmdlet, function, script file, or operable program. Check the spelling of the name, or
if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ Get-MrComputerName
    + CategoryInfo          : ObjectNotFound: (Get-MrComputerName:String) [], CommandNot
   FoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Viene generato un messaggio di errore che informa che la funzione non è stata trovata. È anche possibile controllare il PSDrive Function esattamente come prima e si noterà che la funzione non è presente neanche in quella posizione.

È possibile importare manualmente il file con il cmdlet Import-Module.

Import-Module C:\MyScriptModule.psm1

La funzionalità di caricamento automatico dei moduli è stata introdotta in PowerShell versione 3. Per sfruttare i vantaggi del caricamento automatico dei moduli, è necessario salvare un modulo di script in una cartella con lo stesso nome di base del file .PSM1 e in un percorso specificato in $env:PSModulePath.

$env:PSModulePath
C:\Users\mike-ladm\Documents\WindowsPowerShell\Modules;C:\Program Files\WindowsPowerShell\
Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules;C:\Program Files (x86)\Microsof
t SQL Server\130\Tools\PowerShell\Modules\

I risultati sono difficili da leggere. I percorsi sono separati da un punto e virgola, quindi è possibile suddividere i risultati per restituire ogni percorso in una riga separata. In questo modo, la lettura è più semplice.

$env:PSModulePath -split ';'
C:\Users\mike-ladm\Documents\WindowsPowerShell\Modules
C:\Program Files\WindowsPowerShell\Modules
C:\Windows\system32\WindowsPowerShell\v1.0\Modules
C:\Program Files (x86)\Microsoft SQL Server\130\Tools\PowerShell\Modules\

I primi tre percorsi nell'elenco sono i valori predefiniti. Quando SQL Server Management Studio è stato installato, è stato aggiunto l'ultimo percorso. Per il corretto funzionamento del caricamento automatico del modulo, il file MyScriptModule.psm1 deve trovarsi in una cartella denominata MyScriptModule direttamente all'interno di uno di questi percorsi.

Rallentiamo. Per me, il percorso utente corrente non è il primo nell'elenco. Non uso quasi mai quel percorso perché accedo a Windows con un utente diverso da quello che uso per eseguire PowerShell. Ciò significa che non si trova nella normale cartella Documenti.

Il secondo percorso è il percorso AllUsers. Questo è il percorso in cui archivio tutti i moduli.

Il terzo percorso si trova sotto C:\Windows\System32. Solo Microsoft dovrebbe archiviare i moduli in quella posizione perché risiede all'interno della cartella del sistema operativo.

Quando il file .PSM1 si trova nel percorso corretto, il modulo viene caricato automaticamente quando viene chiamato uno dei relativi comandi.

Manifesti dei moduli

Tutti i moduli devono avere un manifesto. Un manifesto del modulo contiene i metadati relativi al modulo. L'estensione di un file manifesto del modulo è .PSD1. Non tutti i file con estensione .PSD1 sono manifesti del modulo. Possono anche essere usati per operazioni come l'archiviazione della parte ambientale di una configurazione DSC. New-ModuleManifest viene usato per creare un manifesto del modulo. Path è l'unico valore obbligatorio. Tuttavia, il modulo non funzionerà se non viene specificato RootModule. È consigliabile specificare Author e Description nel caso in cui si decida di caricare il modulo in un repository NuGet con PowerShellGet perché questi valori sono necessari in quello scenario.

La versione di un modulo senza manifesto è 0.0, a dimostrazione che il modulo non ha un manifesto.

Get-Module -Name MyScriptModule
ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     0.0        myscriptmodule                      {Get-MrComputerName, Get-MrP...

Il manifesto del modulo può essere creato con tutte le informazioni consigliate.

New-ModuleManifest -Path $env:ProgramFiles\WindowsPowerShell\Modules\MyScriptModule\MyScriptModule.psd1 -RootModule MyScriptModule -Author 'Mike F Robbins' -Description 'MyScriptModule' -CompanyName 'mikefrobbins.com'

Se una di queste informazioni non viene specificata durante la creazione iniziale del manifesto del modulo, è possibile aggiungerla o aggiornarla in un secondo momento usando Update-ModuleManifest. Non ricreare il manifesto usando New-ModuleManifest dopo che è già stato creato perché il GUID cambia.

Definizione di funzioni pubbliche e private

Ci possono essere funzioni helper che si vogliono rendere private e accessibili solo da altre funzioni all'interno del modulo. Non sono destinate a essere accessibili agli utenti del modulo. Esistono alcuni modi per eseguire questa operazione.

Se non si seguono le procedure consigliate ed è disponibile solo un file .PSM1, l'unica opzione consiste nell'usare il cmdlet Export-ModuleMember.

function Get-MrPSVersion {
    $PSVersionTable
}

function Get-MrComputerName {
    $env:COMPUTERNAME
}

Export-ModuleMember -Function Get-MrPSVersion

Nell'esempio precedente, solo la funzione Get-MrPSVersion è disponibile per gli utenti del modulo, ma la funzione Get-MrComputerName è disponibile per altre funzioni all'interno del modulo stesso.

Get-Command -Module MyScriptModule
CommandType     Name                                            Version    Source
-----------     ----                                            -------    ------
Function        Get-MrPSVersion                                 1.0        MyScriptModule

Se è stato aggiunto un manifesto al modulo (ed è in effetti opportuno aggiungerlo), è consigliabile specificare le singole funzioni che si vogliono esportare nella sezione FunctionsToExport del manifesto del modulo.

FunctionsToExport = 'Get-MrPSVersion'

Non è necessario usare sia Export-ModuleMember nel file .PSM1 che la sezione FunctionsToExport del manifesto del modulo. È sufficiente uno o l'altro.

Riepilogo

In questo capitolo si è appreso come trasformare le funzioni in un modulo di script in PowerShell. Sono state illustrate anche alcune delle procedure consigliate per la creazione di moduli di script, ad esempio la creazione di un manifesto del modulo di modulo per il modulo di script.

Revisione

  1. Come si crea un modulo di script in PowerShell?
  2. Perché è importante per le funzioni usare un verbo approvato?
  3. Come si crea un manifesto del modulo in PowerShell?
  4. Quali sono le due opzioni per esportare solo determinate funzioni dal modulo?
  5. Cosa è necessario per il caricamento automatico dei moduli quando viene chiamato un comando?