Performance Environments

avatar
(anonymous user)
Product: PowerShell Universal
Version: 2026.1.5


Hi, I have a general question about your experiences with the environments. I would like to switch many/all scripts to PowerShell 7, but I have noticed that the scripts take significantly longer (15 seconds or more), whereas PowerShell 5.1 is more efficient. I noticed that when starting a script, nothing happens at first. Then I see that PSU provides the following information: “Loading Active Directory module for Windows PowerShell with default drive ‘AD:’”. With Integrated, I have the problem that I can’t use credentials, so that’s not an option. With PowerShell 5.1 under the current version 2026.1.5, I have the problem that it can no longer read certificates, whereas PowerShell 7 has no issues with this. Do you have any tips for me to increase efficiency or improve performance?

All Comments (4)

avatar

Hi Marco,

Thank you for the detailed explanation. My name is Ruben Tapia, and I am a support engineer with Ironman Software.

What you are seeing is consistent with one of the main limitations of using PowerShell 7 together with modules that still rely on Windows PowerShell compatibility. In PSU, when Windows PowerShell Compatibility is triggered, an additional Windows PowerShell process is started for the runspace, which can noticeably increase startup time.

In your case, the message about loading the Active Directory module suggests that AD compatibility may be part of the delay. If your server supports ActiveDirectory module 1.0.1.0, that would be worth checking first, since that version has native PowerShell 7 support. Otherwise, PSU’s guidance is usually to keep AD-dependent workloads in Windows PowerShell 5.1 where needed.

You are also correct about another limitation: the Integrated environment is faster, but it does not support alternate credentials. If you need Run As, PSU supports that through PSCredential secret variables in non-Integrated environments.

To narrow this down, could you please share:

  • Whether the delay happens for all PS7 scripts or only AD-related ones
  • Your environment definition
  • Whether this is Windows Server 2016 or 2019+
  • The exact certificate-reading error from 5.1
  • A small test script that reproduces the delay

Verified references:

https://docs.powershelluniversal.com/config/environments
https://docs.powershelluniversal.com/platform/modules
https://docs.powershelluniversal.com/automation/scripts
https://docs.powershelluniversal.com/config/running-as-a-service-account
https://forums.ironmansoftware.com/t/windows-powershell-compatiblity-is-enabled-we-recommend-disabling-this-feature-of-powershell-when-using-powershell-universal/8686


Best regards,

Ruben Tapia

avatar

Hi @AnonymousUser ,

some requested information:

  • The delay happens only when trying to start AD-jobs
  • PowerShell 7 Version PowerShell 7.5.4
  • Server 2019+
  • certificate-reading error: Cannot find drive. A drive with the name ‘Cert’ does not exist, Must specify ‘CertificateThumbprint or CertificateSubjectName or Certificate’.
  • example = get-aduser -identity $user
avatar

Hi @AnonymousUser . i ve made a change in the PS7 Environment Properties. I ve enabled the option Persistent Runspaces. Since then, after the first script startet, it takes 4-6 seconds. I think this is acceptable.

avatar

HI @AnonymousUser what could also be a “problem” is the background jobs powershell or .net is doing at startup. I have written a script some time ago to disable some of them in our environments (sometimes air gapped systems where only the powershell update check hangs for 5-10 seconds). Perhaps it is useful for you.

In general you only need the machine scope on servers, since we are also using this on our workstations, we are doing both. it fails with a warning if running as user (not administrator) and only applies user scope variables.
At the end, if you set $VerbosePreference = ‘Continue’before running the code, it will also output the found environment variables:

<#
    .SYNOPSIS
        Configure PowerShell and .NET environment variables for air-gapped environments.
    .DESCRIPTION
        This script configures environment variables to:
        - Disable PowerShell telemetry
        - Disable PowerShell update notifications
        - Disable PowerShell diagnostics telemetry
        - Disable .NET CLI telemetry
        - Disable .NET welcome banners
        - Reduce unnecessary module analysis cache cleanup activity

        The script first tries to configure Machine scope.
        If Machine scope fails, it falls back to User scope.

        Official documentation:
        PowerShell Environment Variables:
        https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_environment_variables

        PowerShell Telemetry:
        https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_telemetry

        PowerShell Update Notifications:
        https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_update_notifications

        PowerShell Startup Performance:
        https://learn.microsoft.com/powershell/scripting/dev-cross-plat/performance/startup-performance

        .NET CLI Telemetry:
        https://learn.microsoft.com/dotnet/core/tools/telemetry
    .NOTES
        Recommended for:
        - Air-gapped environments
        - Offline systems
        - Restricted enterprise environments
        - CI/CD runners
        - Non-interactive automation hosts

        A restart of PowerShell sessions is required after applying changes.
#>

# Configure environment variables
$EnvironmentVariables = @{
    POWERSHELL_TELEMETRY_OPTOUT         = '1'
    POWERSHELL_UPDATECHECK              = 'Off'
    POWERSHELL_DIAGNOSTICS_OPTOUT       = '1'
    DOTNET_CLI_TELEMETRY_OPTOUT         = '1'
    DOTNET_NOLOGO                       = 'true'
    PSDisableModuleAnalysisCacheCleanup = '1'
}

# Configure preferred scope order
$Scopes = @(
    'Machine'
    'User'
)

# Apply environment variables
$AppliedScope = $null

foreach ($Scope in $Scopes) {
    try {
        Write-Verbose -Message ("Trying environment scope: {0}" -f $Scope)

        foreach ($Variable in $EnvironmentVariables.GetEnumerator()) {
            [System.Environment]::SetEnvironmentVariable(
                $Variable.Key,
                $Variable.Value,
                $Scope
            )
        }

        $AppliedScope = $Scope

        Write-Verbose -Message (
            "Successfully configured environment variables in scope: {0}" -f
            $Scope
        )

        break
    }
    catch {
        Write-Warning -Message (
            "Failed to configure environment variables in scope '{0}'. {1}" -f
            $Scope,
            $_.Exception.Message
        )
    }
}

# Display configured variables by scope
foreach ($Variable in $EnvironmentVariables.Keys | Sort-Object) {
    foreach ($Scope in $Scopes) {
        $Value = [System.Environment]::GetEnvironmentVariable(
            $Variable,
            $Scope
        )

        Write-Verbose -Message (
            "{0} = {1} [{2}]" -f
            $Variable,
            $Value,
            $Scope
        )
    }
}