[IntuneSettingCatalogCustomPolicyWindows10] Get-DscConfiguration fails
Description of the issue
When executing Get-DscResource after deploying a resource, I receive the following error:
I'm using Get-DSCResource to capture the resource ID's for newly created resources so that I can then feed them back into other resources such as dynamic groups or assignment filters etc.
I've done some debugging to try and locate the issue, and identified that the issue seems to be with the results variable returned to the DSC process (return [System.Collections.Hashtable] $results). By hacking up the "IntuneSettingCatalogCustomPolicyWindows10" module on a test machine and experimenting with the results hashtable, I've managed to isolate the issue to the "MSFT_MicrosoftGraphDeviceManagementConfigurationSettingInstance" class. Setting the "SettingInstance" property to $null works, but trying to set it to anything else fails. I've started manually creating the "Settings" parameter within the module to try and work out what the structure of the results variable needs to be, but again, as soon as I set a value for the "Setting Instance" property it fails.
I've tried lots of different things, but here is an example: This breaks $TestSettings = @() $SettingInstance = @{ 'SettingDefinitionId' = 'device_vendor_msft_policy_config_deliveryoptimization_doallowvpnpeercaching' 'choiceSettingValue' = $null 'odataType' = '#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance' } $ConfigSetting = @{ 'SettingInstance' = $SettingInstance 'Id' = '0' } $TestSettings += $ConfigSetting $results = @{ #region resource generator code Description = $getValue.Description Name = $getValue.Name Platforms = $enumPlatforms Technologies = $enumTechnologies Settings = $TestSettings Id = $getValue.Id Ensure = 'Present' Credential = $Credential ApplicationId = $ApplicationId TenantId = $TenantId ApplicationSecret = $ApplicationSecret CertificateThumbprint = $CertificateThumbprint Managedidentity = $ManagedIdentity.IsPresent AccessTokens = $AccessTokens #endregion }
This works $TestSettings = @()
$ConfigSetting = @{
'SettingInstance' = $null
'Id' = '0'
}
$TestSettings += $ConfigSetting
$results = @{
#region resource generator code
Description = $getValue.Description
Name = $getValue.Name
Platforms = $enumPlatforms
Technologies = $enumTechnologies
Settings = $TestSettings
Id = $getValue.Id
Ensure = 'Present'
Credential = $Credential
ApplicationId = $ApplicationId
TenantId = $TenantId
ApplicationSecret = $ApplicationSecret
CertificateThumbprint = $CertificateThumbprint
Managedidentity = $ManagedIdentity.IsPresent
AccessTokens = $AccessTokens
#endregion
}
I've spent soo much time trying different things unsuccessfully to figure what the issue is with the object structure, I'm thinking it might actually be something to do with the schema class? But I'm well out of my depth now :(
Microsoft 365 DSC Version
1.25.416.1
Which workloads are affected
Intune
The DSC configuration
param (
[parameter()]
[System.Management.Automation.PSCredential]
$Credential
)
Configuration Test_Configuration
{
param (
[parameter()]
[System.Management.Automation.PSCredential]
$Credential
)
if ($null -eq $Credential) {
<# Credentials #>
$Credscredential = Get-Credential -Message "Credentials"
}
else {
$CredsCredential = $Credential
}
$OrganizationName = $CredsCredential.UserName.Split('@')[1]
Import-DscResource -ModuleName 'Microsoft365DSC'
Node localhost
{
IntuneSettingCatalogCustomPolicyWindows10 "DSC Test Policy"
{
Credential = $Credscredential;
Description = "DSC testing policy";
Ensure = "Present";
Name = "DSC Test Policy";
Platforms = "windows10";
Settings = @(
MSFT_MicrosoftGraphdeviceManagementConfigurationSetting {
SettingInstance = MSFT_MicrosoftGraphDeviceManagementConfigurationSettingInstance {
choiceSettingValue = MSFT_MicrosoftGraphDeviceManagementConfigurationChoiceSettingValue {
Value = "device_vendor_msft_policy_config_deliveryoptimization_doallowvpnpeercaching_0"
}
SettingDefinitionId = "device_vendor_msft_policy_config_deliveryoptimization_doallowvpnpeercaching"
odataType = "#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance"
}
}
);
Technologies = "mdm";
}
}
}
Test_Configuration -ConfigurationData .\ConfigurationData.psd1 -Credential $Credential
Verbose logs showing the problem
Environment Information + PowerShell Version
OsName : Microsoft Windows Server 2019 Datacenter
OsOperatingSystemSKU : DatacenterServerEdition
OsArchitecture : 64-bit
WindowsVersion : 1809
WindowsBuildLabEx : 17763.1.amd64fre.rs5_release.180914-1434
OsLanguage : en-US
OsMuiLanguages : {en-US}
Key : PSVersion
Value : 5.1.17763.7131
Name : PSVersion
Key : PSEdition
Value : Desktop
Name : PSEdition
Key : PSCompatibleVersions
Value : {1.0, 2.0, 3.0, 4.0...}
Name : PSCompatibleVersions
Key : BuildVersion
Value : 10.0.17763.7131
Name : BuildVersion
Key : CLRVersion
Value : 4.0.30319.42000
Name : CLRVersion
Key : WSManStackVersion
Value : 3.0
Name : WSManStackVersion
Key : PSRemotingProtocolVersion
Value : 2.3
Name : PSRemotingProtocolVersion
Key : SerializationVersion
Value : 1.1.0.1
Name : SerializationVersion
There should be some information in the Event Viewer --> Application and Services Log --> M365DSC. If you do a simple export of the configuration, compile and run it using Start-DscConfiguration, does it not work as well or does it work?
Hi Fabien. If I run "Export-M365DSCConfiguration -Components 'IntuneSettingCatalogCustomPolicyWindows10' -Credential $credential" and then compile the output of that into a Mof, Running Start-DscConfiguration on the Mof runs successfully. But when I run the Get-DscConfiguration, it fails. There are no logs in the M365DSC event log because the failure isn't in M365DSC. DSC itself is throwing the error. Based on the error, and my testing, it's seems to be something to do with the Hashtable that's being returned to MSFT_DSCLocalConfigurationManager.
Here's my LCM configuration as well.
ActionAfterReboot : ContinueConfiguration AgentId : 7B4E3C03-CCE9-11EF-95FE-00155D497575 AllowModuleOverWrite : False CertificateID : ConfigurationDownloadManagers : {} ConfigurationID : ConfigurationMode : ApplyAndMonitor ConfigurationModeFrequencyMins : 1440 Credential : DebugMode : {NONE} DownloadManagerCustomData : DownloadManagerName : LCMCompatibleVersions : {1.0, 2.0} LCMState : Idle LCMStateDetail : LCMVersion : 2.0 StatusRetentionTimeInDays : 10 SignatureValidationPolicy : NONE SignatureValidations : {} MaximumDownloadSizeMB : 500 PartialConfigurations : RebootNodeIfNeeded : True RefreshFrequencyMins : 30 RefreshMode : PUSH ReportManagers : {} ResourceModuleManagers : {}
@ajkenah I think this is a "not going to be fixed" issue. The resource contains nested configurations (inside of the Settings property) and the conversion into an object from the LCM fails because of those nested configurations. I did some checks and if I remove from the second nesting level below, it will work but obviously without the properties we would like to have.
So to recap: The issue is neither in your configuration nor with the module, it is with the LCM and its conversion capability from a hashtable to its corresponding CIM object. Since the LCM isn't actively developed anymore (the repository with the code is archived on GitHub), I wouldn't expect any fix. We can't fix it either unfortunately. You probably don't have another choice but to import the DSC resource module yourself and run Get-TargetResource "manually" to fetch the configuration. It's simply out of our hands 😢
Is there any medium/longterm plan to migrate to dsc3? This module is awesome, but I can't continue developing production solutions on a deprecated platform with known bugs in the core functionality.
TBH, I don't know what I'd replace it with if not. sigh
@ajkenah Just tested with DSCv3 as the platform instead of the LCM and the same issue there - I might have an idea on how to fix this. Give me a bit of time and I'll get back to you.
Edit: I was able to export nested instances, we have to construct those instances ourselves, requiring a couple of code changes. I'll see what I can do.
Thanks @FabienTschanz . I appreciate the work. It's also good to know that I can use Microsoft365DSC with DSCv3 as the engine instead of LCM.
Hi @FabienTschanz has there been any update on this? I'm back into this project (M365 configuration and management as code) after a bit of a hiatus. This is a pretty major blocker for this project as I use the Get-DSCConfiguration command to get dependant values such as Autopilot GUID's to then dynamically build device filters and Group membership rules etc. Essentially my entire prerequisite resource validation/deployment workflow depends on it.
I could use non-DSC commands, but then I'm adding additional dependencies and complexities to the solution to accommodate something that I should just be able to get natively after deploying the Mof file.
I'm happy to assist if it's not a priority. I'm quite proficient with PowerShell.
Just found, https://github.com/PowerShell/DSC/issues/1001 & https://github.com/microsoft/Microsoft365DSC/issues/6120. reading them now..
@ajkenah The issue is that the module would require a complete rewrite if we were to start using classes (which I personally think would be the better choice here). It would also require a massive amount of testing just to make sure everything works. The other way modifying the objects to be of CIM instances is just as ugly (at least I think so), and would probably also have some performance impact. Hence I'm still not sure what to do here.
No worries @FabienTschanz . I had a good read of the linked tickets last night, and I now have a good idea of what is happening. I think I should be able to help out with this if time and effort are an issue.
So, from my understanding, a quick fix would involve structuring the objects within the settings property to be explicitly defined as the appropriate CIM class (example defined in https://github.com/microsoft/Microsoft365DSC/issues/6120).
Your other mentioned option, of rewriting the module to start using classes, do you have an example of what you're thinking?
Are you thinking of defining a resource specific class and sub classes (for settings etc) that are defined in MSFT_IntuneSettingCatalogCustomPolicyWindows10 which are then called to structure the main objects in all the functions? (Get, Set & Test)?
If so, then yeah, that is a lot of work. As an IT Architect, I like the concept of having a structured object (especially if it unifies the output of Get-TargetResource and the Export-TargetResource functions somewhat), but I guess the questions I would ask are:
- Is rewriting the entire module to use classes in alignment with the general direction of the Microsoft365DSC project?
- Does rewriting the entire module to use classes resolve or enable the resolution of any other underlying issues with the Microsoft365DSC resource?
As mentioned above, I'm happy to put in the time and do some community "give-back", but I wouldn't want to spend such a significant amount of time on making changes that don't align with the overall direction of the project.
@ajkenah Thank you for the commitment, we always appreciate any help we can get. I'm one of the volunteers here as well and very committed to make Microsoft365DSC a great product for everyone involved (make it even better).
Yes, I'm thinking of rewriting the DSC resources from script-based to class-based. See #6202 for a working proposal and prototype of how such a resource can look like. There are tools that can help with transforming from scripts to classes (e.g. https://github.com/jdhitsolutions/DSCResourceMigration), but I didn't take the time to dig too deep into them. There are still a couple of things I'd like to do first before migrating to classes, especially about cleaning up the resources. There is currently much clutter in the resources which doesn't need to be there. After that is done (and the breaking change in October is done), we should be able to take a closer look at a realistic implementation of class-based resources.
To answer your questions directly:
Is rewriting the entire module to use classes in alignment with the general direction of the Microsoft365DSC project?
That's difficult to say, but from what I can see the community wants to adopt DSCv2 and DSCv3 in conjunction with PowerShell 7, which is currently not supported by Microsoft365DSC except for the export. Any other state machine management using the Local Configuration Manager (LCM) depends on Windows PowerShell 5.1. There are ways to work around it, but not yet fully implemented overall (which could be done when we rewrite the module anyway). So as an answer to that, I'd carefully say "Yes".
Does rewriting the entire module to use classes resolve or enable the resolution of any other underlying issues with the Microsoft365DSC resource?
Yes, it solves the issue with CIM de-/serialization and usage of DSCv2 and DSCv3 with complex types. Both work fine if the types are not complex, meaning containing nested CIM instances. That's the advantage of classes - serialization is not dependent on OS information, but rather information that's baked into the product. Another issue it solves is the reliance on WMI/CIM information, requiring administrative privileges to create a report if not all type information is already available on the machine. With classes, administrative privileges are not necessary anymore and the entire type resolution with underlying Windows information can be skipped, leading to a better performance (hopefully).
Classes are a construct that were created primarily for DSC resources. While they do not offer the same capabilities as C# classes, they still have a great impact and even Microsoft uses class based resources for their DSC stuff. We should adopt that and not stick to something that might only have drawbacks on newer platforms. The LCM also is not "developed" anymore, so at any moment, it could potentially break if Microsoft decides to change something in the underlying OS (although that's unlikely).
Edit: If you want to discuss some things privately, hit me up on LinkedIn. There's a link in my GitHub profile.
Thanks @FabienTschanz . You make some very valid points over on https://github.com/microsoft/Microsoft365DSC/issues/6202. Using classes to assist with the transition to DSCv3 & PS7 are very compelling reasons. I've done a little bit of work with classes in PowerShell, but nothing to this extent. I'll try and use your examples to bring myself quickly up to speed. Regardless of whether you need my help or not, I think it will be a good opportunity to get my head around DSCv3 and how to structure resources going forward.
@ajkenah Appreciate the enthusiasm, let's see if it dies down quickly once you see how complicated those things can get 😆 Just joking, feel free to ask if there are any questions. Maybe not in this issue, but I'm always happy to help.