PSDesiredStateConfiguration icon indicating copy to clipboard operation
PSDesiredStateConfiguration copied to clipboard

Performance issues with Invoke-DscResource for multiple DSC resources

Open KristiyanGK opened this issue 5 years ago • 1 comments

When used to invoke multiple DSC Resource the Invoke-DscResource cmdlet works really slow. This is caused by a lot of repeating operations (especially with Test and Set methods).

The performance can potentially be increased with any of the following:

  • Having a $Method input that accepts a parameter for executing both Test and Set methods (similar to DSC standard). This way the internal Dsc object can be reused and does need to be found or created twice.
  • Creating the Dsc object not in a separate runspace but instead creating it in a a scriptblock and then dot sourcing it.
$block = @"
using module '$ModuleName'

[$($invokeSplatParams['Name'])]::new()
"@

$dscObj = . ([ScriptBlock]::create($block))

foreach ($key in $invokeSplatParams.Property.Keys) {
   $dscObj.$key = $invokeSplatParams.Property[$key]
}
  • Having an optional flag for skipping DSC Resource validation and letting the user validate prior to it.

Steps to reproduce

Invoke a big quantity of DSC Resources with Invoke-DscResource. The examples are made using DSC Resources from VMware.vSphereDSC.

Example used for measuring Invoke-DscResource performance

$dataCenterFolderInvokeSplat = @{
    Name = 'DatacenterFolder'
    ModuleName = 'VMware.vSphereDSC'
    Property = @{
        Server = '<Server here>'
        Credential = '<Credentials here>'
        Name = "MyDatacentersFolder"
        Location = ''
        Ensure = 'Present'
    }
}

Import-Module 'PSDesiredStateConfiguration'

Measure-Command {
    $state = Invoke-DscResource @dataCenterFolderInvokeSplat -Method Test

    if (-not $state.InDesiredState) {
        Invoke-DscResource @dataCenterFolderInvokeSplat -Method Set
    }
}

Example used for measuring LCM performance

Configuration Test {
    Import-DscResource -ModuleName VMware.vSphereDSC

    Node $AllNodes.NodeName {
        foreach ($vCenter in $AllNodes.VCenters) {
            $Server = $vCenter.Server
            $User = $vCenter.User
            $Password = $vCenter.Password | ConvertTo-SecureString -asPlainText -Force
            $Credential = New-Object System.Management.Automation.PSCredential($User, $Password)

            DatacenterFolder "MyDatacentersFolder_$($Server)" {
                Server = $Server
                Credential = $Credential
                Name = "MyDatacentersFolder"
                Location = ''
                Ensure = 'Present'
            }
        }
    }
}

$configurationData = @{
    AllNodes = @(
        @{
            NodeName = 'localhost'
            PSDscAllowPlainTextPassword = $true
            VCenters = @(
                @{
                    Server = '<Server here>'
                    User = '<User here>'
                    Password = '<Password here>'
                }
            )
        }
    )
}

Test -ConfigurationData $configurationData

Measure-Command {
    Start-Configuration -Path '.\Test\' -Wait -Force
}

Actual behavior

The following table shows measurements made by using 1 and 10 DSC Resources with Invoke-DscResource and Configurations with that many Resources for use with Start-DscConfiguration.

The measured time is the average result from 3 runs each and the measuring unit is in seconds. The measurement is calculated from the Measure-Command cmdlet result.

Resources Count/Execution type LCM Invoke-DscResource
1 4s 8s
10 32s 61s

Environment data

Name Value
PSVersion 7.0.3
PSEdition Core
GitCommitId 7.0.3
OS Microsoft Windows 10.0.18363
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0

KristiyanGK avatar Nov 06 '20 12:11 KristiyanGK

My guess is that most of this slowness comes from Get-DscResource that's called when running Invoke-DscResource, because it's ran every time, it lists all resources available in all $Env:PSmodulePath, and does it recursively through the paths... @KristiyanGK if you try to change your $Env:PSModulePath just before you run the Invoke-DscResource to only contain the folder that has your resource, it might get a little bit faster.

An potential way to optimise this could be to better support the pipeline in the Invoke-DscResource cmdlet, so that the listing of all available resources is done once and cached in the begin block, then the process block does the invocations. For that the Parameters Method and Property should implement ValueFromPipelineByPropertyName.

gaelcolas avatar May 21 '21 20:05 gaelcolas