Capítulo 10: Módulos de scriptChapter 10 - Script modules

Convertir los scripts y las secuencias de comandos de una frase de PowerShell en herramientas reutilizables es incluso más importante si los va a usar con frecuencia.Turning your one-liners and scripts in PowerShell into reusable tools becomes even more important if it's something that you're going to use frequently. El empaquetado de las funciones en un módulo de script hace que tengan un aspecto más profesional y sean más fáciles de compartir.Packaging your functions in a script module makes them look and feel more professional and makes them easier to share.

Funciones prefijadas por puntosDot-Sourcing Functions

En el capítulo anterior no se abordaron las funciones prefijadas por puntos.Something that we didn't talk about in the previous chapter is dot-sourcing functions. Cuando una función de un script no forma parte de un módulo, la única manera de cargarla en la memoria es usar scripts prefijados por puntos en el archivo .PS1 en el que se guarda.When a function in a script isn't part of a module, the only way to load it into memory is to dot-source the .PS1 file that it's saved in.

La siguiente función se ha guardado como Get-MrPSVersion.ps1.The following function has been saved as Get-MrPSVersion.ps1.

function Get-MrPSVersion {
    $PSVersionTable
}

Al ejecutar el script, no sucede nada.When you run the script, nothing happens.

.\Get-MrPSVersion.ps1

Si intenta llamar a la función, se genera un mensaje de error.If you try to call the function, it generates an error message.

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

Para determinar si las funciones se cargan en la memoria, compruebe si existen en el PSDrive Function.You can determine if functions are loaded into memory by checking to see if they exist on the Function PSDrive.

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

El problema al llamar al script que contiene la función es que las funciones se cargan en el ámbito Script.The problem with calling the script that contains the function is that the functions are loaded in the Script scope. Cuando el script se completa, se quita ese ámbito y también la función.When the script completes, that scope is removed and the function is removed with it.

La función debe cargarse en el ámbito Global.The function needs to be loaded into the Global scope. Esto puede lograrse prefijando por puntos el script que contiene la función.That can be accomplished by dot-sourcing the script that contains the function. Se puede usar la ruta de acceso relativa.The relative path can be used.

. .\Get-MrPSVersion.ps1

Y también la ruta de acceso absoluta.The fully qualified path can also be used.

. C:\Demo\Get-MrPSVersion.ps1

Si una parte de la ruta de acceso se almacena en una variable, se puede combinar con el resto de la ruta de acceso.If a portion of the path is stored in a variable, it can be combined with the remainder of the path. No hay ninguna razón para usar la concatenación de cadenas para combinar la variable junto con el resto de la ruta de acceso.There's no reason to use string concatenation to combine the variable together with the remainder of the path.

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

Ahora, al comprobar el PSDrive Function, se ve como la función Get-MrPSVersion existe.Now when I check the Function PSDrive, the Get-MrPSVersion function exists.

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

Módulos de scriptScript Modules

Un módulo de script de PowerShell es simplemente un archivo que contiene una o más funciones que se guardan como un archivo .PSM1 en lugar de un archivo .PS1.A script module in PowerShell is simply a file containing one or more functions that's saved as a .PSM1 file instead of a .PS1 file.

¿Cómo se crea un módulo de script?How do you create a script module? Quizás piense que se hace mediante un comando con un nombre parecido a New-Module.You're probably guessing with a command named something like New-Module. No es así.Your assumption would be wrong. Si bien hay un comando en PowerShell denominado New-Module, ese comando crea un módulo dinámico, no un módulo de script.While there is a command in PowerShell named New-Module, that command creates a dynamic module, not a script module. Asegúrese siempre de leer la ayuda de un comando, incluso cuando crea que ha encontrado el comando que necesita.Always be sure to read the help for a command even when you think you've found the command you need.

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"

En el capítulo anterior, mencioné que las funciones deben usar verbos aprobados; de lo contrario, generarán un mensaje de advertencia cuando se importe el módulo.In the previous chapter, I mentioned that functions should use approved verbs otherwise they'll generate a warning message when the module is imported. En el código siguiente se usa el cmdlet New-Module para crear un módulo dinámico en la memoria.The following code uses the New-Module cmdlet to create a dynamic module in memory. Este módulo muestra la advertencia de verbo no aprobado.This module demonstrates the unapproved verb warning.

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.

Me gustaría reiterar que, si bien el cmdlet New-Module se usó en el ejemplo anterior, ese no es el comando para crear módulos de script en PowerShell.Just to reiterate, although the New-Module cmdlet was used in the previous example, that's not the command for creating script modules in PowerShell.

Guarde las dos funciones siguientes en un archivo denominado MyScriptModule.psm1.Save the following two functions in a file named MyScriptModule.psm1.

function Get-MrPSVersion {
    $PSVersionTable
}

function Get-MrComputerName {
    $env:COMPUTERNAME
}

Intente llamar a una de las funciones.Try to call one of the functions.

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

Se genera un mensaje de error que indica que no se encuentra la función.An error message is generated saying the function can't be found. También puede comprobar el PSDrive Function como hizo antes y verá que tampoco existe allí.You could also check the Function PSDrive just like before and you'll find that it doesn't exist there either.

Podría importar manualmente el archivo con el cmdlet Import-Module.You could manually import the file with the Import-Module cmdlet.

Import-Module C:\MyScriptModule.psm1

La característica de carga automática de módulos se introdujo en la versión 3 de PowerShell.The module autoloading feature was introduced in PowerShell version 3. Para aprovechar las ventajas de la carga automática de módulos, un módulo de script debe guardarse en una carpeta con el mismo nombre base que el archivo .PSM1 y en una ubicación especificada en $env:PSModulePath.To take advantage of module autoloading, a script module needs to be saved in a folder with the same base name as the .PSM1 file and in a location specified 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\

Los resultados son difíciles de leer.The results are difficult to read. Puesto que las rutas de acceso están separadas por un punto y coma, puede dividir los resultados para que cada ruta de acceso se muestre en una línea independiente.Since the paths are separated by a semicolon, you can split the results to return each path on a separate line. De esta forma, serán más fáciles de leer.This makes them easier to read.

$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\

Las tres primeras rutas de acceso de la lista son las predeterminadas.The first three paths in the list are the default. Al instalar SQL Server Management Studio, se agregó la última ruta de acceso.When SQL Server Management Studio was installed, it added the last path. Para que el módulo de carga automática funcione, el archivo MyScriptModule.psm1 debe encontrarse en una carpeta denominada MyScriptModule directamente dentro de una de esas rutas de acceso.For module autoloading to work, the MyScriptModule.psm1 file needs to be located in a folder named MyScriptModule directly inside one of those paths.

Pero no es tan sencillo.Not so fast. Para mí, la ruta de acceso del usuario actual no es la primera de la lista.For me, my current user path isn't the first one in the list. Casi nunca uso esa ruta de acceso, ya que inicio sesión en Windows con un usuario diferente del que utilizo para ejecutar PowerShell.I almost never use that path since I log into Windows with a different user than the one I use to run PowerShell. Esto significa que no se encuentra en la carpeta Mis documentos normal.That means it's not located in my normal Documents folder.

La segunda ruta de acceso es la ruta de acceso AllUsers.The second path is the AllUsers path. Esta es la ubicación donde almaceno todos los módulos.This is the location where I store all of my modules.

La tercera ruta de acceso se encuentra bajo C:\Windows\System32.The third path is underneath C:\Windows\System32. Solo Microsoft debe almacenar módulos en esa ubicación, ya que reside en la carpeta de sistemas operativos.Only Microsoft should be storing modules in that location since it resides within the operating systems folder.

Una vez que el archivo .PSM1 se encuentra en la ruta de acceso correcta, el módulo se cargará automáticamente cuando se llame a uno de sus comandos.Once the .PSM1 file is located in the correct path, the module will load automatically when one of its commands is called.

Manifiestos de móduloModule Manifests

Todos los módulos deben tener un manifiesto de módulo.All modules should have a module manifest. Un manifiesto de módulo contiene metadatos sobre el módulo.A module manifest contains metadata about your module. La extensión de archivo de un archivo de manifiesto de módulo es .PSD1.The file extension for a module manifest file is .PSD1. No todos los archivos con la extensión .PSD1 son manifiestos de módulo.Not all files with a .PSD1 extension are module manifests. También se pueden usar para cosas como el almacenamiento de la parte de entorno de una configuración de DSC.They can also be used for things such as storing the environmental portion of a DSC configuration. New-ModuleManifest se usa para crear un manifiesto de módulo.New-ModuleManifest is used to create a module manifest. El único valor necesario es Path.Path is the only value that's required. Sin embargo, el módulo no funcionará si no se especifica RootModule.However, the module won't work if RootModule isn't specified. Es aconsejable especificar Author y Description en caso de que decida cargar el módulo en un repositorio de NuGet con PowerShellGet, ya que esos valores son necesarios en ese escenario.It's a good idea to specify Author and Description in case you decide to upload your module to a NuGet repository with PowerShellGet since those values are required in that scenario.

La versión de un módulo sin manifiesto es 0.0.The version of a module without a manifest is 0.0. Se trata de una señal inequívoca de que el módulo no tiene un manifiesto.This is a dead giveaway that the module doesn't have a manifest.

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

El manifiesto de módulo se puede crear con toda la información recomendada.The module manifest can be created with all of the recommended information.

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

Si falta alguna parte de esta información en el momento en que se crea el manifiesto de módulo, se puede agregar o actualizar más adelante mediante Update-ModuleManifest.If any of this information is missed during the initial creation of the module manifest, it can be added or updated later using Update-ModuleManifest. No vuelva a crear el manifiesto mediante New-ModuleManifest una vez que se haya creado porque el GUID cambiará.Don't recreate the manifest using New-ModuleManifest once it's already created because the GUID will change.

Definir las funciones públicas y privadasDefining Public and Private Functions

Es posible que tenga funciones auxiliares que desee que sean privadas y accesibles solo para las demás funciones del módulo.You may have helper functions that you may want to be private and only accessible by other functions within the module. No están diseñadas para ser accesibles para los usuarios de su módulo.They are not intended to be accessible to users of your module. Hay dos formas diferentes de lograrlo.There are a couple of different ways to accomplish this.

Si no sigue los procedimientos recomendados y solo tiene un archivo .PSM1, la única opción es usar el cmdlet Export-ModuleMember.If you're not following the best practices and only have a .PSM1 file, then your only option is to use the Export-ModuleMember cmdlet.

function Get-MrPSVersion {
    $PSVersionTable
}

function Get-MrComputerName {
    $env:COMPUTERNAME
}

Export-ModuleMember -Function Get-MrPSVersion

En el ejemplo anterior, solo la función Get-MrPSVersion está disponible para los usuarios del módulo, pero la función Get-MrComputerName está disponible para otras funciones del módulo.In the previous example, only the Get-MrPSVersion function is available to the users of your module, but the Get-MrComputerName function is available to other functions within the module itself.

Get-Command -Module MyScriptModule

CommandType     Name                        Version    Source
-----------     ----                        -------    ------
Function        Get-MrPSVersion             1.0        MyScript...

Si ha agregado un manifiesto de módulo al módulo (que es lo que debería hacer), le recomiendo que especifique las funciones individuales que desea exportar en la sección FunctionsToExport del manifiesto del módulo.If you've added a module manifest to your module (and you should), then I recommend specifying the individual functions you want to export in the FunctionsToExport section of the module manifest.

FunctionsToExport = 'Get-MrPSVersion'

No es necesario usar tanto Export-ModuleMember en el archivo .PSM1 como la sección FunctionsToExport del manifiesto del módulo.It's not necessary to use both Export-ModuleMember in the .PSM1 file and the FunctionsToExport section of the module manifest. Basta con usar una de las opciones.One or the other is sufficient.

ResumenSummary

En este capítulo, ha aprendido a convertir sus funciones en un módulo de script de PowerShell.In this chapter you've learned how to turn your functions into a script module in PowerShell. También ha aprendido algunos de los procedimientos recomendados para crear módulos de script, como la creación de un manifiesto de módulo para el módulo de script.You've also leaned some of the best practices for creating script modules such as creating a module manifest for your script module.

RevisarReview

  1. ¿Cómo se crea un módulo de script en PowerShell?How do you create a script module in PowerShell?
  2. ¿Por qué es importante que las funciones usen un verbo aprobado?Why is it important for your functions to use an approved verb?
  3. ¿Cómo se crea un manifiesto de módulo en PowerShell?How do you create a module manifest in PowerShell?
  4. ¿Cuáles son las dos opciones para exportar solo determinadas funciones del módulo?What are the two options for exporting only certain functions from your module?
  5. ¿Qué se necesita para que los módulos se carguen automáticamente cuando se llama a un comando?What is required for your modules to load automatically when a command is called?