> 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/it/config/best-practices.md).

# Best practice

## Generale

### Valutare una configurazione immutabile in produzione

PowerShell Universal è basato su un sistema di configurazione basato su script. Questo consente di configurare nuove istanze di PowerShell Universal senza chiamate al database e direttamente da un repository di script. Ciò significa che, durante la distribuzione in produzione, è vantaggioso utilizzare un artefatto ben testato come base per la configurazione di PowerShell Universal.

È possibile utilizzare strumenti come Docker Compose per inserire i file di configurazione di PowerShell Universal in un'immagine di container senza dover aggiornare manualmente un sistema di produzione. PowerShell Universal fornisce anche una funzionalità di [Distribuzione ](/powershell-universal/it/config/deployments.md)che può essere utilizzata per pubblicare le configurazioni direttamente in un'istanza. È quindi possibile utilizzare un sistema di distribuzione continua per aggiornare le istanze di produzione come parte di un flusso di lavoro automatizzato.

A seconda delle sue esigenze, tipicamente in base alle dimensioni del team e all'utilizzo, questo tipo di configurazione potrebbe non essere necessario.

### Disabilitare la modifica Code First quando non è in uso

La funzionalità Code First Editing rileva le modifiche nella directory del repository utilizzando un file system watcher. Questa funzionalità è utile quando si sviluppa una configurazione di PowerShell Universal direttamente dal file system utilizzando strumenti come Visual Studio Code. Detto questo, può avere effetti collaterali imprevisti se non si utilizza la funzionalità. Ad esempio, la scrittura di file nel repository, come i log, può causare il ricaricamento non necessario del sistema di configurazione di PowerShell Universal.

{% hint style="info" %}
La directory del repository predefinita è `C:\ProgramData\UniversalAutomation\Repository`
{% endhint %}

Se si apportano modifiche tramite la console di amministrazione, tramite la sincronizzazione git o utilizzando le distribuzioni, è consigliabile disabilitare il ricaricamento automatico. Ciò aumenterà le prestazioni e ridurrà anche il rischio di modifiche impreviste alla configurazione distribuita.

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

### Preferire ambienti non integrati

{% hint style="info" %}
Questo non si riferisce al parametro switch `-Integrated` dei cmdlet PSU e si applica solo agli ambienti.
{% endhint %}

Sebbene l'ambiente integrato sia veloce e facile da usare, esegue tutte le operazioni PowerShell all'interno del servizio PowerShell Universal. Problemi con un singolo script o endpoint possono influire sulla stabilità del sistema.

Quando si utilizzano ambienti non integrati, viene avviato un processo PowerShell esterno. Per API e Dashboard, tale processo può essere di lunga durata ma può essere riavviato senza influire sul resto del sistema. Con job e terminali, viene avviato un nuovo processo per ogni istanza del job e del terminale. Quando i job e i terminali vengono arrestati, il processo viene terminato e tutte le risorse consumate da tale processo vengono recuperate dal sistema.

Inoltre, quando si caricano moduli nell'ambiente integrato, lo spazio del processo può risultare inquinato da versioni diverse di DLL comuni che PSU potrebbe utilizzare a sua volta. Ciò può causare problemi di binding degli assembly che possono impedire ai moduli importati di funzionare come previsto.

Gli ambienti PowerShell 7 e Windows PowerShell 5.1 avranno la maggiore compatibilità con i moduli perché sfruttano l'eseguibile `Universal.Agent` che può controllare il caricamento degli assembly, a differenza di `pwsh.exe` o `powershell.exe`.

### Isolare i moduli problematici

I moduli PowerShell complessi possono causare problemi con PowerShell Universal. Alcuni moduli non sono progettati per essere ospitati in un processo di lunga durata come PowerShell Universal. È consigliabile utilizzare questi moduli in operazioni transitorie come i job.

Ad esempio, dbatools può causare la perdita di connessioni al database quando viene utilizzato direttamente all'interno dell'ambiente integrato di PowerShell Universal. Per evitare questo, è possibile avviare un processo esterno eseguendo un job di PowerShell Universal in un ambiente non integrato. Lo script verrà eseguito, il processo terminerà e la connessione al database verrà recuperata automaticamente.

Di seguito è riportato un elenco di alcuni moduli con cui abbiamo riscontrato problemi.

* VMware PowerCLI - La gestione delle connessioni è limitata all'ambito del processo
* Az - La gestione delle connessioni è limitata all'ambito del processo

### Sfruttare i moduli personalizzati

La creazione di moduli personalizzati garantisce di poter utilizzare le stesse funzionalità in tutta la piattaforma PowerShell Universal senza duplicare il codice. È possibile utilizzare le stesse funzioni in API, script e dashboard senza dover duplicare la logica.

Ridurre la quantità di script in ognuno di questi punti può aiutare a testare e isolare meglio i problemi causati dall'integrazione con la piattaforma o dal modulo stesso.

Consideri anche la creazione di funzioni per incapsulare componenti dashboard complessi. Questo riduce la complessità complessiva dello script della dashboard e ne facilita il debug e la lettura.

### Impostare una durata della cache

Quando si utilizza `Set-PSUCache`, si assicuri di impostare una qualche forma di durata per la cache. Questo è particolarmente importante se si dispone di dati che non vengono utilizzati continuamente e che sono di grandi dimensioni. I dati inseriti nella cache senza una durata non vengono mai restituiti al sistema.

Ad esempio, è possibile utilizzare la scadenza scorrevole per far scadere i dati della cache se non vengono utilizzati per un certo tempo, ad esempio per un'ora.

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

### Limitare o partizionare le dimensioni della cache persistente

La cache persistente memorizza i dati nel database PSU. I dati vengono serializzati con la [classe PSSerializer](https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.psserializer?view=powershellsdk-7.4.0) che fa parte dell'SDK di PowerShell. Questo formato di dati è lo stesso utilizzato da PowerShell Remoting. Trattandosi di un formato XML, questi dati saranno significativamente più grandi rispetto all'archiviazione binaria. La memorizzazione di molti oggetti in un singolo elemento memorizzato nella cache comporterà prestazioni di deserializzazione scadenti una volta che i dati raggiungono determinati limiti.

Ciò può influire negativamente sul server PowerShell Universal perché deve recuperare e quindi deserializzare una stringa di grandi dimensioni ogni volta che i dati vengono letti dalla cache.

Consideri di selezionare sottoinsiemi più piccoli degli oggetti che desidera memorizzare. Ad esempio, selezioni solo le proprietà necessarie anziché l'intero oggetto.

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

Consideri di memorizzare i dati in partizioni più piccole. Se possibile, suddivida i dati in blocchi più piccoli per evitare di recuperare un valore di grandi dimensioni dal database.

```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
```

### Ridurre il livello di log in produzione

I livelli di log predefiniti per PowerShell Universal sono di tipo verbose. Per migliorare le prestazioni del sistema, è possibile ridurre i livelli di log. Questo deve essere fatto in due punti.

Innanzitutto, è consigliabile ridurre l'impostazione System Log Level. La si trova in `appsettings.json`. Consideri di modificare il livello da Verbose a Warning o Error.

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

In secondo luogo, è possibile modificare le destinazioni di logging per regolare i livelli o ridurre le funzionalità che vengono registrate.

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

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

## API

### Evitare di restituire oggetti molto complessi

Per impostazione predefinita, gli endpoint API serializzeranno gli oggetti restituiti in JSON utilizzando `ConvertTo-Json`. Sebbene la piattaforma limiti la profondità del JSON, oggetti molto complessi possono far sì che il cmdlet vada fuori controllo e consumi elevate quantità di CPU. PowerShell Universal tenterà di annullare questa elaborazione se viene rilevata, ma causerà comunque problemi al suo ambiente API.

Si assicuri di comprendere la complessità degli oggetti che restituisce. Se gli oggetti sono troppo complessi, consideri di utilizzare `Select-Object` per selezionare un sottoinsieme dei dati restituiti. Può anche chiamare `ConvertTo-Json` autonomamente per controllare il parametro `-Depth`.

Un esempio di questo sarebbe restituire oggetti Process con `Get-Process` . A causa della complessità del tipo Process, ciò causa problemi durante la serializzazione. Selezioni invece solo un sottoinsieme delle proprietà necessarie.

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

### Evitare processi di lunga durata nelle API

Il pool di thread HTTP ha dimensioni limitate. Processi di lunga durata nelle API possono esaurire il pool, il che può causare problemi all'intero server PowerShell Universal. Se prevede di avere un'API che richiede più di qualche secondo, consideri di far avviare all'API un job. Può quindi creare una seconda API per verificare lo stato dei job restituiti dalla prima API. Ciò garantirà che l'operazione continui a essere elaborata mentre il pool di thread HTTP recupera la connessione disponibile.

Ad esempio, potrebbe avere le seguenti API. Il primo endpoint avvia un job e restituisce l'ID del job. Il secondo endpoint recupera l'output della pipeline per il job specificato.

```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
}
```

Per chiamare questi endpoint, potremmo procedere come segue 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
```

## Automazione

### Ridurre l'output non necessario dei job

Sebbene memorizzare l'output dei job sia utile per l'auditing, memorizzare tutto l'output dei job può far aumentare notevolmente le dimensioni dello storage, il che a sua volta rallenterà le prestazioni del sistema PowerShell Universal. Alcuni passaggi che può adottare per tenere sotto controllo l'output dei job sono i seguenti.

#### Scartare l'output della pipeline

Se non intende utilizzare l'output della pipeline, può indicare a PowerShell Universal di scartarlo. Ciò ridurrà la quantità di dati memorizzati e aumenterà le prestazioni dei suoi job, perché il sistema non deve serializzare tutto l'output per l'archiviazione. Vedrà comunque i flussi di output nel log del job.

#### Sfruttare i flussi

L'uso dei flussi Debug, Warning ed Error può aiutare a ridurre ciò che viene mostrato nel job per impostazione predefinita. Impostare la preferenza di azione per ciascun flusso può consentirle di disabilitare determinati flussi per le operazioni ordinarie ma di abilitarli quando il job presenta problemi.

Ad esempio, se utilizza `Write-Debug` in tutto il suo script, può disabilitarlo tramite la variabile `$DebugActionPreference` impostandola su `SilentlyContinue`. Se il job iniziasse a presentare problemi, potrebbe impostarla su `Continue` per visualizzare l'output nel log.

#### Utilizzare Out-Null

`Out-Null` può catturare quello che sarebbe l'output della pipeline e scartarlo. Se non desidera scartare tutto l'output della pipeline, può scartarne una parte utilizzando `Out-Null`. Questo migliorerà le prestazioni e ridurrà le dimensioni dei dati dei suoi job.

### Eseguire una pulizia aggressiva dei job non importanti

Alcuni job, come un trigger utilizzato per le notifiche, potrebbero non essere quasi mai esaminati. In questo caso, consideri di impostare la cronologia dei job su un valore molto basso.

## App

### Utilizzare funzioni nelle app

Quando si creano sezioni complesse di un'app, è consigliabile incapsularle in una funzione per organizzare e isolare meglio tale sezione complessa. Le dashboard molto annidate sono difficili da sottoporre a debug e rendono facile introdurre errori di sintassi che influiranno sull'intera app.

Consigliamo inoltre di utilizzare i moduli per memorizzare le sue funzioni, così da ridurre ulteriormente le dimensioni e la complessità dello script principale dell'app. Inoltre, i moduli possono poi essere condivisi tra le app.

Un esempio sarebbe incapsulare la logica di una tabella all'interno di una funzione e quindi utilizzare la funzione all'interno dell'app.

```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
}
```

### Valutare di sfruttare i job

I job sono utili perché avviano un processo esterno e possono essere utilizzati per l'auditing delle interazioni con l'app. Poiché le app sono di lunga durata, determinate operazioni e moduli possono iniziare a causare problemi di memoria o di altre risorse se utilizzati sotto carico. L'avvio di job garantisce che l'ambiente venga recuperato dopo ogni esecuzione.

I job hanno senso per operazioni che apportano modifiche (ad esempio la creazione di una VM o di un utente), ma le loro caratteristiche prestazionali non saranno adatte a ogni scenario.

Un esempio sarebbe chiamare un job da un form.

```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!"
}
```

### Pianificare i riavvii delle app

Se riscontra problemi con le risorse delle app, può riavviare le app utilizzando un job pianificato che viene eseguito al di fuori dell'orario lavorativo. Può utilizzare i cmdlet `Get-PSUApp`, `Stop-PSUApp` e `Start-PSUApp` per riavviare le singole app. Questa tecnica è valida solo quando le app sono in esecuzione in ambienti non integrati.

### Essere consapevoli delle prestazioni di -Render con New-UDTable

Una funzionalità di `New-UDTable` e `New-UDTableColumn` è la possibilità di renderizzare le colonne in modo che contengano qualsiasi componente si desideri visualizzare in base alla riga di dati che si fornisce alla tabella. Questo è di solito utile per personalizzare l'aspetto o per fornire azioni per i dati della riga. Il rendering può diventare un problema di prestazioni se utilizzato in modo errato. Renderizzare molte righe contemporaneamente o utilizzare lo ScriptBlock di render per eseguire processi di lunga durata causerà problemi.

#### Rendering di troppe righe

Se utilizza il parametro `-Data` di `New-UDTable`, lo ScriptBlock `-Render` verrà chiamato per ogni elemento passato al parametro data. Se ha centinaia o migliaia di elementi, questo farà aumentare i tempi di caricamento della pagina.

Consideri di utilizzare `-LoadData` per caricare e visualizzare solo una pagina di dati alla volta. Questo chiama `-Render` solo per gli elementi visualizzati e non per l'intero set di dati.

#### Render di lunga durata

A causa dei dettagli di implementazione di `-Render`, non è consigliabile utilizzare operazioni di rendering di lunga durata. Se prevede che il suo `-Render` richieda più di qualche millisecondo, consideri di utilizzare `New-UDDynamic` per spostare il rendering sul server e visualizzare uno scheletro di caricamento. In questo caso il server può pianificare in modo efficiente l'operazione di rendering utilizzando il pool di runspace.

Un esempio di questo è mostrato di seguito.

```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 
)
```

Il risultato è una tabella che si carica immediatamente ma che mostra scheletri di caricamento nelle colonne lente da renderizzare.

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

## Portale

### Evitare chiamate a cmdlet di terze parti nei widget

Quando possibile, eviti di chiamare cmdlet di terze parti direttamente nei widget. Ogni widget ha il proprio runspace, ma tutti i widget vengono eseguiti nel processo del server PowerShell Universal. La chiamata di cmdlet direttamente nei widget causa il caricamento di assembly .NET nel server. Ciò può causare conflitti con altri caricamenti di assembly. Inoltre, la memoria potrebbe non essere recuperata dai comandi chiamati nel runspace.

L'utilizzo di `Invoke-PSUScript` e l'esecuzione di uno script in un ambiente esterno garantiscono che tutti i caricamenti di assembly siano contenuti in un processo separato e che tutta la memoria venga recuperata dopo l'uscita del processo.

Per una buona esperienza utente, potrebbe non avere sempre senso avviare un job per ogni interazione su una pagina. Consideri di utilizzare i cmdlet PSUCache per impostare e recuperare i dati dalla cache al fine di migliorare i tempi di caricamento delle pagine.

Non tutti i moduli causeranno problemi, quindi questa tecnica potrebbe non essere sempre necessaria. È molto improbabile che i moduli script causino problemi con il caricamento degli assembly.

## Infrastruttura e hosting

### Server PowerShell Universal dedicato

Consideri un server PowerShell Universal dedicato per ospitare la piattaforma. Eviti l'installazione insieme ad altre piattaforme che potrebbero causare contesa di risorse con l'applicazione. PowerShell Universal utilizza funzionalità standard di PowerShell come moduli, criteri di esecuzione e configurazione del remoting, e altre piattaforme potrebbero configurare queste impostazioni in modi con cui PSU potrebbe non funzionare bene.

### Utilizzare MS SQL o PostgreSQL in produzione

Eviti di utilizzare SQLite per casi d'uso in produzione. Non offre la possibilità di scalare quando i carichi di lavoro aumentano. Impedisce a più server PSU di utilizzare lo stesso archivio dati. Non scala quando si raggiungono dimensioni superiori a 2 GB.

La migrazione da un database SQLite a un database SQL può essere soggetta a errori e richiedere molto tempo. Se sta valutando una distribuzione per uso in produzione, consigliamo di iniziare con un server SQL centralizzato.

### Eseguire backup pianificati del database

Consigliamo di eseguire il backup del database a intervalli regolari. Sebbene i dati di configurazione siano archiviati principalmente nel repository, il database contiene risorse come App Token, identità locali, cronologia dei job e altro ancora. I backup regolari aiutano inoltre a recuperare da aggiornamenti dello schema non riusciti e offrono la possibilità di eseguire il rollback a versioni precedenti dello schema senza dover effettuare un downgrade dello schema.

### Cluster di database per la ridondanza

Consigliamo di utilizzare un cluster di failover del database, se possibile. Ciò consente un ripristino rapido e limita i tempi di inattività del server PowerShell Universal nel caso in cui uno dei server SQL dovesse andare offline.

### Bilanciamento del carico

Valuti l'utilizzo di un bilanciatore di carico davanti alle sue istanze di PowerShell Universal. Gli utenti verranno indirizzati al server meno occupato o eseguiranno il failover dai server offline. Ciò offre inoltre un modo migliore per organizzare gli aggiornamenti dell'applicazione PowerShell Universal.

PowerShell Universal supporta bilanciatori di carico come F5.

### Utilizzare HTTPS

[HTTPS ](/powershell-universal/it/config/hosting.md#configuring-https)non solo offre protezione dagli attacchi di intercettazione sulla rete, ma garantisce anche prestazioni migliori quando si utilizzano i cmdlet di PowerShell Universal. Questi si basano su funzionalità di HTTPS durante la comunicazione con la piattaforma e ricorrono a tecnologie di comunicazione legacy se non sono disponibili.

### Log shipping

I file di log di sistema vengono scritti nella cartella `%ProgramData%\PowerShellUniversal\Logs` e verranno eliminati dopo 30 giorni. Per impostazione predefinita, ogni giorno viene scritto un nuovo file di log. Valuti la possibilità di trasferire i log da questa directory al suo SIEM se intende conservarli per più di 30 giorni.

È inoltre possibile configurare le [Destinazioni di logging](/powershell-universal/it/sviluppo/logging.md#targets) per inviare i messaggi di log direttamente 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/it/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.
