> For the complete documentation index, see [llms.txt](https://docs.devolutions.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.devolutions.net/powershell-universal/es/config/best-practices.md).

# Prácticas recomendadas

## General

### Considere una configuración inmutable en producción

PowerShell Universal se basa en un sistema de configuración mediante scripts. Esto permite configurar nuevas instancias de PowerShell Universal sin llamadas a la base de datos y directamente desde un repositorio de scripts. Esto significa que, al desplegar en producción, resulta ventajoso utilizar un artefacto bien probado como base de su configuración de PowerShell Universal.

Puede utilizar herramientas como Docker Compose para incorporar los ficheros de configuración de PowerShell Universal en una imagen de contenedor sin tener que actualizar manualmente un sistema de producción. PowerShell Universal también ofrece una función de [Despliegue ](/powershell-universal/es/config/deployments.md)que puede utilizarse para publicar configuraciones directamente en una instancia. A continuación, puede utilizar un sistema de despliegue continuo para actualizar sus instancias de producción como parte de un flujo de trabajo automatizado.

Según sus necesidades, normalmente en función del tamaño del equipo y del uso, este tipo de configuración puede no ser necesario.

### Desactive la edición Code First cuando no esté en uso

La función de edición Code First detecta cambios en el directorio del repositorio mediante un observador del sistema de ficheros. Esta función es útil cuando se desarrolla una configuración de PowerShell Universal directamente desde el sistema de ficheros con herramientas como Visual Studio Code. Dicho esto, puede tener efectos secundarios inesperados si no utiliza la función. Por ejemplo, escribir ficheros en el repositorio, como registros, puede provocar que el sistema de configuración de PowerShell Universal se recargue innecesariamente.

{% hint style="info" %}
El directorio del repositorio es por defecto `C:\ProgramData\UniversalAutomation\Repository`
{% endhint %}

Si realiza cambios a través de la consola de administración, mediante la sincronización con git o utilizando despliegues, debería desactivar la recarga automática. Esto aumentará el rendimiento y también reducirá el riesgo de cambios inesperados en su configuración desplegada.

<figure><img src="/files/TmCcPPZoCd68ioKfMwCf" alt=""><figcaption></figcaption></figure>

### Priorice los entornos no integrados

{% hint style="info" %}
Esto no se refiere al parámetro de conmutador `-Integrated` de los cmdlets de PSU y solo se aplica a los entornos.
{% endhint %}

Aunque el entorno integrado es rápido y fácil de usar, ejecuta todas sus operaciones de PowerShell dentro del servicio de PowerShell Universal. Los problemas con un único script o endpoint pueden afectar a la estabilidad del sistema.

Al utilizar entornos no integrados, se inicia un proceso externo de PowerShell. En el caso de las API y los paneles, ese proceso puede ser de larga duración, pero se puede reiniciar sin afectar al resto del sistema. Con los trabajos y los terminales, se inicia un nuevo proceso para cada instancia del trabajo y del terminal. Cuando los trabajos y los terminales se detienen, el proceso finaliza y todos los recursos consumidos por ese proceso son recuperados por el sistema.

Además, al cargar módulos en el entorno integrado, el espacio de proceso puede contaminarse con diferentes versiones de DLL comunes que PSU podría estar utilizando por sí mismo. Esto puede causar problemas de enlace de ensamblados que pueden hacer que los módulos importados no funcionen como se espera.

Los entornos de PowerShell 7 y Windows PowerShell 5.1 tendrán la mayor compatibilidad con los módulos, ya que aprovechan el ejecutable `Universal.Agent`, que puede controlar la carga de ensamblados, a diferencia de `pwsh.exe` o `powershell.exe`.

### Aísle los módulos problemáticos

Los módulos complejos de PowerShell pueden causar problemas con PowerShell Universal. Ciertos módulos no están diseñados para alojarse en un proceso de larga duración como PowerShell Universal. Conviene utilizar estos módulos en operaciones transitorias como los trabajos.

Por ejemplo, dbatools puede provocar fugas de conexiones de base de datos cuando se utiliza directamente en el entorno integrado de PowerShell Universal. Para evitarlo, puede iniciar un proceso externo ejecutando un trabajo de PowerShell Universal en un entorno no integrado. El script se ejecutará, el proceso finalizará y la conexión de base de datos se recuperará automáticamente.

A continuación se muestra una lista de algunos módulos con los que hemos experimentado problemas.

* VMware PowerCLI: la gestión de conexiones está limitada al ámbito del proceso
* Az: la gestión de conexiones está limitada al ámbito del proceso

### Aproveche los módulos personalizados

Crear módulos personalizados garantiza que pueda utilizar la misma funcionalidad en toda la plataforma PowerShell Universal sin duplicar código. Puede usar las mismas funciones en API, scripts y paneles sin tener que duplicar la lógica.

Reducir la cantidad de script en cualquiera de estos lugares puede ayudarle a probar y aislar mejor los problemas causados por la integración con la plataforma o por el propio módulo.

Considere también crear funciones para encapsular componentes complejos del panel. Esto reduce la complejidad general del script del panel y facilita su depuración y lectura.

### Establezca un tiempo de vida para la caché

Al utilizar `Set-PSUCache`, asegúrese de establecer algún tipo de tiempo de vida para la caché. Esto es especialmente importante si tiene datos que no se utilizan todo el tiempo y que son de gran tamaño. Los datos almacenados en la caché sin un tiempo de vida nunca se devuelven al sistema.

Por ejemplo, puede utilizar la expiración deslizante para que los datos de la caché expiren si no se utilizan durante un tiempo de una hora.

```powershell
Set-PSUCache -Key 'Data' -Value (Get-Date) -SlidingExpiration (New-Timespan -Hours 1)
```

### Limite o particione el tamaño de la caché persistente

La caché persistente almacena los datos en la base de datos de PSU. Los datos se serializan con la [clase PSSerializer](https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.psserializer?view=powershellsdk-7.4.0) que forma parte del SDK de PowerShell. Este formato de datos es el mismo que utiliza PowerShell Remoting. Al ser un formato XML, estos datos serán significativamente más grandes que el almacenamiento binario. Almacenar muchos objetos en un único elemento en caché dará como resultado un rendimiento de deserialización deficiente una vez que los datos alcancen ciertos límites.

Esto puede afectar negativamente al servidor de PowerShell Universal porque necesita recuperar y luego deserializar una cadena grande cada vez que se leen los datos de la caché.

Considere seleccionar subconjuntos más pequeños de los objetos que desea almacenar. Por ejemplo, seleccione solo las propiedades que necesita en lugar del objeto completo.

```powershell
$Data = Get-ADUser -Properties CN,Enabled 
Set-PSUCache -Key Users -Value $Data
```

Considere almacenar los datos en particiones más pequeñas. Si es posible, segmente los datos en fragmentos más pequeños para evitar recuperar un valor grande de la base de datos.

```powershell
$OU1 = Get-ADUser -SearchBase "OU=OU1,DC=PSU" -Properties CN,Enabled 
Set-PSUCache -Key Users_OU1 -Value $OU1
$OU2 = Get-ADUser -SearchBase "OU=OU2,DC=PSU" -Properties CN,Enabled 
Set-PSUCache -Key Users_OU2 -Value $OU2
```

### Reduzca el nivel de registro en producción

Los niveles de registro predeterminados de PowerShell Universal son detallados. Para mejorar el rendimiento del sistema, puede reducir los niveles de registro. Esto debe hacerse en dos lugares.

En primer lugar, conviene reducir el ajuste System Log Level. Se encuentra en `appsettings.json`. Considere cambiar el nivel de Verbose a Warning o Error.

```json
{
    "SystemLogLevel": "Error"
}
```

En segundo lugar, puede ajustar los destinos de registro para modificar los niveles o reducir las funciones que se registran.

```powershell
New-PSULoggingTarget -Type "File" -Properties @{
    path = "C:\ProgramData\PowerShellUniversal\log.txt"
} -Level 'Error'

New-PSULoggingTarget -Type "Database" -Properties @{
} -Level 'Error'
```

## API

### Evite devolver objetos muy complejos

Por defecto, los endpoints de API serializarán los objetos devueltos a JSON mediante `ConvertTo-Json`. Aunque la plataforma restringe la profundidad del JSON, los objetos muy complejos pueden hacer que el cmdlet se descontrole y consuma grandes cantidades de CPU. PowerShell Universal intentará cancelar este procesamiento si lo detecta, pero seguirá causando problemas en su entorno de API.

Asegúrese de comprender la complejidad de los objetos que devuelve. Si los objetos son demasiado complejos, considere utilizar `Select-Object` para seleccionar un subconjunto de los datos devueltos. También puede llamar a `ConvertTo-Json` usted mismo para controlar el parámetro `-Depth`.

Un ejemplo de esto sería devolver objetos Process con `Get-Process`. Debido a la complejidad del tipo Process, causa problemas durante la serialización. En su lugar, seleccione solo un subconjunto de las propiedades necesarias.

```powershell
Get-Process | Select-Object Name,Id
```

### Evite los procesos de larga duración en las API

El grupo de subprocesos HTTP tiene un tamaño limitado. Los procesos de larga duración en las API pueden agotar el grupo, lo que puede causar problemas en todo el servidor de PowerShell Universal. Si prevé tener una API que tarde más de unos segundos, considere que la API inicie un trabajo. Después puede crear una segunda API para comprobar el estado de los trabajos devueltos por la primera API. Esto garantizará que la operación siga procesándose pero que el grupo de subprocesos HTTP recupere la conexión disponible.

Por ejemplo, podría tener las siguientes API. El primer endpoint inicia un trabajo y devuelve el ID del trabajo. El segundo endpoint recupera la salida de la canalización del trabajo especificado.

```powershell
New-PSUEndpoint -Url '/createReport' -Method POST -Endpoint {
    (Invoke-PSUScript -Name CreateReport.ps1 -Integrated)
}

New-PSUEndpoint -Url '/createReport/:id' -Method GET -Endpoint {
     Get-PSUJob -Id $Id -Integrated | Get-PSUJobPipelineOutput -Integrated
}
```

Para llamar a estos endpoints, podríamos hacer lo siguiente con `Invoke-RestMethod`.

```powershell
$Id = Invoke-RestMethod http://localhost:5000/createReport -Method POST
Start-Sleep 5
Invoke-RestMethod http://localhost:5000/createReport/$Id -Method GET
```

## Automatización

### Reduzca la salida de trabajos innecesaria

Aunque almacenar la salida de los trabajos es útil para la auditoría, almacenar toda la salida de los trabajos puede hacer que su almacenamiento crezca desproporcionadamente, lo que a su vez ralentizará el rendimiento de su sistema PowerShell Universal. Algunas medidas que puede tomar para mantener bajo control la salida de los trabajos son las siguientes.

#### Descarte la salida de la canalización

Si no va a utilizar la salida de la canalización, puede indicar a PowerShell Universal que la descarte. Esto reducirá la cantidad de datos almacenados y también aumentará el rendimiento de sus trabajos, ya que el sistema no necesita serializar toda la salida para almacenarla. Seguirá viendo sus flujos de salida en el registro del trabajo.

#### Aproveche los flujos

Utilizar los flujos Debug, Warning y Error puede ayudar a reducir lo que se muestra en el trabajo por defecto. Establecer la preferencia de acción por flujo puede permitirle desactivar ciertos flujos en las operaciones habituales, pero habilitarlos cuando el trabajo presente problemas.

Por ejemplo, si utiliza `Write-Debug` a lo largo de su script, puede desactivarlo mediante la variable `$DebugActionPreference` estableciéndola en `SilentlyContinue`. Si el trabajo comenzara a presentar problemas, podría establecerla en `Continue` para ver la salida en el registro.

#### Utilice Out-Null

`Out-Null` puede capturar la posible salida de la canalización y descartarla. Si no quiere descartar toda la salida de la canalización, puede descartar parte de ella utilizando `Out-Null`. Esto mejorará el rendimiento y reducirá el tamaño de los datos de sus trabajos.

### Depure de forma agresiva los trabajos poco importantes

Algunos trabajos, como un disparador utilizado para notificaciones, es posible que casi nunca se revisen. En este caso, considere establecer un historial de trabajos muy bajo.

## Aplicaciones

### Utilice funciones en las aplicaciones

Al crear secciones complejas de una aplicación, se recomienda encapsularlas en una función para organizar y aislar mejor esa sección compleja. Los paneles con muchos niveles de anidamiento son difíciles de depurar y facilitan la introducción de errores de sintaxis que afectarán a toda la aplicación.

También recomendamos utilizar módulos para almacenar sus funciones y reducir aún más el tamaño y la complejidad del script principal de su aplicación. Además, los módulos pueden compartirse después entre aplicaciones.

Un ejemplo sería encapsular la lógica de una tabla dentro de una función y luego utilizar la función dentro de la aplicación.

```powershell
function New-ProcessTable {
    $Data = Get-Process
    $Columns = @( 
        New-UDTableColumn -Title 'Name' -Property 'Name'
        New-UDTableColumn -Title 'Id' -Property 'Id'
    )
    New-UDTable -Data $Data -Columns $Columns -ShowSearch
}

New-UDApp -Content {
    New-ProcessTable
}
```

### Considere aprovechar los trabajos

Los trabajos son útiles porque inician un proceso externo y pueden utilizarse para auditar las interacciones con la aplicación. Como las aplicaciones son de larga duración, ciertas operaciones y módulos pueden empezar a causar problemas de memoria o de otros recursos si se utilizan bajo carga. Iniciar trabajos garantiza que el entorno se recupere después de cada ejecución.

Los trabajos tienen sentido para operaciones que realizan cambios (p. ej., crear una máquina virtual o un usuario), pero sus características de rendimiento no funcionarán en todos los escenarios.

Un ejemplo sería llamar a un trabajo desde un formulario.

```powershell
New-UDForm -Content {
   New-UDTextbox -Id 'UserName' -Label 'UserName'
} -OnSubmit {
   Invoke-PSUScript -Name 'CreateUser.ps1' -UserName $EventData.UserName -Environment PS7 -Integrated -Wait
   Show-UDToast "User $($EventData.UserName) was created!"
}
```

### Programe reinicios de las aplicaciones

Si experimenta problemas con los recursos de las aplicaciones, puede reiniciarlas mediante un trabajo programado que se ejecute fuera del horario laboral. Puede utilizar los cmdlets `Get-PSUApp`, `Stop-PSUApp` y `Start-PSUApp` para reiniciar las aplicaciones individuales. Esta técnica solo es válida cuando las aplicaciones se ejecutan en entornos no integrados.

### Tenga en cuenta el rendimiento de -Render con New-UDTable

Una función de `New-UDTable` y `New-UDTableColumn` es la capacidad de renderizar columnas para que contengan cualquier componente que desee mostrar en función de la fila de datos que proporcione a la tabla. Esto suele ser útil para personalizar el aspecto o para proporcionar acciones para los datos de la fila. El renderizado puede convertirse en un problema de rendimiento si se utiliza incorrectamente. Renderizar muchas filas a la vez o utilizar el ScriptBlock de renderizado para ejecutar procesos de larga duración causará problemas.

#### Renderizar demasiadas filas

Si utiliza el parámetro `-Data` de `New-UDTable`, el ScriptBlock `-Render` se llamará para cada elemento que pase al parámetro de datos. Si tiene cientos o miles de elementos, esto hará que aumenten los tiempos de carga de la página.

Considere utilizar `-LoadData` para cargar y mostrar solo una página de datos a la vez. Esto solo llama a `-Render` para los elementos mostrados y no para todo el conjunto de datos.

#### Renderizados de larga duración

Debido a los detalles de implementación de `-Render`, no se recomienda utilizar operaciones de renderizado de larga duración. Si espera que su `-Render` tarde más de unos milisegundos, considere utilizar `New-UDDynamic` para trasladar el renderizado al servidor y mostrar un esqueleto de carga. En este caso, el servidor puede programar de forma eficiente la operación de renderizado utilizando el grupo de runspaces.

A continuación se muestra un ejemplo de esto.

```powershell
$Data = @(
    @{Dessert = 'Frozen yoghurt'; Calories = 1; Fat = 6.0; Carbs = 24; Protein = 4.0 }
    @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0 }
    @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0 }
    @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0 }
    @{Dessert = 'Gingerbread'; Calories = 200; Fat = 6.0; Carbs = 24; Protein = 4.0 }
) 

$Columns = @(
    New-UDTableColumn -Property Dessert -Title Dessert -Render { 
        New-UDDynamic -Content {
            Start-Sleep (Get-Random -Min 1 -Max 5)
            New-UDButton -Text "Click for Dessert!" -OnClick { Show-UDToast -Message $EventData.Dessert } -Variant 'text'
        } -LoadingComponent {
            New-UDSkeleton
        }
    }
    New-UDTableColumn -Property Calories -Title Calories 
    New-UDTableColumn -Property Fat -Title Fat 
    New-UDTableColumn -Property Carbs -Title Carbs 
    New-UDTableColumn -Property Protein -Title Protein 
)
```

El resultado es una tabla que se carga inmediatamente pero que muestra esqueletos de carga en las columnas de renderizado lento.

<figure><img src="/files/wDdYQl40s8MaPdBjDVaj" alt=""><figcaption></figcaption></figure>

## Portal

### Evite llamadas a cmdlets de terceros en los widgets

Cuando sea posible, evite llamar a cmdlets de terceros directamente en los widgets. Cada widget tiene su propio runspace, pero todos los widgets se ejecutan en el proceso del servidor de PowerShell Universal. Llamar a cmdlets directamente en los widgets provoca que se carguen ensamblados .NET en el servidor. Esto puede causar conflictos con otras cargas de ensamblados. Además, es posible que la memoria no se recupere de los comandos llamados en el runspace.

Utilizar `Invoke-PSUScript` y ejecutar un script en un entorno externo garantiza que todas las cargas de ensamblados queden contenidas en un proceso separado y que toda la memoria se recupere después de que el proceso finalice.

Para una buena experiencia de usuario, puede que no siempre tenga sentido iniciar un trabajo para cada interacción en una página. Considere utilizar los cmdlets de PSUCache para establecer y recuperar datos de la caché y así mejorar los tiempos de carga de la página.

No todos los módulos causarán problemas, por lo que esta técnica puede no ser siempre necesaria. Es muy poco probable que los módulos de script causen problemas con la carga de ensamblados.

## Infraestructura y alojamiento

### Servidor dedicado de PowerShell Universal

Considere un servidor dedicado de PowerShell Universal para alojar la plataforma. Evite instalarlo junto a otras plataformas que puedan generar contención de recursos con la aplicación. PowerShell Universal utiliza funciones estándar de PowerShell como módulos, políticas de ejecución y configuración de remoting, y otras plataformas pueden configurar estos ajustes de formas con las que PSU podría no funcionar bien.

### Utilice MS SQL o PostgreSQL en producción

Evite usar SQLite en casos de uso de producción. No ofrece la capacidad de escalar cuando aumentan las cargas de trabajo. Impide que varios servidores PSU utilicen el mismo almacén de datos. No escala al alcanzar tamaños superiores a 2 GB.

Migrar de una base de datos SQLite a una base de datos SQL puede ser propenso a errores y llevar mucho tiempo. Si está considerando una implementación para uso en producción, le recomendamos empezar con un servidor SQL centralizado.

### Realice copias de seguridad programadas de la base de datos

Recomendamos hacer copias de seguridad de la base de datos a intervalos regulares. Aunque los datos de configuración se almacenan principalmente en el repositorio, la base de datos contiene recursos como App Tokens, identidades locales, historial de trabajos y más. Las copias de seguridad regulares también ayudan a recuperarse de actualizaciones de esquema fallidas y permiten volver a versiones anteriores del esquema sin necesidad de realizar una degradación del esquema.

### Clúster de base de datos para redundancia

Recomendamos utilizar un clúster de conmutación por error de base de datos, si es posible. Esto permite una recuperación rápida y limita el tiempo de inactividad del servidor PowerShell Universal si uno de los servidores SQL quedara sin conexión.

### Equilibrio de carga

Considere emplear un equilibrador de carga delante de sus instancias de PowerShell Universal. Los usuarios serán dirigidos al servidor menos ocupado o conmutarán desde los servidores sin conexión. Esto también proporciona una mejor manera de escalonar las actualizaciones de la aplicación PowerShell Universal.

PowerShell Universal admite equilibradores de carga como F5.

### Utilice HTTPS

[HTTPS ](/powershell-universal/es/config/hosting.md#configuring-https)no solo proporciona seguridad frente a atacantes que escuchan en la red, sino que también ofrece un mejor rendimiento al usar los cmdlets de PowerShell Universal. Estos dependen de funciones de HTTPS al comunicarse con la plataforma y recurren a tecnologías de comunicación heredadas si no están disponibles.

### Envío de registros

Los ficheros de registro del sistema se escriben en la carpeta `%ProgramData%\PowerShellUniversal\Logs` y se purgarán después de 30 días. Cada día se escribe un nuevo fichero de registro, de forma predeterminada. Considere enviar los registros de este directorio a su SIEM si desea conservarlos más de 30 días.

También puede configurar [destinos de registro](/powershell-universal/es/desarrollo/logging.md#targets) para enviar mensajes de registro directamente a un SIEM.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.devolutions.net/powershell-universal/es/config/best-practices.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
