Performance issues with Invoke-DscResource for multiple DSC resources
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 |
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.