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

# Bedste praksis

Følg bedste praksis i PowerShell Universal for miljøer, cachelagring, API-design, joboutput, appydeevne og pålidelighed ved hosting i produktion.

## Generelt

### Overvej uforanderlig konfiguration i produktion

PowerShell Universal er bygget på et scriptbaseret konfigurationssystem. Dette gør det muligt at konfigurere nye instanser af PowerShell Universal uden databasekald og direkte fra et lager af scripts. Det betyder, at det ved udrulning til produktion er en fordel at bruge en grundigt testet artefakt som grundlag for Deres PowerShell Universal-konfiguration.

De kan bruge værktøjer som Docker Compose til at lægge konfigurationsfiler til PowerShell Universal i lag i et objektbeholderbillede uden at skulle opdatere et produktionssystem manuelt i hånden. PowerShell Universal indeholder også en [Deployment ](/powershell-universal/da/config/deployments.md)funktion, der kan bruges til at publicere konfigurationer direkte til en instans. De kan derefter bruge et system til kontinuerlig udrulning til at opdatere Deres produktionsinstanser som led i et automatiseret arbejdsflow.

Afhængigt af Deres behov, typisk baseret på teamstørrelse og brug, er denne type konfiguration muligvis ikke nødvendig.

### Deaktiver Code First Editing, når det ikke er i brug

Funktionen Code First Editing registrerer ændringer i lagerkataloget ved hjælp af overvågning af filsystemet. Denne funktion er nyttig, når De udvikler en PowerShell Universal-konfiguration direkte fra filsystemet med værktøjer som Visual Studio Code. Når det er sagt, kan den have uventede bivirkninger, hvis De ikke bruger funktionen. Hvis De for eksempel skriver filer til lageret, f.eks. logfiler, kan det medføre, at konfigurationssystemet i PowerShell Universal genindlæser sig selv unødigt.

{% hint style="info" %}
Lagerkataloget er som standard `C:\ProgramData\UniversalAutomation\Repository`
{% endhint %}

Hvis De foretager ændringer via admin console, via git-synkronisering eller ved hjælp af udrulninger, bør De deaktivere automatisk genindlæsning. Dette øger både ydeevnen og reducerer også risikoen for uventede ændringer af Deres udrullede konfiguration.

### Foretræk ikke-integrerede miljøer

{% hint style="info" %}
Dette henviser ikke til switch-parameteren `-Integrated` for PSU-cmdletterne og gælder kun for miljøer.
{% endhint %}

Selv om det integrerede miljø er hurtigt og nemt at bruge, kører det alle Deres PowerShell-handlinger i PowerShell Universal-tjenesten. Problemer med et enkelt script eller slutpunkt kan påvirke systemets stabilitet.

Når der bruges miljøer, der ikke er Integrated, startes en ekstern PowerShell-proces. For API'er og dashboards kan den proces være langvarig, men den kan genstartes uden at påvirke resten af systemet. Ved jobs og terminaler startes en ny proces for hver instans af jobbet og terminalen. Når jobs og terminaler stoppes, afsluttes processen, og eventuelle ressourcer, som processen har brugt, genindvindes af systemet.

Derudover kan procesområdet, når moduler indlæses i det integrerede miljø, blive forurenet med forskellige versioner af fælles DLL'er, som PSU selv kan bruge. Dette kan forårsage problemer med assembly-binding, der kan få de importerede moduler til ikke at fungere som forventet.

Miljøerne PowerShell 7 og Windows PowerShell 5.1 har den største kompatibilitet med moduler, fordi de udnytter den eksekverbare fil `Universal.Agent`, som kan styre assemblyindlæsning; i modsætning til `pwsh.exe` eller `powershell.exe`.

### Isoler problematiske moduler

Komplekse PowerShell-moduler kan give problemer med PowerShell Universal. Visse moduler er ikke designet til at blive hostet i en længerevarende proces som PowerShell Universal. De bør bruge disse moduler i midlertidige handlinger som jobs.

dbatools kan f.eks. lække databaseforbindelser, når det bruges direkte i PowerShell Universals integrerede miljø. For at undgå dette kan De starte en ekstern proces ved at køre et PowerShell Universal-job i et ikke-integreret miljø. Scriptet kører, processen afsluttes, og databaseforbindelsen genindvindes automatisk.

Nedenfor er en liste over nogle moduler, som vi har oplevet problemer med.

* VMware PowerCLI - Forbindelsesadministration er afgrænset til processen
* Az - forbindelsesadministration er begrænset til processen

### Udnyt brugerdefinerede moduler

Opbygning af brugerdefinerede moduler sikrer, at De kan bruge den samme funktionalitet på hele PowerShell Universal-platformen uden at duplikere kode. De kan bruge de samme funktioner i API'er, scripts og dashboards uden at skulle duplikere logikken.

Hvis De reducerer mængden af script på et hvilket som helst af disse steder, kan det hjælpe Dem med bedre at teste og isolere problemer, der skyldes integration med platformen eller selve modulet.

Overvej også at oprette funktioner, der ombryder komplekse dashboardkomponenter. Dette reducerer den samlede kompleksitet af dashboard-scriptet og gør det nemt at fejlfinde og læse.

### Angiv en cachelevetid

Når De bruger `Set-PSUCache`, sørg for at angive en form for levetid for cachen. Dette er særligt vigtigt, hvis De har data, der ikke bruges hele tiden, og som er store. Data, der lægges i cachen uden en levetid, returneres aldrig til systemet.

De kan f.eks. bruge det glidende udløb til at lade cachedata udløbe, hvis det ikke bruges i et stykke tid, i en time.

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

### Grænse eller partitionsstørrelse for vedvarende cache

Den vedvarende cache gemmer data i PSU-databasen. Dataene serialiseres med [PSSerializer-klassen](https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.psserializer?view=powershellsdk-7.4.0), som er en del af PowerShell SDK. Dette dataformat er det samme, som bruges af PowerShell Remoting. Da det er et XML-format, bliver disse data væsentligt større end binær lagring. Hvis mange objekter gemmes i ét cachelagret element, giver det dårlig ydeevne ved deserialisering, når dataene når visse grænser.

Dette kan påvirke PowerShell Universal-serveren negativt, fordi den skal hente og derefter deserialisere en stor streng, hver gang dataene læses fra cachen.

Overvej at vælge mindre delmængder af de objekter, De ønsker at lagre. Vælg f.eks. kun de egenskaber, De har brug for, i stedet for hele objektet.

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

Overvej at lagre data i mindre partitioner. Segmentér om muligt dataene i mindre dele for at undgå at hente en stor værdi fra databasen.

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

### Reducer logniveau i produktion

Standardlogniveauerne for PowerShell Universal er verbose. For at forbedre systemets ydeevne kan De reducere logniveauerne. Dette skal gøres to steder.

Først bør De reducere indstillingen System Log Level. Den findes i `appsettings.json`. Overvej at ændre niveauet fra Verbose til Warning eller Error.

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

For det andet kan De justere logføringsmålene for at justere niveauer eller reducere de funktioner, der logges.

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

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

## API'er

### Undgå at returnere meget komplekse objekter

Som standard serialiserer API-slutpunkter returnerede objekter til JSON ved hjælp af `ConvertTo-Json`. Selvom platformen begrænser dybden af JSON, kan meget komplekse objekter få cmdletten til at løbe løbsk og forbruge store mængder CPU. PowerShell Universal vil forsøge at annullere denne behandling, hvis den registreres, men den vil stadig forårsage problemer i Deres API-miljø.

Sørg for, at De forstår kompleksiteten af de objekter, De returnerer. Hvis objekter er for komplekse, overvej at bruge `Select-Object` til at vælge en delmængde af de returnerede data. De kan også selv kalde `ConvertTo-Json` for at styre parameteren `-Depth`.

Et eksempel på dette ville være at returnere Process-objekter med `Get-Process` . På grund af kompleksiteten af typen Process giver det problemer under serialisering. Vælg i stedet kun en delmængde af de egenskaber, der er påkrævede.

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

### Undgå langvarige processer i API'er

HTTP-trådpuljen har begrænset størrelse. Længerevarende processer i API'er kan udtømme puljen, hvilket kan give problemer for hele PowerShell Universal-serveren. Hvis De planlægger en API, der tager mere end et par sekunder, bør De overveje at lade API'en starte et job. De kan derefter oprette en anden API til at kontrollere tilstanden for de jobs, som den første API returnerer. Det sikrer, at handlingen fortsætter med at blive behandlet, mens HTTP-trådpuljen tager den tilgængelige forbindelse tilbage.

De kunne for eksempel have følgende API'er. Det første slutpunkt starter et job og returnerer job-id'et. Det andet slutpunkt henter pipelineoutputtet for det angivne job.

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

For at kalde disse slutpunkter kunne vi gøre følgende med `Invoke-RestMethod`.

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

## Automatisering

### Reducer unødvendigt joboutput

Selvom lagring af joboutput er nyttig til revision, kan lagring af alt joboutput få Deres lagerplads til at vokse kraftigt i størrelse, hvilket igen sænker ydeevnen for Deres PowerShell Universal-system. Nogle trin, De kan tage for at holde joboutput i skak, er som følger.

#### Kassér pipelineoutput

Hvis De ikke vil bruge pipelineoutput, kan De instruere PowerShell Universal om at kassere det. Det reducerer mængden af lagrede data og øger også ydeevnen for Deres jobs, fordi systemet ikke behøver at serialisere alt output til lagring. De vil stadig se Deres outputstreams i jobloggen.

#### Udnyt strømme

Brug af streamene Debug, Warning og Error kan hjælpe med at reducere det, der vises i jobbet som standard. Hvis De angiver handlingspræferencen pr. stream, kan De deaktivere bestemte streams ved almindelige handlinger, men aktivere streams, når jobbet oplever problemer.

Hvis De for eksempel bruger `Write-Debug` i hele Deres script, kan De deaktivere det via variablen `$DebugActionPreference` ved at angive den til `SilentlyContinue`. Hvis jobbet skulle begynde at få problemer, kunne De angive den til `Continue` for at få vist outputtet i loggen.

#### Anvend Out-Null

`Out-Null` kan opfange potentielt pipelineoutput og kassere det. Hvis De ikke vil kassere alt pipelineoutput, kan De kassere noget af det ved hjælp af `Out-Null`. Det forbedrer ydeevnen og reducerer størrelsen af Deres jobdata.

### Ryd aggressivt op i uvigtige jobs

Nogle jobs, f.eks. en udløser, der bruges til meddelelser, bliver næsten aldrig gennemgået. Overvej at sætte jobhistorikken meget lav i dette tilfælde.

## Apps

### Brug funktioner i apps

Når De opretter komplekse sektioner af en app, anbefales det at indkapsle den i en funktion for bedre at organisere og isolere den komplekse sektion. Meget indlejrede dashboards er svære at fejlfinde og gør det let at introducere syntaksfejl, der påvirker hele appen.

Vi anbefaler også at bruge moduler til at lagre Deres funktioner for yderligere at reducere størrelsen og kompleksiteten af Deres centrale app-script. Derudover kan moduler derefter deles på tværs af apps.

Et eksempel kunne være at indkapsle logikken for en tabel i en funktion og derefter bruge funktionen i appen.

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

### Overvej at udnytte jobs

Jobs er nyttige, fordi de starter en ekstern proces og kan bruges til revision af interaktioner med appen. Da apps kører længe, kan visse handlinger og moduler begynde at forårsage problemer med hukommelse eller andre ressourcer, hvis de bruges under belastning. Start af jobs sikrer, at miljøet frigøres efter hver kørsel.

Jobs giver mening for handlinger, der foretager ændringer (f.eks. oprettelse af en VM eller en bruger), men deres ydeevneegenskaber vil ikke fungere i alle scenarier.

Et eksempel ville være at kalde et job fra en formular.

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

### Planlæg genstart af apps

Hvis De oplever problemer med app-ressourcer, kan De genstarte apps ved hjælp af et planlagt job, der kører uden for arbejdstiden. De kan bruge `Get-PSUApp`, `Stop-PSUApp`og `Start-PSUApp`cmdlets til at genstarte de enkelte apps. Denne teknik er kun gyldig, når apps kører i ikke-integrerede miljøer.

### Vær opmærksom på ydeevnen for -Render med New-UDTable

En funktion ved `New-UDTable` og`New-UDTableColumn` er muligheden for at gengive kolonner, så de indeholder en hvilken som helst komponent, De vil vise, ud fra den datarække, De leverer til tabellen. Det er normalt nyttigt til at tilpasse udseendet eller til at angive handlinger for rækkens data. Gengivelse kan blive et problem for ydeevnen, hvis det bruges forkert. Gengivelse af mange rækker på én gang eller brug af render-ScriptBlock til at køre længerevarende processer giver problemer.

#### Gengivelse af for mange rækker

Hvis De bruger parameteren `-Data` for `New-UDTable`, kaldes ScriptBlock'en `-Render` for hvert element, De sender til dataparameteren. Hvis De har hundredvis eller tusindvis af elementer, medfører det, at indlæsningstiderne for siden stiger.

Overvej at bruge `-LoadData` til kun at indlæse og vise én side med data ad gangen. Dette kalder kun `-Render` for de viste elementer og ikke hele datasættet.

#### Langvarige gengivelser

På grund af implementeringsdetaljerne for `-Render` anbefales det ikke at bruge langvarige gengivelseshandlinger. Hvis De forventer, at Deres `-Render` tager mere end et par millisekunder, bør De overveje at bruge `New-UDDynamic` til at flytte gengivelsen tilbage til serveren og vise et indlæsningsskelet. Serveren kan i dette tilfælde effektivt planlægge gengivelseshandlingen ved hjælp af runspace-puljen.

Et eksempel på dette vises nedenfor.

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

Resultatet er en tabel, der indlæses med det samme, men viser indlæsningsskeletter i de kolonner, der er langsomme at gengive.

## Portal

### Undgå tredjeparts-cmdlet-kald i Widgets

Undgå om muligt at kalde tredjeparts-cmdlets direkte i widgets. Hver widget har sit eget runspace, men alle widgets kører i PowerShell Universal-serverprocessen. Kald af cmdlets direkte i widgets medfører, at .NET-assemblies indlæses i serveren. Dette kan forårsage konflikter med andre assemblyindlæsninger. Derudover frigives hukommelse muligvis ikke fra kommandoer, der kaldes i runspacet.

Brug af `Invoke-PSUScript` og kørsel af et script i et eksternt miljø sikrer, at alle assemblyindlæsninger holdes inden for en separat proces, og at al hukommelse frigives, når processen afsluttes.

Af hensyn til en god brugeroplevelse giver det ikke altid mening at starte et job for hver interaktion på en side. Overvej at bruge PSUCache-cmdletterne til at gemme og hente data i cachen for at forbedre sidens indlæsningstider.

Ikke alle moduler forårsager problemer, så denne teknik er måske ikke altid nødvendig. Det er meget usandsynligt, at scriptmoduler forårsager nogen problemer med indlæsning af assemblies.

## Infrastruktur og hosting

### Dedikeret PowerShell Universal-server

Overvej en dedikeret PowerShell Universal-server som vært for platformen. Undgå at installere side om side med andre platforme, der kan skabe konflikt om ressourcer med programmet. PowerShell Universal bruger standardfunktioner i PowerShell som moduler, udførelsespolitikker og remoting-konfiguration, og andre platforme kan konfigurere disse indstillinger på måder, som PSU muligvis ikke fungerer godt sammen med.

### Anvend MS SQL eller PostgreSQL til produktion

Undgå at bruge SQLite til produktionsbrugsscenarier. Det giver ikke mulighed for at skalere, når arbejdsbelastningerne stiger. Det forhindrer flere PSU-servere i at bruge det samme datalager. Det skalerer ikke ved størrelser over 2 GB.

Migrering fra en SQLite-database til en SQL-database kan være fejlbehæftet og tidskrævende. Hvis De overvejer en udrulning til produktionsbrug, anbefaler vi at starte med en centraliseret SQL Server.

### Udfør planlagte databasesikkerhedskopier

Vi anbefaler at sikkerhedskopiere databasen med jævne mellemrum. Selvom konfigurationsdata primært gemmes i lageret, indeholder databasen ressourcer som App Tokens, lokale identiteter, jobhistorik og mere. Regelmæssige sikkerhedskopier hjælper også med at gendanne efter mislykkede skemaopgraderinger og giver mulighed for at rulle tilbage til tidligere skemaversioner uden at skulle udføre en skemanedgradering.

### Databaseklynge til redundans

Vi anbefaler at bruge en failover-klynge til databasen, hvis det er muligt. Det giver mulighed for hurtig genoprettelse og begrænser nedetiden for PowerShell Universal-serveren, hvis en af SQL-serverne skulle gå offline.

### Belastningsfordeling

Overvej at anvende en belastningsfordeler foran Deres PowerShell Universal-instanser. Brugere dirigeres til den mindst belastede server eller foretager failover fra servere, der er offline. Dette giver også en bedre måde at udrulle opgraderinger af PowerShell Universal-programmet trinvist på.

PowerShell Universal understøtter belastningsfordelere som F5.

### Brug HTTPS

[HTTPS ](/powershell-universal/da/config/hosting.md#configuring-https)giver ikke kun sikkerhed mod angribere, der lytter på netværket, men giver også bedre ydeevne ved brug af PowerShell Universal-cmdlets. Disse cmdlets bygger på funktioner i HTTPS under kommunikationen med platformen og falder tilbage til ældre kommunikationsteknologier, hvis funktionerne ikke er tilgængelige.

### Logafsendelse

Systemlogfiler skrives til mappen `%ProgramData%\PowerShellUniversal\Logs` og slettes efter 30 dage. Hver dag skrives en ny logfil, som standard. Overvej at sende logge fra dette katalog til Deres SIEM, hvis De vil beholde logge i mere end 30 dage.

De kan også konfigurere [logføringsmål](/powershell-universal/da/udvikling/logging.md#targets) til at sende logmeddelelser direkte til et 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
