Distribución y actualización interna de módulos de PowerShell ── PSResourceGet y el repositorio interno
· Actualizado el: · Go Komura · PowerShell, Módulos, Distribución, Gestión de versiones, Mejora operativa, Mantenibilidad, Sistemas de información, Automatización
«Escribí un script útil, así que lo dejé en la carpeta compartida» ── a partir de ese momento empieza a acumularse, en silencio, deuda de mantenimiento. Alguien lo copia y lo modifica localmente, las correcciones al archivo original no se reflejan en esa copia, y nadie sabe qué versión está funcionando dónde. Unos años después, en la carpeta compartida conviven informe.ps1, informe_v2.ps1 e informe_corregido_final.ps1.
En el mundo de PowerShell, la respuesta a este problema es clara: convertirlo en un módulo, asignarle una versión y distribuirlo desde un repositorio. Con solo eso ya se puede responder a preguntas como «¿qué versión está instalada?» y «¿llega la actualización a todo el mundo?». Y desde PowerShell 7.4, el mecanismo para hacerlo (PSResourceGet) viene incluido de serie.
En este artículo se explica cómo convertir en módulo los scripts que se comparten internamente, levantar un repositorio interno y gestionar su distribución y actualización, con una configuración realista que no requiere montar un servidor dedicado. Para la modularización de funciones en sí, conviene leer antes «Diseño de parámetros y modularización en PowerShell», lo que facilitará la comprensión.
Público objetivo y entorno previsto
| Elemento | Contenido |
|---|---|
| Público objetivo | Personal de sistemas de información/operaciones que distribuye archivos .ps1 desde una carpeta compartida |
| Versiones cubiertas | Tanto Windows PowerShell 5.1 como PowerShell 7.x. Sin embargo, PSResourceGet solo viene incluido a partir de PowerShell 7.4; en 5.1 hace falta instalarlo de antemano tanto en el lado de distribución como en el de uso (capítulo 5)1 |
| Coexistencia con el entorno existente | PSResourceGet puede coexistir con el PowerShellGet 2.2.5 tradicional. Se puede introducir sin reescribir los scripts existentes1 |
| Entorno de verificación de los ejemplos | El código de ejemplo distribuido al final del artículo se ha verificado ejecutándolo en PowerShell 7.6 |
| Permisos necesarios | Se requieren permisos de administrador para crear y configurar la carpeta compartida del repositorio, y también para instalar con -Scope AllUsers |
1. Conclusión primero
- PSResourceGet (
Microsoft.PowerShell.PSResourceGet) viene incluido en PowerShell 7.4. Coexiste con el PowerShellGet 2.2.5 tradicional, por lo que se puede usar sin romper los scripts existentes. No viene incluido en Windows PowerShell 5.1, así que hay que instalarlo de antemano tanto en el lado de uso como en el de distribución (siguiente capítulo).1 - El repositorio interno se puede poner en marcha con un recurso compartido de archivos. Basta con indicar una ruta UNC a
Register-PSResourceRepository. No hace falta un servidor dedicado.2 - Considere obligatorio el manifiesto (
.psd1). Sin un número de versión no se puede ni actualizar ni aislar problemas.3 - No use un comodín en
FunctionsToExport; declare las funciones de forma explícita en un arreglo. Esto acelera la búsqueda de comandos y evita la exposición no intencionada de funciones internas.3 - Use versionado semántico. Si los cambios que rompen compatibilidad no se reflejan en la versión mayor, quien consume el módulo no podrá actualizar con confianza.4
- Para publicar se usa
Publish-PSResource, para obtenerInstall-PSResourcey para actualizarUpdate-PSResource.56 - Las rutas de búsqueda de módulos son distintas en 5.1 y en 7. Si no se entiende la diferencia en
$env:PSModulePath, se produce el clásico «lo instalé pero no lo encuentra».7 - Si la directiva de ejecución es
AllSigned, es obligatorio firmar con Authenticode cada archivo de script. La firma de catálogo sirve para verificar la integridad del paquete, pero no satisface la directiva de ejecución.8 - Sea prudente con la actualización automática de los módulos de los que dependen las ejecuciones desatendidas. Lo más seguro es verificarlos y luego subir de versión de forma planificada.
En conjunto, el mecanismo que se construye en este artículo tiene la siguiente forma.
flowchart LR
DEV["Lado de desarrollo<br/>El personal de sistemas escribe el módulo"]
TEST["Pruebas y análisis estático<br/>Pester / PSScriptAnalyzer"]
REPO["Repositorio interno<br/>Recurso compartido (ruta UNC) o<br/>feed compatible con NuGet"]
USER["PC del lado de uso<br/>Find / Install / Update-PSResource"]
SRV["Servidor del lado de uso<br/>Ejecución desatendida (batch nocturno, etc.)"]
DEV --> TEST
TEST -->|"Publish-PSResource"| REPO
REPO -->|"Install-PSResource"| USER
REPO -->|"Install-PSResource -Scope AllUsers"| SRV
Los comandos que aparecen sobre las flechas son el núcleo de este artículo. El repositorio se registra una única vez, al principio, tanto en el lado de distribución como en el de uso, mediante Register-PSResourceRepository (capítulo 5); a partir de ahí todo funciona solo con los comandos de publicación, obtención y actualización (capítulo 6). Que únicamente el responsable de distribución tenga permiso de escritura es el único límite de seguridad de este diagrama.
2. Los problemas del «.ps1 en la carpeta compartida»
Antes que nada, aclaremos qué problema se busca resolver.
| Síntoma | Causa raíz |
|---|---|
| No se sabe qué versión está en ejecución | No existe el concepto de número de versión |
| Las correcciones no llegan a todo el mundo | Cada quien tiene su propia copia |
| No se sabe quién lo usa | No queda registro de quién lo obtuvo (*este punto, como se explica más adelante, depende de la forma de distribución elegida) |
| Se rompe solo en algunos entornos | Las dependencias (módulos necesarios, versión de PowerShell) no están declaradas |
| Se quiere corregir, pero no se puede prever el alcance del impacto | No hay distinción entre funciones públicas y funciones internas |
La modularización y la distribución mediante repositorio resuelven directamente los cuatro primeros puntos. Sin embargo, «quién lo usa» depende únicamente del mecanismo de distribución elegido. El repositorio de recurso compartido de archivos que se presenta a partir del próximo capítulo es cómodo, pero, a cambio, no deja registro de quién lo obtuvo y cuándo (Get-InstalledPSResource solo muestra el estado del equipo en el que se ejecuta ese comando). Si necesita conocer el estado de adopción, combínelo con alguna de las siguientes opciones.
- Habilitar la auditoría de lectura en la carpeta compartida (auditoría de acceso a archivos; véase «Consultar el registro de eventos con Get-WinEvent en la práctica»)
- Usar un feed compatible con NuGet que ofrezca estadísticas de descarga (por ejemplo, Azure Artifacts)
- Ejecutar
Get-InstalledPSResourceen cada equipo y consolidar los resultados (véase «Introducción a PowerShell Remoting (WinRM)»)
3. La configuración mínima de un módulo
La forma mínima de un módulo distribuible consta de tres elementos: la carpeta, el .psm1 y el .psd1.
KsOps\
KsOps.psd1 ← manifiesto (versión, funciones públicas, dependencias)
KsOps.psm1 ← implementación (o dot-sourcing desde las carpetas Public/Private)
Public\
Get-KsShareUsage.ps1
Invoke-KsArchive.ps1
Private\
ConvertTo-KsSize.ps1
El manifiesto se genera como plantilla con New-ModuleManifest, y luego se completan los campos necesarios.3
$manifest = @{
Path = '.\KsOps\KsOps.psd1'
RootModule = 'KsOps.psm1'
ModuleVersion = '1.0.0'
GUID = [guid]::NewGuid().Guid
Author = 'Departamento de Sistemas de Información'
CompanyName = 'Empresa de Ejemplo S.A.'
Description = 'Módulo común de scripts de operación interna (inventario del servidor de archivos y archivado)'
PowerShellVersion = '5.1'
CompatiblePSEditions = @('Desktop', 'Core') # si se usa tanto en 5.1 como en 7
# No usar comodín. Declarar explícitamente solo lo que se publica
FunctionsToExport = @('Get-KsShareUsage', 'Invoke-KsArchive')
CmdletsToExport = @()
VariablesToExport = @()
AliasesToExport = @()
RequiredModules = @() # declarar aquí las dependencias, si las hay
Tags = @('internal', 'operations')
ProjectUri = 'https://git.example.co.jp/it/ksops'
}
New-ModuleManifest @manifest
El .psm1 se puede escribir con una plantilla estándar que carga los scripts de Public/Private y exporta solo las funciones públicas.
# 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 "Error al cargar el módulo: $($file.FullName) ── $_" }
}
# Solo se publican las funciones de Public (debe coincidir con lo declarado en el manifiesto)
Export-ModuleMember -Function $public.BaseName
Hay dos razones para no usar un comodín en FunctionsToExport. Una es el rendimiento del descubrimiento de comandos: al declararlo explícitamente, se puede determinar «qué comando está en qué módulo» sin analizar el módulo en sí. La otra es de diseño: si los auxiliares internos se pueden invocar desde fuera, se convierten de facto en una API pública y ya no se podrán modificar más adelante.3
En el ejemplo anterior se declaran tanto Desktop (Windows PowerShell 5.1) como Core (PowerShell 7) en CompatiblePSEditions, pero declararlo no basta para que funcione en ambos. La preparación necesaria para usar PSResourceGet en 5.1 se explica en el capítulo 5, y el «lo instalé pero no lo encuentra» que provoca la diferencia de rutas de búsqueda entre 5.1 y 7 se explica en el capítulo 7. Si tiene previsto distribuir el módulo para ambas ediciones, lea antes esos dos capítulos.
4. Cómo decidir el versionado
Que quien consume el módulo pueda actualizar con confianza depende de cómo se asignen los números de versión. Adopte el versionado semántico (mayor.menor.parche) y exprese siempre los cambios que rompen compatibilidad en la versión mayor.4
| Tipo de cambio | Qué componente subir |
|---|---|
| Cambio de nombre de parámetro, eliminación de una función, cambio en la forma del valor de retorno | Mayor (1.2.3 → 2.0.0) |
| Adición de funciones o parámetros (lo existente sigue funcionando igual) | Menor (1.2.3 → 1.3.0) |
| Solo corrección de errores | Parche (1.2.3 → 1.2.4) |
Cuando se necesite distribuir una versión de verificación, se puede usar una versión de prelanzamiento. Si se establece una cadena como beta1 en PrivateData.PSData.Prerelease del manifiesto (el guion que la separa de la versión se añade automáticamente, quedando 1.3.0-beta1), esa versión no se obtiene con una instalación normal y solo se instala cuando se especifica explícitamente -Prerelease. En la cadena solo se pueden usar caracteres alfanuméricos ASCII y guiones; no se admiten puntos ni +.4
Para el tratamiento de los cambios que rompen compatibilidad, resultan útiles los criterios de diseño de interfaces (véase «Compatibilidad hacia atrás de interfaces DLL y COM»).
5. Crear el repositorio interno ── un recurso compartido de archivos es suficiente
PSResourceGet puede tratar una carpeta de un recurso compartido de archivos como repositorio.2 Esta es la configuración con menor coste de implementación.
Primero, confirmemos el requisito previo. Viene incluido a partir de PowerShell 7.4, pero no está presente en Windows PowerShell 5.1. Para usar en 5.1 los comandos que siguen (Register-PSResourceRepository, etc.), instale el módulo de antemano.1
# 【Solo Windows PowerShell 5.1】Instalar PSResourceGet.
# Como 5.1 y 7 cargan módulos desde rutas distintas, ejecútelo en la edición que vaya a usar
if (-not (Get-Module -ListAvailable -Name Microsoft.PowerShell.PSResourceGet)) {
Install-Module -Name Microsoft.PowerShell.PSResourceGet -Scope AllUsers -Force
}
# 【Ejecutar una sola vez, tanto en el lado de distribución como en el de uso】Registrar el repositorio interno
# Trusted: se trata como confiable por ser material de distribución interna / Priority: se busca antes que PSGallery
$repo = @{
Name = 'KsInternal'
Uri = '\\fileserver\PSRepository'
Trusted = $true
Priority = 10
}
Register-PSResourceRepository @repo
Get-PSResourceRepository | Format-Table Name, Uri, Trusted, Priority
Configure los permisos de acceso de la carpeta compartida como «solo el responsable de distribución puede escribir, los usuarios solo pueden leer». Si esto queda demasiado abierto, se convierte en una vía por la que cualquiera podría distribuir código arbitrario a toda la empresa. Configure ambos tipos de permisos, los del recurso compartido y los de NTFS (el permiso efectivo será el más restrictivo de los dos).9
# Ejecutar en el servidor de archivos (requiere permisos de administrador)
$path = 'D:\PSRepository'
$null = New-Item -Path $path -ItemType Directory -Force
# Recurso compartido: los usuarios solo leen; solo el responsable de distribución escribe
New-SmbShare -Name 'PSRepository' -Path $path `
-ReadAccess 'EXAMPLE\Domain Users' -ChangeAccess 'EXAMPLE\Responsables de distribución de módulos'
# NTFS: no es "añadir", sino "sustituir por una lista de permitidos".
# Como New-Item -Force también tiene éxito sobre una carpeta ya existente, si esta
# carpeta estuvo antes publicada en otro recurso compartido, o alguien recibió permiso
# de modificación de forma temporal, con solo añadir icacls /grant esa vía de escritura
# seguiría ahí. Y como todos los equipos registran este repositorio como Trusted, la persona que la conserve podría distribuir código a toda la empresa
$acl = Get-Acl -Path $path
$acl.SetAccessRuleProtection($true, $false) # cortar la herencia sin arrastrar las ACE heredadas
foreach ($ace in @($acl.Access)) { # eliminar también las ACE asignadas directamente
[void]$acl.RemoveAccessRuleSpecific($ace)
}
# Solo se conservan los sujetos indicados aquí
$allow = @(
@{ Id = 'EXAMPLE\Responsables de distribución de módulos'; Rights = 'Modify' } # pueden publicar
@{ Id = 'EXAMPLE\Domain Users'; Rights = 'ReadAndExecute' } # solo obtienen
@{ 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'))
}
# Aplicar "vaciar" y "volver a cargar" con Set-Acl por separado deja un instante en el
# que nadie tiene acceso. Aplíquelo de una sola vez
Set-Acl -Path $path -AclObject $acl
icacls $path # confirmar el resultado; comprobar visualmente que no hay sujetos inesperados
No se conforme con añadir simplemente icacls /grant. /grant no elimina las ACE existentes. Si hay otra persona con una vía para escribir en esta carpeta, esa vía se mantiene intacta.9 Y, además, este repositorio parte de la premisa de que todos los equipos lo registran con Trusted = $true. Los módulos alojados en un repositorio de confianza se instalan sin confirmación. La persona que conserve ese acceso podría distribuir código arbitrario a todo el entorno de ejecución de PowerShell de la empresa, momento en el que se derrumba la premisa de que «solo el responsable de distribución puede publicar».
Tampoco basta con cortar la herencia (SetAccessRuleProtection($true, $false)), porque este método solo gestiona las ACE heredadas; las ACE asignadas directamente a esa carpeta quedan fuera de su alcance.9 Por eso, como en el ejemplo anterior, primero se eliminan también las ACE asignadas directamente y luego se vuelven a introducir solo los sujetos de la lista de permitidos. Si la carpeta se acaba de crear, el resultado es el mismo, pero se trata de un tipo de problema que solo aparece cuando se reutiliza una carpeta existente, así que es más seguro incluir este paso en el procedimiento.
Haga que «el responsable de distribución» sea un grupo, no una cuenta individual. En la práctica ocurre que el traslado de la persona a otro puesto deja sin posibilidad de actualizar el módulo. Si utiliza un feed compatible con NuGet que requiera autenticación, emita para el lado de uso credenciales de solo lectura, separadas de las credenciales (clave de API) destinadas a la publicación. Para el diseño de permisos de la carpeta compartida en sí, consulte también «Inventariar un servidor de archivos con PowerShell».
Para una operación más formal, registre un feed compatible con NuGet como Azure Artifacts o GitHub Packages. En los repositorios que requieren autenticación, se puede configurar para que las credenciales se obtengan desde el almacén de SecretManagement (véase «Manejo seguro de credenciales en PowerShell»).2
6. Publicación, obtención y actualización
# 【Lado de distribución】Publicar el módulo
Publish-PSResource -Path .\KsOps -Repository 'KsInternal'
# 【Lado de uso】Buscarlo e instalarlo
Find-PSResource -Name 'KsOps' -Repository 'KsInternal'
Install-PSResource -Name 'KsOps' -Repository 'KsInternal' -Scope CurrentUser
# Instalar con la versión fijada (recomendado para servidores de producción)
Install-PSResource -Name 'KsOps' -Version '1.2.3' -Repository 'KsInternal' -Scope AllUsers
# Actualizar
Update-PSResource -Name 'KsOps' -Repository 'KsInternal'
# Comprobar qué hay instalado (solo refleja el estado de "este equipo")
Get-InstalledPSResource -Name 'KsOps' | Format-Table Name, Version, Repository, InstalledDate
# Si se quiere conocer la adopción en toda la empresa, ejecutarlo en cada equipo y consolidar
Invoke-Command -ComputerName $servers -ScriptBlock {
Get-InstalledPSResource -Name 'KsOps' -ErrorAction SilentlyContinue |
Select-Object Name, Version
} | Sort-Object PSComputerName
Distinguir el uso de -Scope es importante. Los módulos que utiliza una ejecución desatendida del Programador de tareas deben instalarse en AllUsers (o en el entorno propio de la cuenta de servicio). La causa típica de «en mi entorno funciona, pero el batch nocturno falla con no se reconoce el cmdlet» es haberlo instalado en el ámbito CurrentUser.67
Además, como -Scope AllUsers escribe en una ubicación común a todos los usuarios (bajo Program Files), fallará si PowerShell no se inició como administrador.7 Si en la primera instalación aparece «acceso denegado», compruebe antes que la sesión está elevada.
7. Rutas de búsqueda de módulos y las diferencias entre 5.1 y 7
PowerShell busca los módulos en las carpetas enumeradas en $env:PSModulePath. Las rutas predeterminadas son distintas en Windows PowerShell 5.1 y en PowerShell 7.7
| Edición | Ruta predeterminada del ámbito de usuario |
|---|---|
| Windows PowerShell 5.1 | %USERPROFILE%\Documents\WindowsPowerShell\Modules |
| PowerShell 7 | %USERPROFILE%\Documents\PowerShell\Modules |
Para un módulo que se use en ambas ediciones, declare tanto Desktop como Core en CompatiblePSEditions y distribúyalo solo después de probarlo en los dos entornos. Para verificar la compatibilidad resulta útil PSUseCompatibleSyntax de PSScriptAnalyzer (véase «Mantener la calidad de los scripts de PowerShell con PSScriptAnalyzer»).
Cuando surja un problema, compruebe estos tres puntos.
$env:PSModulePath -split ';' # rutas de búsqueda
Get-Module -Name KsOps -ListAvailable # si se encuentra y en qué versión
(Get-Module KsOps -ListAvailable).ModuleBase # ubicación real desde la que se carga
8. Firma y directiva de ejecución
Existen dos mecanismos de firma con propósitos distintos, y confundirlos provoca el problema de «lo firmé, pero no se puede ejecutar».8
| Mecanismo | Qué garantiza | ¿Satisface la directiva de ejecución AllSigned? |
|---|---|---|
Firma Authenticode (Set-AuthenticodeSignature) |
El editor y la integridad de cada archivo de script individual | Sí la satisface (cada archivo debe estar firmado) |
Firma de catálogo (New-FileCatalog + firma) |
La integridad del conjunto del módulo (el paquete) | No la satisface |
La directiva de ejecución verifica la firma Authenticode del propio archivo .ps1 / .psm1 que se intenta cargar. Aunque se firme el archivo de catálogo (.cat), los archivos de script que contiene siguen sin firmar, así que en un entorno AllSigned la ejecución queda bloqueada. Por lo tanto, si se opera con AllSigned, es obligatorio firmar cada archivo que se va a ejecutar.
# (1) Obligatorio en un entorno AllSigned: firmar con Authenticode cada archivo que se ejecuta
Get-ChildItem .\KsOps -Recurse -Include *.ps1, *.psm1, *.psd1 | ForEach-Object {
Set-AuthenticodeSignature -FilePath $_.FullName -Certificate $cert `
-TimestampServer 'http://timestamp.digicert.com'
}
# (2) Además, usar un catálogo para detectar manipulaciones en el paquete completo
New-FileCatalog -Path .\KsOps -CatalogFilePath .\KsOps\KsOps.cat -CatalogVersion 2
Set-AuthenticodeSignature -FilePath .\KsOps\KsOps.cat -Certificate $cert `
-TimestampServer 'http://timestamp.digicert.com'
# Verificación en el lado de uso (aparte de la directiva de ejecución, comprueba que el paquete no esté dañado)
Test-FileCatalog -Path .\KsOps -CatalogFilePath .\KsOps\KsOps.cat -Detailed
El valor del catálogo está en poder confirmar si «el conjunto del módulo obtenido es idéntico al que se distribuyó», y PSResourceGet también dispone de -AuthenticodeCheck para verificar la firma y el catálogo.6 Entienda los roles por separado: la firma Authenticode determina si se puede ejecutar, y el catálogo garantiza la integridad del material distribuido.
Si se añade una marca de tiempo, la firma sigue siendo válida incluso después de que caduque el certificado de firma. La visión general de la directiva de ejecución y la operación de firma se resume en «Directiva de ejecución y firma de scripts en PowerShell».
9. Reglas operativas que conviene fijar de antemano
La operación importa más que la tecnología. Como mínimo, defina lo siguiente.
- Quién puede publicar. Quien tiene permiso de escritura en la carpeta compartida es quien puede distribuir código a toda la empresa
- Dónde se registra el historial de cambios. Anote los cambios que rompen compatibilidad en
ReleaseNotes(dentro dePrivateData.PSDatadel manifiesto) o en un CHANGELOG - Si el servidor de producción fija la versión. Para los módulos de los que depende una ejecución desatendida, lo seguro es fijar la versión y actualizarla de forma planificada
- El procedimiento de retirada. Al eliminar una función, suba la versión mayor y anuncie la obsolescencia con antelación
- Publicar solo después de pasar pruebas y lint. Lo ideal es que la publicación se realice desde CI (véase «Organización de pruebas de PowerShell con Pester»)
10. Reglas prácticas habituales (tabla de decisión)
| Punto a decidir | Opciones | Criterio de decisión |
|---|---|---|
| Formato de distribución | .ps1 en carpeta compartida / Módulo + repositorio |
El punto de decisión es si se puede gestionar la versión y la actualización |
| Gestión de módulos | PowerShellGet 2.x / PSResourceGet | A partir de 7.4 viene incluido. Para código nuevo, PSResourceGet1 |
| Repositorio | Recurso compartido de archivos / feed compatible con NuGet | Empiece con un recurso compartido de archivos. Si necesita autenticación o auditoría, use un feed2 |
| Manifiesto | Omitirlo / Obligatorio | Sin versión, la operación no es viable3 |
| Funciones publicadas | '*' / Declaradas explícitamente en un arreglo |
Por el rendimiento de la búsqueda y para no exponer funciones internas3 |
| Destino de instalación | CurrentUser / AllUsers para ejecución desatendida | El criterio es si resulta visible desde la cuenta de servicio7 |
| Actualización en producción | Automática / Versión fijada + actualización planificada | Para evitar que el batch nocturno pase a ejecutarse con la nueva versión sin aviso |
| Firma | Ninguna / Firma Authenticode de cada archivo (+ catálogo) | En un entorno AllSigned es obligatoria la firma por archivo. El catálogo es para verificar la integridad8 |
11. Resumen
- La distribución de
.ps1en una carpeta compartida deja sin control la versión, la actualización, las dependencias y el alcance de lo publicado. La modularización y la distribución mediante repositorio lo resuelven. Sin embargo, «quién lo usa» es la única excepción. Como el repositorio de recurso compartido de archivos no deja registro de las obtenciones, combínelo con alguna de estas opciones: auditoría de lectura de la carpeta compartida, un feed con estadísticas de descarga, o la consolidación deGet-InstalledPSResourceejecutado en cada equipo. - El manifiesto es obligatorio. Declare
FunctionsToExportde forma explícita con un arreglo y no publique las funciones internas. - El repositorio interno se puede poner en marcha con solo registrar un recurso compartido de archivos mediante una ruta UNC. La gestión del permiso de escritura es, en la práctica, el límite de seguridad.
- La publicación se hace con
Publish-PSResource, la obtención conInstall-PSResourcey la actualización conUpdate-PSResource. Instale en el ámbitoAllUserslos módulos que use una ejecución desatendida. - Las rutas de búsqueda son distintas en 5.1 y en 7. Si el módulo debe funcionar en ambas, declare
CompatiblePSEditionsy pruébelo en los dos entornos. - En un entorno
AllSignedcada archivo de script necesita firma Authenticode. La firma de catálogo sirve para verificar la integridad del material distribuido y es independiente de si se puede ejecutar. No olvide añadir siempre la marca de tiempo.
Descarga del código de ejemplo
El código tratado en este artículo se distribuye ya empaquetado, listo para ejecutarse. Incluye un conjunto completo de módulo con las funciones públicas y privadas separadas, además del procedimiento de distribución al repositorio interno.
Descargar el código de ejemplo (zip)
Los ejemplos de este artículo se han verificado ejecutándolos realmente en PowerShell 7.6 (16 pruebas de Pester). Si ejecuta Invoke-SampleTests.ps1, incluido en el zip, podrá reproducir la misma verificación en su propio equipo.
# Análisis de sintaxis + análisis estático + pruebas de Pester
./Invoke-SampleTests.ps1
Los valores de configuración (rutas, nombres de servidor, ID de inquilino, etc.) son ejemplos. No los ejecute tal cual en un entorno de producción; adáptelos al entorno de su propia empresa.
Artículos relacionados
- Diseño de parámetros y modularización de scripts de PowerShell ── de «un script que funciona» a «un script que se puede entregar a otra persona»
- Directiva de ejecución y firma de scripts en PowerShell ── dejar atrás la operación de «taparlo todo con Bypass»
- Mantener la calidad de los scripts de PowerShell con PSScriptAnalyzer ── selección de reglas e integración en CI
- Organización de pruebas de PowerShell con Pester ── un patrón práctico para que los scripts operativos sean más difíciles de romper
- Manejo seguro de credenciales en PowerShell ── eliminar las contraseñas en texto claro de los scripts
- Medidas contra la dependencia de una sola persona, para que nada se detenga «aunque quien lo creó se vaya»
Áreas de consultoría relacionadas
KomuraSoft LLC se ocupa de la modularización de los activos de scripts internos y la puesta a punto de la infraestructura de distribución, la estandarización de operaciones que dependen de una sola persona, y la mejora de la mantenibilidad de scripts existentes.
- Consultoría técnica y revisión de diseño
- Migración y aprovechamiento de activos existentes
- Modificación y mantenimiento de software Windows existente
- Contacto
Referencias
-
Microsoft Learn, Package management for PowerShell. Sobre que Microsoft.PowerShell.PSResourceGet es el módulo que sustituye a PowerShellGet y PackageManagement, que viene incluido en PowerShell 7.4 y coexiste con el PowerShellGet 2.2.5 tradicional, y que también se puede instalar en Windows PowerShell 5.1 desde PowerShell Gallery. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Register-PSResourceRepository. Sobre cómo registrar un repositorio indicando en -Uri una carpeta local, un recurso compartido de archivos (ruta UNC) o la URL de un feed compatible con NuGet, la configuración de confianza con -Trusted, el orden de búsqueda mediante -Priority, y la especificación de credenciales en repositorios que requieren autenticación. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, How to write a PowerShell module manifest. Sobre la creación del manifiesto con New-ModuleManifest, claves como RootModule, ModuleVersion, GUID, PowerShellVersion, CompatiblePSEditions y RequiredModules, y la razón (rendimiento del descubrimiento de comandos) por la que las declaraciones de exportación como FunctionsToExport deben especificarse de forma explícita en lugar de con comodines. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Prerelease module versions. Sobre el versionado basado en versionado semántico, la especificación de versiones de prelanzamiento mediante PrivateData.PSData.Prerelease, y el hecho de que las versiones de prelanzamiento no se obtienen de forma predeterminada. ↩ ↩2 ↩3
-
Microsoft Learn, Publish-PSResource. Sobre cómo publicar en el repositorio la carpeta de módulo indicada en -Path, la especificación del destino de publicación mediante -Repository, y la autenticación mediante -ApiKey. ↩
-
Microsoft Learn, Install-PSResource. Sobre la instalación mediante -Name / -Version / -Repository, la especificación del destino con -Scope (CurrentUser / AllUsers), y la omisión de la confirmación con -TrustRepository. Junto con la actualización mediante Update-PSResource. ↩ ↩2 ↩3
-
Microsoft Learn, about_PSModulePath. Sobre cómo PowerShell busca los módulos en las carpetas enumeradas en $env:PSModulePath, y sobre las diferencias entre Windows PowerShell y PowerShell 7 en las rutas predeterminadas de los ámbitos de usuario y de todos los usuarios. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, New-FileCatalog. Sobre cómo generar un archivo de catálogo (.cat) que incluye los hash de los archivos de una carpeta, la posibilidad de firmar el catálogo con Set-AuthenticodeSignature, y cómo Test-FileCatalog permite detectar manipulaciones comparando el catálogo con el conjunto de archivos. Sobre el hecho de que lo que verifica la directiva de ejecución es la firma Authenticode del propio archivo de script que se ejecuta, véase about_Execution_Policies (en AllSigned solo se pueden ejecutar scripts firmados por un editor de confianza) y Set-AuthenticodeSignature (aplicar una firma Authenticode a un archivo, y añadir una marca de tiempo mediante -TimestampServer). ↩ ↩2 ↩3
Artículos relacionados
Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.
Automatizar el kitting de PC con winget + PowerShell ── Convertir el manual de procedimientos en algo ejecutable
Resumimos cómo hacer reproducible la configuración de PC para nuevos empleados: instalación de aplicaciones con winget, export/import, co...
Deje de usar Write-Host — Flujos de salida de PowerShell y diseño de registros
Explica los seis flujos de salida de PowerShell, los problemas de Write-Host y su uso correcto, por qué se contamina el valor de retorno ...
Introducción a Microsoft Graph PowerShell — la gestión de Microsoft 365 tras la baja de AzureAD y MSOnline
Guía práctica para migrar la gestión de Microsoft 365 a Microsoft Graph PowerShell tras la baja de AzureAD y MSOnline: conexión y permiso...
Endurecimiento de la seguridad de PowerShell — registro, AMSI, modo de lenguaje y JEA
Resumen práctico para usar PowerShell con seguridad sin prohibirlo: registro de bloques de script y transcripción, deshabilitar AMSI y ve...
Investigar el registro de eventos con Get-WinEvent de forma práctica — la velocidad del filtrado determina el tiempo de investigación
Cómo agilizar la investigación del registro de eventos de Windows con PowerShell. Por qué filtrar con Where-Object es lento, cuándo usar ...
Temas relacionados
Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.
Temas técnicos de Windows
Portal sobre desarrollo de Windows, investigación de fallos y aprovechamiento de activos existentes.
Preguntas frecuentes
Preguntas habituales en las consultas sobre el tema del artículo.
- ¿En qué se diferencian PowerShellGet y PSResourceGet? ¿Cuál debo usar?
- PSResourceGet (Microsoft.PowerShell.PSResourceGet) es el nuevo mecanismo de gestión de módulos que sustituye a los antiguos PowerShellGet y PackageManagement, y viene incluido de serie en PowerShell 7.4. Puede coexistir con el PowerShellGet 2.2.5 tradicional, por lo que es posible migrar sin romper los scripts existentes. Para código nuevo, se recomienda usar PSResourceGet, cuyos cmdlets siguen la serie -PSResource (Install-PSResource, Publish-PSResource, etc.). También puede utilizarse en Windows PowerShell 5.1 si se instala desde PowerShell Gallery.
- ¿Necesito un servidor dedicado para levantar un repositorio interno?
- No es necesario. La forma más sencilla es registrar una carpeta de un recurso compartido de archivos como repositorio: basta con indicar la ruta UNC a Register-PSResourceRepository para que funcione. No hace falta ni un servidor dedicado ni una base de datos. Si la organización necesita control de acceso o auditoría más estrictos, o si también se debe obtener el módulo desde fuera de la empresa, conviene usar un feed compatible con NuGet como Azure Artifacts o GitHub Packages. Lo más realista es empezar con un recurso compartido de archivos y migrar cuando surja la necesidad.
- ¿Es imprescindible el manifiesto de módulo (.psd1)?
- En la práctica, considérelo obligatorio. Un archivo .psm1 por sí solo ya se puede cargar como módulo, pero sin manifiesto no hay número de versión, y entonces no se sabe «qué versión está instalada». Sin versión tampoco se puede gestionar la actualización ni aislar el origen de un problema cuando surge. Además, el manifiesto permite declarar explícitamente las funciones que se exportan, los módulos de los que depende y las versiones y ediciones de PowerShell compatibles. Como New-ModuleManifest genera la plantilla automáticamente, el coste de crearlo es mínimo.
- ¿Qué problema causa escribir '*' en FunctionsToExport?
- El descubrimiento automático de comandos se vuelve más lento y, además, se exponen funciones internas que no debían publicarse. PowerShell necesita saber qué comando pertenece a qué módulo antes de cargarlo, y con un comodín solo puede averiguarlo analizando el propio módulo. Si se declaran explícitamente las funciones que se publican mediante un arreglo, ese análisis deja de ser necesario. Además, si las funciones auxiliares de uso interno se pueden invocar desde fuera, pasan a ser en la práctica una API pública y ya no se podrán modificar más adelante.
- ¿Se puede hacer que el módulo distribuido se actualice automáticamente en el lado de uso?
- Sí, se puede actualizar con Update-PSResource, pero diseñe con cuidado la actualización automática de los módulos de los que dependen los scripts de producción. El motivo es que un proceso desatendido nocturno pasaría a ejecutarse con la nueva versión sin que nadie se dé cuenta. En la práctica, lo más seguro es verificar la nueva versión en un entorno de pruebas y luego planificar la actualización en producción. Si aun así necesita automatizarlo, incorpore algún freno, como fijar la versión mayor al actualizar (por ejemplo, con un rango como -Version '1.*').
Perfil del autor
Página de presentación del autor del artículo.
Go Komura
Representante de KomuraSoft LLC
Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.