Compartir vía


Capítulo 10: Módulos de script

Si se encuentra usando con frecuencia las mismas líneas únicas de PowerShell o scripts, convertirlos en herramientas reutilizables resulta aún más importante. Empaquetar las funciones en un módulo de script les ofrece una sensación más profesional y facilita el soporte técnico y el uso compartido con otros usuarios.

Funciones de dot-sourcing

Una cosa que no hemos cubierto en el capítulo anterior es las funciones dot-sourcing. Al definir una función en un script, pero no en parte de un módulo, la única manera de cargarla en la memoria es dot-sourcing su .ps1 archivo.

Por ejemplo, guarde la siguiente función en un archivo denominado Get-MrPSVersion.ps1.

function Get-MrPSVersion {
    $PSVersionTable
}

Al ejecutar el script, parece que no sucede nada.

.\Get-MrPSVersion.ps1

Si se intenta llamar a la función, se produce un error porque no se carga en la memoria.

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) [],
   CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Puede confirmar si las funciones se cargan en la memoria comprobando su existencia en 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.Ge
   tChildItemCommand

El problema con la ejecución del script que define la función es que lo carga en el ámbito Script. Una vez que el script termine de ejecutarse, PowerShell descarta ese ámbito junto con la función .

Para mantener la función disponible después de ejecutar el script, debe cargarse en el ámbito global . Puede hacerlo mediante dot-sourcing del archivo de script. Puede usar una ruta de acceso relativa para este propósito.

. .\Get-MrPSVersion.ps1

También puede usar la ruta completa del script cuando lo ejecute con dot-sourcing.

. C:\Demo\Get-MrPSVersion.ps1

Si parte de la ruta se almacena en una variable, puede combinarla con el resto de la ruta. No es necesario usar la concatenación de cadenas para hacerlo.

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

Ahora, si comprueba la función PSDrive, verá que la Get-MrPSVersion función está disponible.

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

Módulos de script

En PowerShell, un módulo de script es simplemente un .psm1 archivo que contiene una o varias funciones, al igual que un script normal, pero con una extensión de archivo diferente.

¿Cómo se crea un módulo de script? Podrías asumir con un comando llamado algo tal como New-Module. Esa suposición es una estimación razonable, pero ese comando crea realmente un módulo dinámico, no un módulo de script.

Este escenario es un buen recordatorio para leer siempre la documentación de soporte, incluso cuando un nombre de comando parece ser exactamente lo que necesita.

help New-Module
NAME
    New-Module

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


SYNTAX
    New-Module [-Name] <System.String> [-ScriptBlock]
    <System.Management.Automation.ScriptBlock> [-ArgumentList
    <System.Object[]>] [-AsCustomObject] [-Cmdlet <System.String[]>]
    [-Function <System.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: https://learn.microsoft.com/powershell/module/microsoft.
    powershell.core/new-module?view=powershell-5.1&WT.mc_id=ps-gethelp
    Export-ModuleMember
    Get-Module
    Import-Module
    Remove-Module
    about_Modules

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 se mencionó que las funciones deben usar verbos aprobados. De lo contrario, PowerShell genera una advertencia cuando se importa el módulo.

En el ejemplo siguiente se usa el New-Module cmdlet para crear un módulo dinámico en memoria, específicamente para demostrar lo que sucede cuando no se usa un verbo aprobado.

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.

Aunque usó el New-Module cmdlet en el ejemplo anterior, como se mencionó anteriormente, no es el comando para crear módulos de script en PowerShell.

Para crear un módulo de script, guarde las funciones en un .psm1 archivo. Por ejemplo, guarde las dos funciones siguientes en un archivo denominado MyScriptModule.psm1.

function Get-MrPSVersion {
    $PSVersionTable
}

function Get-MrComputerName {
    $env:COMPUTERNAME
}

Intente ejecutar una de las funciones.

Get-MrComputerName

Al llamar a la función, recibirá un error que indica que PowerShell no lo encuentra. Al igual que antes, comprobar la función: PSDrive confirma que no se carga en la memoria.

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) [
   ], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Para que la función esté disponible, puede importar manualmente el MyScriptModule.psm1 archivo mediante el Import-Module cmdlet .

Import-Module C:\MyScriptModule.psm1

PowerShell introdujo la carga automática del módulo en la versión 3. Para aprovechar esta característica, el módulo de script debe guardarse en una carpeta con el mismo nombre base que el .psm1 archivo. Esa carpeta debe encontrarse en uno de los directorios especificados en la $env:PSModulePath variable de entorno.

$env:PSModulePath

La salida de $env:PSModulePath es difícil de leer.

C:\Users\mike-ladm\Documents\WindowsPowerShell\Modules;C:\Program Files\Wind
owsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules;C:\
Program Files (x86)\Microsoft SQL Server\130\Tools\PowerShell\Modules\

Para que los resultados sean más legibles, divide las rutas en el separador de ruta de punto y coma para que cada una aparezca en su propia línea.

$env:PSModulePath -split ';'

Las tres primeras rutas de acceso de la lista son las ubicaciones predeterminadas del módulo. SQL Server Management Studio agregó la última ruta de acceso al instalarla.

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\

Para que la carga automática del módulo funcione, debe colocar el archivo MyScriptModule.psm1 en una carpeta denominada MyScriptModule y esa carpeta debe residir directamente dentro de una de las rutas de acceso enumeradas.
$env:PSModulePath.

No todas esas rutas de acceso son igualmente útiles. Por ejemplo, la ruta de acceso del usuario actual en mi sistema no es la primera de la lista. Esto se debe a que inicio sesión en Windows con una cuenta diferente a la que uso para ejecutar PowerShell. Por lo tanto, no apunta a la carpeta de documentos de mi usuario.

La segunda ruta de acceso es la ruta AllUsers , que es donde se almacenan todos mis módulos.

La tercera ruta de acceso apunta a C:\Windows\System32, una ubicación del sistema protegida. Solo Microsoft debe colocar módulos allí, ya que se encuentra en la estructura de directorios del sistema operativo.

Una vez que coloque el .psm1 archivo en una carpeta adecuada dentro de una de estas rutas de acceso, PowerShell carga automáticamente el módulo la primera vez que llame a uno de sus comandos.

Manifiestos de módulo

Cada módulo debe incluir un manifiesto de módulo, que es un .psd1 archivo que contiene metadatos sobre el módulo. Aunque la .psd1 extensión se usa para manifiestos, no todos los .psd1 archivos son manifiestos de módulo. También puede usarlos para otros fines, como definir datos de entorno en un DSC.
configuración.

Puede crear un manifiesto de módulo mediante el New-ModuleManifest cmdlet . El único parámetro necesario es Path, pero para que el módulo funcione correctamente, también debe especificar el parámetro RootModule .

Se recomienda incluir valores como Author y Description, especialmente si planea publicar el módulo en un repositorio nuGet mediante PowerShellGet. Estos campos son necesarios en ese escenario.

Una manera rápida de saber si un módulo carece de un manifiesto es comprobar su versión.

Get-Module -Name MyScriptModule

Un número de versión de 0.0 es un signo claro de que el módulo carece de un manifiesto.

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     0.0        MyScriptModule                      {Get-MrComputer...

Debe incluir todos los detalles recomendados al crear un manifiesto de módulo para asegurarse de que el módulo está bien documentado y listo para compartir o publicar.

$moduleManifestParams = @{
    Path = "$env:ProgramFiles\WindowsPowerShell\Modules\MyScriptModule\MyScriptModule.psd1"
    RootModule = 'MyScriptModule'
    Author = 'Mike F. Robbins'
    Description = 'MyScriptModule'
    CompanyName = 'mikefrobbins.com'
}

New-ModuleManifest @moduleManifestParams

Si omite algún valor al crear inicialmente el manifiesto del módulo, puede agregarlo o actualizarlo más adelante mediante el Update-ModuleManifest cmdlet . Evite volver a crear el manifiesto con New-ModuleManifest una vez que lo hayas creado, ya que, al hacerlo, se genera un nuevo GUID.

Definición de funciones públicas y privadas

A veces, el módulo puede incluir funciones auxiliares que no desea exponer a los usuarios. Otras funciones del módulo usan internamente estas funciones privadas, pero no se exponen a los usuarios. Hay varias maneras de controlar este escenario.

Si no sigue los procedimientos recomendados y solo tiene un archivo sin un .psm1 manifiesto de módulo, la única opción es controlar la visibilidad mediante el Export-ModuleMember cmdlet . Esta opción le permite definir explícitamente qué funciones se deben exponer directamente desde dentro del archivo del .psm1 módulo de script, manteniendo todo lo demás privado de forma predeterminada.

En el ejemplo siguiente, solo se expone la Get-MrPSVersion función a los usuarios del módulo, mientras que la Get-MrComputerName función sigue siendo accesible internamente para otras funciones dentro del módulo.

function Get-MrPSVersion {
    $PSVersionTable
}

function Get-MrComputerName {
    $env:COMPUTERNAME
}

Export-ModuleMember -Function Get-MrPSVersion

Determine qué comandos están disponibles públicamente en el módulo MyScriptModule .

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

Si agrega un manifiesto de módulo al módulo, se recomienda enumerar explícitamente las funciones que desea exportar en la sección FunctionsToExport . Esta opción le proporciona control sobre lo que expone a los usuarios desde el archivo de manifiesto del .psd1 módulo.

FunctionsToExport = 'Get-MrPSVersion'

No es necesario usar tanto Export-ModuleMember en el .psm1 archivo como en la FunctionsToExport sección del manifiesto del módulo. Cualquiera de los enfoques es suficiente por sí solo.

Resumen

En este capítulo, ha aprendido a convertir las funciones en un módulo de script en PowerShell. También ha explorado los procedimientos recomendados para crear módulos de script, incluida la importancia de agregar un manifiesto de módulo para definir metadatos y administrar comandos exportados.

Revisión

  1. ¿Cómo se crea un módulo de script en PowerShell?
  2. ¿Por qué es importante usar verbos aprobados para los nombres de función?
  3. ¿Cómo se crea un manifiesto de módulo en PowerShell?
  4. ¿Cuáles son las dos maneras de exportar solo funciones específicas desde un módulo?
  5. ¿Qué condiciones se deben cumplir para que un módulo se descargue automáticamente al ejecutar uno de sus comandos?

Referencias