Modules/M365DSCDRGUtil.psm1
function Get-StringFirstCharacterToUpper { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [System.String] $Value ) return $Value.Substring(0,1).ToUpper() + $Value.Substring(1,$Value.length-1) } function Get-StringFirstCharacterToLower { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [System.String] $Value ) return $Value.Substring(0,1).ToLower() + $Value.Substring(1,$Value.length-1) } function Remove-M365DSCCimInstanceTrailingCharacterFromExport { [CmdletBinding()] [OutputType([System.String])] param( [Parameter(Mandatory = $true)] [System.String] $DSCBlock ) $DSCBlock = $DSCBlock.replace(" ,`r`n" , " `r`n" ) $DSCBlock = $DSCBlock.replace("`r`n;`r`n" , "`r`n" ) $DSCBlock = $DSCBlock.replace("`r`n,`r`n" , "`r`n" ) return $DSCBlock } function Rename-M365DSCCimInstanceParameter { [CmdletBinding()] [OutputType([System.Collections.Hashtable], [System.Collections.Hashtable[]])] param( [Parameter(Mandatory = $true)] $Properties, [Parameter(Mandatory = $false)] [System.Collections.Hashtable] $KeyMapping = @{'odataType' = '@odata.type'} ) $result = $Properties $type = $Properties.getType().FullName #region Array if ($type -like '*[[\]]') { $values = @() foreach ($item in $Properties) { try { $values += Rename-M365DSCCimInstanceParameter $item -KeyMapping $KeyMapping } catch { Write-Verbose -Message "Error getting values for item {$item}" } } $result = $values return , $result } #endregion #region Single if ($type -like '*Hashtable') { $result = ([Hashtable]$Properties).clone() } if ($type -like '*CimInstance*' -or $type -like '*Hashtable*' -or $type -like '*Object*') { $hashProperties = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $result $keys = ($hashProperties.clone()).keys foreach ($key in $keys) { $keyName = $key.substring(0, 1).tolower() + $key.substring(1, $key.length - 1) if ($key -in $KeyMapping.Keys) { $keyName = $KeyMapping.$key } $property = $hashProperties.$key if ($null -ne $property) { $hashProperties.Remove($key) try { $subValue = Rename-M365DSCCimInstanceParameter $property -KeyMapping $KeyMapping if ($null -ne $subValue) { $hashProperties.add($keyName, $subValue) } } catch { Write-Verbose -Message "Error adding $property" } } } $result = $hashProperties } return $result #endregion } function Get-M365DSCDRGComplexTypeToHashtable { [CmdletBinding()] [OutputType([hashtable], [hashtable[]])] param( [Parameter()] $ComplexObject ) if ($null -eq $ComplexObject) { return $null } if ($ComplexObject.gettype().fullname -like '*[[\]]') { $results = @() foreach ($item in $ComplexObject) { if ($item) { $hash = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $item $results += $hash } } # PowerShell returns all non-captured stream output, not just the argument of the return statement. #An empty array is mangled into $null in the process. #However, an array can be preserved on return by prepending it with the array construction operator (,) return , [hashtable[]]$results } if ($ComplexObject.getType().fullname -like '*Dictionary*') { $results = @{} $ComplexObject = [hashtable]::new($ComplexObject) $keys = $ComplexObject.Keys foreach ($key in $keys) { if ($null -ne $ComplexObject.$key) { $keyName = $key $keyType = $ComplexObject.$key.gettype().fullname if ($keyType -like '*CimInstance*' -or $keyType -like '*Dictionary*' -or $keyType -like 'Microsoft.Graph.PowerShell.Models.*' -or $keyType -like 'Microsoft.Graph.Beta.PowerShell.Models.*' -or $keyType -like '*[[\]]') { $hash = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $ComplexObject.$key $results.Add($keyName, $hash) } else { $results.Add($keyName, $ComplexObject.$key) } } } return [hashtable]$results } $results = @{} if ($ComplexObject.getType().Fullname -like '*hashtable') { $keys = $ComplexObject.keys } else { $keys = $ComplexObject | Get-Member | Where-Object -FilterScript { $_.MemberType -eq 'Property' } } foreach ($key in $keys) { $keyName = $key if ($ComplexObject.getType().Fullname -notlike '*hashtable') { $keyName = $key.Name } if ($null -ne $ComplexObject.$keyName) { $keyType = $ComplexObject.$keyName.gettype().fullname if ($keyType -like '*CimInstance*' -or $keyType -like '*Dictionary*' -or $keyType -like 'Microsoft.Graph.*PowerShell.Models.*') { $hash = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $ComplexObject.$keyName if ($null -ne $hash -and $hash.Keys.Count -gt 0) { $results.Add($keyName, $hash) } } else { $results.Add($keyName, $ComplexObject.$keyName) } } } return [hashtable]$results } <# Use ComplexTypeMapping to overwrite the type of nested CIM Example $complexMapping=@( @{ Name="ApprovalStages" CimInstanceName="MSFT_MicrosoftGraphapprovalstage1" IsRequired=$false } @{ Name="PrimaryApprovers" CimInstanceName="MicrosoftGraphuserset" IsRequired=$false } @{ Name="EscalationApprovers" CimInstanceName="MicrosoftGraphuserset" IsRequired=$false } ) With Name: the name of the parameter to be overwritten CimInstanceName: The type of the CIM instance (can include or not the prefix MSFT_) IsRequired: If isRequired equals true, an empty hashtable or array will be returned. Some of the Graph parameters are required even though they are empty #> function Get-M365DSCDRGComplexTypeToString { [CmdletBinding()] param( [Parameter()] $ComplexObject, [Parameter(Mandatory = $true)] [System.String] $CIMInstanceName, [Parameter()] [Array] $ComplexTypeMapping, [Parameter()] [System.String] $Whitespace = '', [Parameter()] [System.uint32] $IndentLevel = 3, [Parameter()] [switch] $isArray = $false ) if ($null -eq $ComplexObject) { return $null } $indent = '' for ($i = 0; $i -lt $IndentLevel ; $i++) { $indent += ' ' } #If ComplexObject is an Array if ($ComplexObject.GetType().FullName -like '*[[\]]') { $currentProperty = @() $IndentLevel++ foreach ($item in $ComplexObject) { $splat = @{ 'ComplexObject' = $item 'CIMInstanceName' = $CIMInstanceName 'IndentLevel' = $IndentLevel } if ($ComplexTypeMapping) { $splat.add('ComplexTypeMapping', $ComplexTypeMapping) } $currentProperty += Get-M365DSCDRGComplexTypeToString -isArray:$true @splat } # PowerShell returns all non-captured stream output, not just the argument of the return statement. #An empty array is mangled into $null in the process. #However, an array can be preserved on return by prepending it with the array construction operator (,) return , $currentProperty } $currentProperty = '' if ($isArray) { $currentProperty += "`r`n" $currentProperty += $indent } $CIMInstanceName = $CIMInstanceName.replace('MSFT_', '') $currentProperty += "MSFT_$CIMInstanceName{`r`n" $IndentLevel++ $indent = ' ' * $IndentLevel $keyNotNull = 0 if ($ComplexObject.Keys.count -eq 0) { return $null } foreach ($key in $ComplexObject.Keys) { if ($null -ne $ComplexObject.$key) { $keyNotNull++ if ($ComplexObject.$key.GetType().FullName -like 'Microsoft.Graph.PowerShell.Models.*' -or $key -in $ComplexTypeMapping.Name) { $hashPropertyType = $ComplexObject[$key].GetType().Name.tolower() $isArray = $false if ($ComplexObject[$key].GetType().FullName -like '*[[\]]') { $isArray = $true } #overwrite type if object defined in mapping complextypemapping if ($key -in $ComplexTypeMapping.Name) { $hashPropertyType = ([Array]($ComplexTypeMapping | Where-Object -FilterScript { $_.Name -eq $key }).CimInstanceName)[0] $hashProperty = $ComplexObject[$key] $currentProperty += "`r`n" } else { $hashProperty = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $ComplexObject[$key] } if (-not $isArray) { $currentProperty += $indent + $key + ' = ' } if ($isArray -and $key -in $ComplexTypeMapping.Name) { if ($ComplexObject.$key.count -gt 0) { $currentProperty += $indent + $key + ' = ' $currentProperty += '@(' } } if ($isArray) { $IndentLevel++ foreach ($item in $ComplexObject[$key]) { if ($ComplexObject.$key.GetType().FullName -like 'Microsoft.Graph.PowerShell.Models.*') { $item = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $item } $nestedPropertyString = Get-M365DSCDRGComplexTypeToString ` -ComplexObject $item ` -CIMInstanceName $hashPropertyType ` -IndentLevel $IndentLevel ` -ComplexTypeMapping $ComplexTypeMapping ` -IsArray:$true if ([string]::IsNullOrWhiteSpace($nestedPropertyString)) { $nestedPropertyString = "@()`r`n" } $currentProperty += $nestedPropertyString } $IndentLevel-- } else { $nestedPropertyString = Get-M365DSCDRGComplexTypeToString ` -ComplexObject $hashProperty ` -CIMInstanceName $hashPropertyType ` -IndentLevel $IndentLevel ` -ComplexTypeMapping $ComplexTypeMapping if ([string]::IsNullOrWhiteSpace($nestedPropertyString)) { $nestedPropertyString = "`$null`r`n" } $currentProperty += $nestedPropertyString } if ($isArray) { if ($ComplexObject.$key.count -gt 0) { $currentProperty += $indent $currentProperty += ')' $currentProperty += "`r`n" } } $isArray = $PSBoundParameters.IsArray } else { $currentValue = $ComplexObject[$key] if ($currentValue.GetType().Name -eq 'String') { $currentValue = $ComplexObject[$key].Replace("'", "''").Replace("�", "''") } $currentProperty += Get-M365DSCDRGSimpleObjectTypeToString -Key $key -Value $currentValue -Space ($indent) } } else { $mappedKey = $ComplexTypeMapping | Where-Object -FilterScript { $_.name -eq $key } if ($mappedKey -and $mappedKey.isRequired) { if ($mappedKey.isArray) { $currentProperty += "$indent$key = @()`r`n" } else { $currentProperty += "$indent$key = `$null`r`n" } } } } $indent = '' $indent = ' ' * ($IndentLevel -1) if ($key -in $ComplexTypeMapping.Name) { $currentProperty += "`r`n" } $currentProperty += "$indent}" if ($isArray -or $IndentLevel -gt 4) { $currentProperty += "`r`n" } #Indenting last parenthesis when the cim instance is an array if ($IndentLevel -eq 5) { $indent = ' ' * ($IndentLevel -2) $currentProperty += $indent } $emptyCIM = $currentProperty.replace(' ', '').replace("`r`n", '') if ($emptyCIM -eq "MSFT_$CIMInstanceName{}") { $currentProperty = $null } if ($null -ne $currentProperty) { $fancySingleQuotes = "[\u2019\u2018]" $fancyDoubleQuotes = "[\u201C\u201D]" $currentProperty = [regex]::Replace($currentProperty, $fancySingleQuotes, "''") $currentProperty = [regex]::Replace($currentProperty, $fancyDoubleQuotes, '"') } return $currentProperty } function Get-M365DSCDRGSimpleObjectTypeToString { [CmdletBinding()] [OutputType([System.String])] param( [Parameter(Mandatory = $true)] [System.String] $Key, [Parameter(Mandatory = $true)] $Value, [Parameter()] [System.String] $Space = ' ' ) $returnValue = '' switch -Wildcard ($Value.GetType().Fullname ) { '*.Boolean' { $returnValue = $Space + $Key + " = `$" + $Value.ToString() + "`r`n" } '*.String' { if ($key -eq '@odata.type') { $key = 'odataType' } $returnValue = $Space + $Key + " = '" + $Value + "'`r`n" } '*.DateTime' { $returnValue = $Space + $Key + " = '" + $Value + "'`r`n" } '*[[\]]' { $returnValue = $Space + $key + ' = @(' $whitespace = '' $newline = '' if ($Value.count -gt 1) { $returnValue += "`r`n" $whitespace = $Space + ' ' $newline = "`r`n" } foreach ($item in ($Value | Where-Object -FilterScript { $null -ne $_ })) { switch -Wildcard ($item.GetType().Fullname) { '*.String' { $returnValue += "$whitespace'$item'$newline" } '*.DateTime' { $returnValue += "$whitespace'$item'$newline" } Default { $returnValue += "$whitespace$item$newline" } } } if ($Value.count -gt 1) { $returnValue += "$Space)`r`n" } else { $returnValue += ")`r`n" } } Default { $returnValue = $Space + $Key + ' = ' + $Value + "`r`n" } } return $returnValue } function Compare-M365DSCComplexObject { [CmdletBinding()] [OutputType([System.Boolean])] param( [Parameter()] $Source, [Parameter()] $Target ) #Comparing full objects if ($null -eq $Source -and $null -eq $Target) { return $true } $sourceValue = '' $targetValue = '' if (($null -eq $Source) -xor ($null -eq $Target)) { if ($null -eq $Source) { $sourceValue = 'Source is null' } if ($null -eq $Target) { $targetValue = 'Target is null' } Write-Verbose -Message "Configuration drift - Complex object: {$sourceValue$targetValue}" return $false } if ($Source.getType().FullName -like '*CimInstance[[\]]' -or $Source.getType().FullName -like '*Hashtable[[\]]') { if ($source.count -ne $target.count) { Write-Verbose -Message "Configuration drift - The complex array have different number of items: Source {$($source.count)} Target {$($target.count)}" return $false } if ($source.count -eq 0) { return $true } foreach ($item in $Source) { $hashSource = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $item foreach ($targetItem in $Target) { $compareResult = Compare-M365DSCComplexObject ` -Source $hashSource ` -Target $targetItem if ($compareResult) { break } } if (-not $compareResult) { Write-Verbose -Message 'Configuration drift - The complex array items are not identical' return $false } } return $true } $keys = $Source.Keys | Where-Object -FilterScript { $_ -ne 'PSComputerName' } foreach ($key in $keys) { #Matching possible key names between Source and Target $skey = $key $tkey = $key $sourceValue = $Source.$key $targetValue = $Target.$tkey #One of the item is null and not the other if (($null -eq $Source.$key) -xor ($null -eq $Target.$tkey)) { if ($null -eq $Source.$key) { $sourceValue = 'null' } if ($null -eq $Target.$tkey) { $targetValue = 'null' } #Write-Verbose -Message "Configuration drift - key: $key Source {$sourceValue} Target {$targetValue}" return $false } #Both keys aren't null or empty if (($null -ne $Source.$key) -and ($null -ne $Target.$tkey)) { if ($Source.$key.getType().FullName -like '*CimInstance*' -or $Source.$key.getType().FullName -like '*hashtable*') { #Recursive call for complex object $compareResult = Compare-M365DSCComplexObject ` -Source (Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $Source.$key) ` -Target $Target.$tkey if (-not $compareResult) { #Write-Verbose -Message "Configuration drift - complex object key: $key Source {$sourceValue} Target {$targetValue}" return $false } } else { #Simple object comparison $referenceObject = $Target.$tkey $differenceObject = $Source.$key #Identifying date from the current values $targetType = ($Target.$tkey.getType()).Name if ($targetType -like '*Date*') { $compareResult = $true $sourceDate = [DateTime]$Source.$key if ($sourceDate -ne $targetType) { $compareResult = $null } } else { $compareResult = Compare-Object ` -ReferenceObject ($referenceObject) ` -DifferenceObject ($differenceObject) } if ($null -ne $compareResult) { #Write-Verbose -Message "Configuration drift - simple object key: $key Source {$sourceValue} Target {$targetValue}" return $false } } } } return $true } function Convert-M365DSCDRGComplexTypeToHashtable { [CmdletBinding()] [OutputType([hashtable], [hashtable[]])] param( [Parameter(Mandatory = $true)] $ComplexObject ) if ($ComplexObject.getType().Fullname -like '*[[\]]') { $results = @() foreach ($item in $ComplexObject) { $hash = Convert-M365DSCDRGComplexTypeToHashtable -ComplexObject $item $results += $hash } #Write-Verbose -Message ("Convert-M365DSCDRGComplexTypeToHashtable >>> results: "+(convertTo-JSON $results -Depth 20)) # PowerShell returns all non-captured stream output, not just the argument of the return statement. #An empty array is mangled into $null in the process. #However, an array can be preserved on return by prepending it with the array construction operator (,) return , [hashtable[]]$results } $hashComplexObject = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $ComplexObject if ($null -ne $hashComplexObject) { $results = $hashComplexObject.clone() $keys = $hashComplexObject.Keys | Where-Object -FilterScript { $_ -ne 'PSComputerName' } foreach ($key in $keys) { if ($hashComplexObject[$key] -and $hashComplexObject[$key].getType().Fullname -like '*CimInstance*') { $results[$key] = Convert-M365DSCDRGComplexTypeToHashtable -ComplexObject $hashComplexObject[$key] } else { $propertyName = $key[0].ToString().ToLower() + $key.Substring(1, $key.Length - 1) $propertyValue = $results[$key] $results.remove($key) | Out-Null $results.add($propertyName, $propertyValue) } } } return [hashtable]$results } function Get-SettingCatalogSettingValue { [CmdletBinding()] [OutputType([System.Collections.Hashtable],[System.Collections.Hashtable[]])] param ( [Parameter()] $SettingValue, [Parameter()] $SettingValueType ) switch -Wildcard ($SettingValueType) { '*ChoiceSettingInstance' { $complexValue = @{} $complexValue.Add('odataType',$SettingValue.'@odata.type') $complexValue.Add('Value',$SettingValue.value) $children = @() foreach($child in $SettingValue.children) { $complexChild = @{} $complexChild.Add('SettingDefinitionId', $child.settingDefinitionId) $complexChild.Add('odataType', $child.'@odata.type') $valueName = $child.'@odata.type'.replace('#microsoft.graph.deviceManagementConfiguration', '').replace('Instance', 'Value') $valueName = Get-StringFirstCharacterToLower -Value $valueName $rawValue = $child.$valueName $childSettingValue = Get-SettingCatalogSettingValue -SettingValue $rawValue -SettingValueType $child.'@odata.type' $complexChild.Add($valueName,$childSettingValue) $children += $complexChild } $complexValue.Add('Children',$children) } '*ChoiceSettingCollectionInstance' { $complexCollection = @() foreach($item in $SettingValue) { $complexValue = @{} $complexValue.Add('Value',$item.value) $children = @() foreach($child in $item.children) { $complexChild = @{} $complexChild.Add('SettingDefinitionId', $child.settingDefinitionId) $complexChild.Add('odataType', $child.'@odata.type') $valueName = $child.'@odata.type'.replace('#microsoft.graph.deviceManagementConfiguration', '').replace('Instance', 'Value') $valueName = Get-StringFirstCharacterToLower -Value $valueName $rawValue = $child.$valueName $childSettingValue = Get-SettingCatalogSettingValue -SettingValue $rawValue -SettingValueType $child.'@odata.type' $complexChild.Add($valueName,$childSettingValue) $children += $complexChild } $complexValue.Add('Children',$children) $complexCollection += $complexValue } return ,([hashtable[]]$complexCollection) } '*SimpleSettingInstance' { $complexValue = @{} $complexValue.Add('odataType',$SettingValue.'@odata.type') $valueName = 'IntValue' $value = $SettingValue.value if($SettingValue.'@odata.type' -ne '#microsoft.graph.deviceManagementConfigurationIntegerSettingValue') { $valueName = 'StringValue' } $complexValue.Add($valueName,$value) if($SettingValue.'@odata.type' -eq '#microsoft.graph.deviceManagementConfigurationSecretSettingValue') { $complexValue.Add('ValueState',$SettingValue.valueState) } } '*SimpleSettingCollectionInstance' { $complexCollection = @() foreach($item in $SettingValue) { $complexValue = @{} $complexValue.Add('odataType',$item.'@odata.type') $valueName = 'IntValue' $value = $item.value if($item.'@odata.type' -ne '#microsoft.graph.deviceManagementConfigurationIntegerSettingValue') { $valueName = 'StringValue' } $complexValue.Add($valueName,$value) if($item.'@odata.type' -eq '#microsoft.graph.deviceManagementConfigurationSecretSettingValue') { $complexValue.Add('ValueState',$item.valueState) } $complexCollection += $complexValue } return ,([hashtable[]]$complexCollection) } '*GroupSettingInstance' { $complexValue = @{} $complexValue.Add('odataType',$SettingValue.'@odata.type') $children = @() foreach($child in $SettingValue.children) { $complexChild = @{} $complexChild.Add('SettingDefinitionId', $child.settingDefinitionId) $complexChild.Add('odataType', $child.'@odata.type') $valueName = $child.'@odata.type'.replace('#microsoft.graph.deviceManagementConfiguration', '').replace('Instance', 'Value') $valueName = Get-StringFirstCharacterToLower -Value $valueName $rawValue = $child.$valueName $settingValue = Get-SettingCatalogSettingValue -SettingValue $rawValue -SettingValueType $child.'@odata.type' $complexChild.Add($valueName,$settingValue) $children += $complexChild } $complexValue.Add('Children',$children) } '*GroupSettingCollectionInstance' { $complexCollection = @() foreach($groupSettingValue in $SettingValue) { $complexValue = @{} #$complexValue.Add('odataType',$SettingValue.'@odata.type') $children = @() foreach($child in $groupSettingValue.children) { $complexChild = @{} $complexChild.Add('SettingDefinitionId', $child.settingDefinitionId) $complexChild.Add('odataType', $child.'@odata.type') $valueName = $child.'@odata.type'.replace('#microsoft.graph.deviceManagementConfiguration', '').replace('Instance', 'Value') $valueName = Get-StringFirstCharacterToLower -Value $valueName $rawValue = $child.$valueName $settingValue = Get-SettingCatalogSettingValue -SettingValue $rawValue -SettingValueType $child.'@odata.type' $complexChild.Add($valueName,$settingValue) $children += $complexChild } $complexValue.Add('Children',$children) $complexCollection += $complexValue } return ,([hashtable[]]$complexCollection) } } return $complexValue } function Get-SettingCatalogPolicySettingsFromTemplate { [CmdletBinding()] [OutputType([System.Array])] param ( [Parameter(Mandatory = $true)] [System.Collections.Hashtable] $DSCParams, [Parameter(Mandatory = $true)] [System.String] $templateReferenceId ) $DSCParams.Remove('Identity') | Out-Null $DSCParams.Remove('DisplayName') | Out-Null $DSCParams.Remove('Description') | Out-Null $settings = @() $templateSettings = Get-MgDeviceManagementConfigurationPolicyTemplateSettingTemplate -DeviceManagementConfigurationPolicyTemplateId $templateReferenceId $simpleSettings = @() $simpleSettings += $templateSettings.SettingInstanceTemplate | Where-Object -FilterScript ` { $_.AdditionalProperties.'@odata.type' -ne '#microsoft.graph.deviceManagementConfigurationGroupSettingCollectionInstanceTemplate' } foreach ($templateSetting in $simpleSettings) { $setting = @{} $settingKey = $DSCParams.keys | Where-Object -FilterScript { $templateSetting.settingDefinitionId -like "*$($_)" } if ((-not [String]::IsNullOrEmpty($settingKey)) -and $DSCParams."$settingKey") { $setting.add('@odata.type', '#microsoft.graph.deviceManagementConfigurationSetting') $myFormattedSetting = Format-M365DSCParamsToSettingInstance -DSCParams @{$settingKey = $DSCParams."$settingKey" } ` -TemplateSetting $templateSetting $setting.add('settingInstance', $myFormattedSetting) $settings += $setting $DSCParams.Remove($settingKey) | Out-Null } } #Prepare attacksurfacereductionrules groupCollectionTemplateSettings $groupCollectionTemplateSettings = @() $groupCollectionTemplateSettings += $templateSettings.SettingInstanceTemplate | Where-Object -FilterScript ` { $_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.deviceManagementConfigurationGroupSettingCollectionInstanceTemplate' } foreach ($groupCollectionTemplateSetting in $groupCollectionTemplateSettings) { $setting = @{} $setting.add('@odata.type', '#microsoft.graph.deviceManagementConfigurationSetting') $settingInstance = [ordered]@{} $settingInstance.add('@odata.type', '#microsoft.graph.deviceManagementConfigurationGroupSettingCollectionInstance') $settingInstance.add('settingDefinitionId', $groupCollectionTemplateSetting.settingDefinitionId) $settingInstance.add('settingInstanceTemplateReference', @{ '@odata.type' = '#microsoft.graph.deviceManagementConfigurationSettingInstanceTemplateReference' 'settingInstanceTemplateId' = $groupCollectionTemplateSetting.settingInstanceTemplateId }) $groupSettingCollectionValues = @() $groupSettingCollectionValueChildren = @() $groupSettingCollectionValue = @{} $groupSettingCollectionValue.add('@odata.type', '#microsoft.graph.deviceManagementConfigurationGroupSettingValue') $settingValueTemplateId = $groupCollectionTemplateSetting.AdditionalProperties.groupSettingCollectionValueTemplate.settingValueTemplateId if (-Not [string]::IsNullOrEmpty($settingValueTemplateId)) { $groupSettingCollectionValue.add('settingValueTemplateReference', @{'settingValueTemplateId' = $SettingValueTemplateId }) } foreach ($key in $DSCParams.keys) { $templateValue = $groupCollectionTemplateSetting.AdditionalProperties.groupSettingCollectionValueTemplate.children | Where-Object ` -FilterScript { $_.settingDefinitionId -like "*$key" } if ($templateValue) { $groupSettingCollectionValueChild = Format-M365DSCParamsToSettingInstance ` -DSCParams @{$key = $DSCParams."$key" } ` -TemplateSetting $templateValue ` -IncludeSettingValueTemplateId $false ` -IncludeSettingInstanceTemplateId $false $groupSettingCollectionValueChildren += $groupSettingCollectionValueChild } } $groupSettingCollectionValue.add('children', $groupSettingCollectionValueChildren) $groupSettingCollectionValues += $groupSettingCollectionValue $settingInstance.add('groupSettingCollectionValue', $groupSettingCollectionValues) $setting.add('settingInstance', $settingInstance) if ($setting.settingInstance.groupSettingCollectionValue.children.count -gt 0) { $settings += $setting } } return $settings } function New-IntuneSettingCatalogPolicy { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter()] [System.String] $Description, [Parameter()] [System.String] $Platforms, [Parameter()] [System.String] $Technologies, [Parameter()] [System.String] $TemplateReferenceId, [Parameter()] [Array] $Settings ) try { $Uri = '/beta/deviceManagement/configurationPolicies' $policy = @{ 'name' = $Name 'description' = $Description 'platforms' = $Platforms 'technologies' = $Technologies 'templateReference' = @{'templateId' = $TemplateReferenceId } 'settings' = $Settings } $body = $policy | ConvertTo-Json -Depth 20 #write-verbose -Message $body Invoke-MgGraphRequest -Method POST -Uri $Uri -Body $body -ErrorAction Stop } catch { New-M365DSCLogEntry -Message 'Error updating data:' ` -Exception $_ ` -Source $($MyInvocation.MyCommand.Source) ` -TenantId $TenantId ` -Credential $Credential return $null } } function Update-IntuneSettingCatalogPolicy { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [System.String] $DeviceConfigurationPolicyId, [Parameter()] [System.String] $Name, [Parameter()] [System.String] $Description, [Parameter()] [System.String] $Platforms, [Parameter()] [System.String] $Technologies, [Parameter()] [System.String] $TemplateReferenceId, [Parameter()] [Array] $Settings ) try { $Uri = "/beta/deviceManagement/configurationPolicies/$DeviceConfigurationPolicyId" $policy = @{ 'name' = $Name 'description' = $Description 'platforms' = $Platforms 'templateReference' = @{'templateId' = $TemplateReferenceId } 'technologies' = $Technologies 'settings' = $Settings } $body = $policy | ConvertTo-Json -Depth 20 #write-verbose -Message $body Invoke-MgGraphRequest -Method PUT -Uri $Uri -Body $body -ErrorAction Stop } catch { New-M365DSCLogEntry -Message 'Error updating data:' ` -Exception $_ ` -Source $($MyInvocation.MyCommand.Source) ` -TenantId $TenantId ` -Credential $Credential return $null } } function ConvertFrom-IntunePolicyAssignment { [CmdletBinding()] [OutputType([System.Collections.Hashtable[]])] param ( [Parameter(Mandatory = $true)] [Array] $Assignments, [Parameter()] [System.Boolean] $IncludeDeviceFilter = $true ) $assignmentResult = @() foreach ($assignment in $Assignments) { $hashAssignment = @{} $dataType = $assignment.Target.AdditionalProperties."@odata.type" $groupId = $assignment.Target.AdditionalProperties.groupId $hashAssignment.add('dataType',$dataType) if (-not [string]::IsNullOrEmpty($groupId)) { $hashAssignment.add('groupId', $groupId) $group = Get-MgGroup -GroupId ($groupId) -ErrorAction SilentlyContinue if ($null -ne $group) { $groupDisplayName = $group.DisplayName } } if ($dataType -eq '#microsoft.graph.allLicensedUsersAssignmentTarget') { $groupDisplayName = 'All users' } if ($dataType -eq '#microsoft.graph.allDevicesAssignmentTarget') { $groupDisplayName = 'All devices' } if ($null -ne $groupDisplayName) { $hashAssignment.add('groupDisplayName', $groupDisplayName) } if ($IncludeDeviceFilter) { if ($null -ne $assignment.Target.DeviceAndAppManagementAssignmentFilterType) { $hashAssignment.add('deviceAndAppManagementAssignmentFilterType', $assignment.Target.DeviceAndAppManagementAssignmentFilterType.ToString()) } if ($null -ne $assignment.Target.DeviceAndAppManagementAssignmentFilterId) { $hashAssignment.add('deviceAndAppManagementAssignmentFilterId', $assignment.Target.DeviceAndAppManagementAssignmentFilterId) } } $assignmentResult += $hashAssignment } return $assignmentResult } function ConvertTo-IntunePolicyAssignment { [CmdletBinding()] [OutputType([Hashtable[]])] param ( [Parameter(Mandatory = $true)] $Assignments, [Parameter()] [System.Boolean] $IncludeDeviceFilter = $true ) $assignmentResult = @() foreach ($assignment in $Assignments) { $target = @{"@odata.type" = $assignment.dataType} if ($IncludeDeviceFilter) { if ($null -ne $assignment.DeviceAndAppManagementAssignmentFilterId) { $target.add('deviceAndAppManagementAssignmentFilterId', $assignment.DeviceAndAppManagementAssignmentFilterId) } if ($null -ne $assignment.DeviceAndAppManagementAssignmentFilterType) { $target.add('deviceAndAppManagementAssignmentFilterType',$assignment.DeviceAndAppManagementAssignmentFilterType) } } if ($assignment.dataType -like '*GroupAssignmentTarget') { $group = Get-MgGroup -GroupId ($assignment.groupId) -ErrorAction SilentlyContinue if ($null -eq $group) { if ($assignment.groupDisplayName) { $group = Get-MgGroup -Filter "DisplayName eq '$($assignment.groupDisplayName)'" -ErrorAction SilentlyContinue if ($null -eq $group) { $message = "Skipping assignment for the group with DisplayName {$($assignment.groupDisplayName)} as it could not be found in the directory.`r`n" $message += "Please update your DSC resource extract with the correct groupId or groupDisplayName." write-verbose -Message $message $target = $null } if ($group -and $group.count -gt 1) { $message = "Skipping assignment for the group with DisplayName {$($assignment.groupDisplayName)} as it is not unique in the directory.`r`n" $message += "Please update your DSC resource extract with the correct groupId or a unique group DisplayName." write-verbose -Message $message $group = $null $target = $null } } else { $message = "Skipping assignment for the group with Id {$($assignment.groupId)} as it could not be found in the directory.`r`n" $message += "Please update your DSC resource extract with the correct groupId or a unique group DisplayName." write-verbose -Message $message $target = $null } } #Skipping assignment if group not found from either groupId or groupDisplayName if ($null -ne $group) { $target.add('groupId',$group.Id) } } if ($target) { $assignmentResult += @{target = $target} } } return $assignmentResult } function Compare-M365DSCIntunePolicyAssignment { [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter()] $Source, [Parameter()] $Target ) $testResult = $Source.Count -eq $Target.Count if ($testResult) { foreach ($assignment in $Source) { if ($assignment.dataType -like '*groupAssignmentTarget') { $assignmentTarget = $Target | Where-Object -FilterScript { $_.dataType -eq $assignment.DataType -and $_.groupId -eq $assignment.groupId } $testResult = $null -ne $assignmentTarget # Using assignment groupDisplayName only if the groupId is not found in the directory otherwise groupId should be the key if (-not $testResult) { $groupNotFound = $null -eq (Get-MgGroup -GroupId ($assignment.groupId) -ErrorAction SilentlyContinue) } if (-not $testResult -and $groupNotFound) { $assignmentTarget = $Target | Where-Object -FilterScript { $_.dataType -eq $assignment.DataType -and $_.groupDisplayName -eq $assignment.groupDisplayName } $testResult = $null -ne $assignmentTarget } if ($testResult) { $isFilterIdSpecified = $assignment.deviceAndAppManagementAssignmentFilterType -ne 'none' $testResult = $assignment.deviceAndAppManagementAssignmentFilterType -eq $assignmentTarget.deviceAndAppManagementAssignmentFilterType if ($testResult -and $isFilterIdSpecified) { $testResult = $assignment.deviceAndAppManagementAssignmentFilterId -eq $assignmentTarget.deviceAndAppManagementAssignmentFilterId } } if ($testResult) { $testResult = $assignment.collectionId -eq $assignmentTarget.collectionId } } else { $testResult = $null -ne ($Target | Where-Object -FilterScript { $_.dataType -eq $assignment.DataType }) } if (-not $testResult) { break } } } return $testResult } function Update-DeviceConfigurationPolicyAssignment { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [System.String] $DeviceConfigurationPolicyId, [Parameter()] [Array] $Targets, [Parameter()] [System.String] $Repository = 'deviceManagement/configurationPolicies', [Parameter()] [ValidateSet('v1.0','beta')] [System.String] $APIVersion = 'beta', [Parameter()] [System.String] $RootIdentifier = 'assignments' ) try { $deviceManagementPolicyAssignments = @() $Uri = "/$APIVersion/$Repository/$DeviceConfigurationPolicyId/assign" foreach ($target in $targets) { $formattedTarget = @{"@odata.type" = $target.dataType} if(-not $formattedTarget."@odata.type" -and $target."@odata.type") { $formattedTarget."@odata.type" = $target."@odata.type" } if ($target.groupId) { $group = Get-MgGroup -GroupId ($target.groupId) -ErrorAction SilentlyContinue if ($null -eq $group) { if ($target.groupDisplayName) { $group = Get-MgGroup -Filter "DisplayName eq '$($target.groupDisplayName)'" -ErrorAction SilentlyContinue if ($null -eq $group) { $message = "Skipping assignment for the group with DisplayName {$($target.groupDisplayName)} as it could not be found in the directory.`r`n" $message += "Please update your DSC resource extract with the correct groupId or groupDisplayName." write-verbose -Message $message $target = $null } if ($group -and $group.count -gt 1) { $message = "Skipping assignment for the group with DisplayName {$($target.groupDisplayName)} as it is not unique in the directory.`r`n" $message += "Please update your DSC resource extract with the correct groupId or a unique group DisplayName." write-verbose -Message $message $group = $null $target = $null } } else { $message = "Skipping assignment for the group with Id {$($target.groupId)} as it could not be found in the directory.`r`n" $message += "Please update your DSC resource extract with the correct groupId or a unique group DisplayName." write-verbose -Message $message $target = $null } } #Skipping assignment if group not found from either groupId or groupDisplayName if ($null -ne $group) { $formattedTarget.add('groupId',$group.Id) } } if ($target.collectionId) { $formattedTarget.Add('collectionId',$target.collectionId) } if ($target.deviceAndAppManagementAssignmentFilterType) { $formattedTarget.Add('deviceAndAppManagementAssignmentFilterType',$target.deviceAndAppManagementAssignmentFilterType) } if ($target.deviceAndAppManagementAssignmentFilterId) { $formattedTarget.Add('deviceAndAppManagementAssignmentFilterId',$target.deviceAndAppManagementAssignmentFilterId) } $deviceManagementPolicyAssignments += @{'target' = $formattedTarget} } $body = @{$RootIdentifier = $deviceManagementPolicyAssignments} | ConvertTo-Json -Depth 20 Write-Verbose -Message $body Invoke-MgGraphRequest -Method POST -Uri $Uri -Body $body -ErrorAction Stop } catch { New-M365DSCLogEntry -Message 'Error updating data:' ` -Exception $_ ` -Source $($MyInvocation.MyCommand.Source) ` -TenantId $TenantId ` -Credential $Credential return $null } } function Get-OmaSettingPlainTextValue { [CmdletBinding()] [OutputType([System.String])] Param( [Parameter(Mandatory = $true)] [System.String] $SecretReferenceValueId, [Parameter()] [ValidateSet('v1.0', 'beta')] [System.String] $APIVersion = 'beta' ) try { <# e.g. PolicyId for SecretReferenceValueId '35ea58ec-2a79-471d-8eea-7e28e6cd2722_bdf6c690-05fb-4d02-835d-5a7406c35d58_abe32712-2255-445f-a35e-0c6f143d82ca' is 'bdf6c690-05fb-4d02-835d-5a7406c35d58' #> $SplitSecretReferenceValueId = $SecretReferenceValueId.Split("_") if ($SplitSecretReferenceValueId.Count -eq 3) { $PolicyId = $SplitSecretReferenceValueId[1] } else { return $null } } catch { return $null } $Repository = 'deviceManagement/deviceConfigurations' $Uri = "/{0}/{1}/{2}/getOmaSettingPlainTextValue(secretReferenceValueId='{3}')" -f $APIVersion, $Repository, $PolicyId, $SecretReferenceValueId try { $Result = Invoke-MgGraphRequest -Method GET -Uri $Uri -ErrorAction Stop } catch { $Message = "Error decrypting OmaSetting with SecretReferenceValueId {0}:" -f $SecretReferenceValueId New-M365DSCLogEntry -Message $Message ` -Exception $_ ` -Source $($MyInvocation.MyCommand.Source) ` -TenantId $TenantId ` -Credential $Credential return $null } if (![String]::IsNullOrEmpty($Result.Value)) { return $Result.Value } else { return $null } } function Get-IntuneSettingCatalogPolicySetting { [CmdletBinding()] [OutputType([System.Array])] param( [Parameter(Mandatory = 'true')] [System.Collections.Hashtable] $DSCParams, [Parameter(Mandatory = 'true')] [System.String] $TemplateId ) $global:excludedDefinitionIds = @() $DSCParams.Remove('Identity') | Out-Null $DSCParams.Remove('DisplayName') | Out-Null $DSCParams.Remove('Description') | Out-Null #Prepare setting definitions mapping $settingTemplates = Get-MgBetaDeviceManagementConfigurationPolicyTemplateSettingTemplate -DeviceManagementConfigurationPolicyTemplateId $TemplateId -ExpandProperty 'SettingDefinitions' $settingInstances = @() foreach ($settingInstanceTemplate in $settingTemplates.SettingInstanceTemplate) { $settingInstance = @{} $settingDefinition = $settingTemplates.SettingDefinitions | Where-Object { $_.Id -eq $settingInstanceTemplate.SettingDefinitionId -and ` ($_.AdditionalProperties.dependentOn.Count -eq 0 -and $_.AdditionalProperties.options.dependentOn.Count -eq 0) } $settingName = $settingInstanceTemplate.SettingDefinitionId.split('_') | Select-Object -Last 1 $settingType = $settingInstanceTemplate.AdditionalProperties.'@odata.type'.Replace('InstanceTemplate', 'Instance') $settingInstance.Add('settingDefinitionId', $settingInstanceTemplate.settingDefinitionId) $settingInstance.Add('@odata.type', $settingType) if (-not [string]::IsNullOrEmpty($settingInstanceTemplate.settingInstanceTemplateId)) { $settingInstance.Add('settingInstanceTemplateReference', @{'settingInstanceTemplateId' = $settingInstanceTemplate.settingInstanceTemplateId }) } $settingValueName = $settingType.Replace('#microsoft.graph.deviceManagementConfiguration', '').Replace('Instance', 'Value') $settingValueName = $settingValueName.Substring(0, 1).ToLower() + $settingValueName.Substring(1, $settingValueName.length - 1 ) $settingValueType = $settingInstanceTemplate.AdditionalProperties."$($settingValueName)Template".'@odata.type' if ($null -ne $settingValueType) { $settingValueType = $settingValueType.Replace('ValueTemplate', 'Value') } $settingValueTemplateId = $settingInstanceTemplate.AdditionalProperties."$($settingValueName)Template".settingValueTemplateId $settingValue = Get-IntuneSettingCatalogPolicySettingInstanceValue ` -DSCParams $DSCParams ` -SettingDefinition $settingDefinition ` -SettingTemplates $settingTemplates ` -SettingName $settingName ` -SettingType $settingType ` -SettingValueName $settingValueName ` -SettingValueType $settingValueType ` -SettingValueTemplateId $settingValueTemplateId if ($settingValue.Count -gt 0) { $settingInstance += [Hashtable]$settingValue $settingInstances += @{ '@odata.type' = '#microsoft.graph.deviceManagementConfigurationSetting' 'settingInstance' = $settingInstance } } } return $settingInstances } function Get-IntuneSettingCatalogPolicySettingInstanceValue { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param( [Parameter(Mandatory = 'true')] [System.Collections.Hashtable] $DSCParams, [Parameter()] $SettingDefinition, [Parameter()] $SettingTemplates, [Parameter()] [System.String] $SettingType, [Parameter()] [System.String] $SettingName, [Parameter()] [System.String] $SettingValueName, [Parameter()] [System.String] $SettingValueType, [Parameter()] [System.String] $SettingValueTemplateId ) $settingValuesToReturn = @{} if ($null -eq $global:excludedDefinitionIds) { $global:excludedDefinitionIds = @() } switch ($settingType) { '#microsoft.graph.deviceManagementConfigurationGroupSettingCollectionInstance' { $groupSettingCollectionValue = @{} $groupSettingCollectionValueChildren = @() $groupSettingCollectionDefinitionChildren = $SettingTemplates.SettingDefinitions | Where-Object { ($_.AdditionalProperties.dependentOn.Count -gt 0 -and $_.AdditionalProperties.dependentOn.parentSettingId.Contains($SettingDefinition.Id)) -or ($_.AdditionalProperties.options.dependentOn.Count -gt 0 -and $_.AdditionalProperties.options.dependentOn.parentSettingId.Contains($SettingDefinition.Id)) } foreach ($childDefinition in $groupSettingCollectionDefinitionChildren) { $childSettingName = $childDefinition.Name $childSettingType = $childDefinition.AdditionalProperties.'@odata.type'.Replace('Definition', 'Instance') $childSettingValueName = $childSettingType.Replace('#microsoft.graph.deviceManagementConfiguration', '').Replace('Instance', 'Value') $childSettingValueType = "#microsoft.graph.deviceManagementConfiguration$($childSettingValueName)" $childSettingValueName = $childSettingValueName.Substring(0, 1).ToLower() + $childSettingValueName.Substring(1, $childSettingValueName.length - 1 ) $childSettingValue = Get-IntuneSettingCatalogPolicySettingInstanceValue ` -DSCParams $DSCParams ` -SettingDefinition $childDefinition ` -SettingTemplates $SettingTemplates ` -SettingName $childSettingName ` -SettingType $childDefinition.AdditionalProperties.'@odata.type' ` -SettingValueName $childSettingValueName ` -SettingValueType $childSettingValueType ` if ($null -ne $childSettingValue) { $childSettingValue.Add('settingDefinitionId', $childDefinition.Id) $childSettingValue.Add('@odata.type', $childSettingType) $groupSettingCollectionValueChildren += $childSettingValue } } if ($groupSettingCollectionDefinitionChildren.Count -gt 0) { $groupSettingCollectionValue.Add('children', $groupSettingCollectionValueChildren) $settingValuesToReturn.Add('groupSettingCollectionValue', @($groupSettingCollectionValue)) } } { $_ -eq '#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance' -or $_ -eq '#microsoft.graph.deviceManagementConfigurationChoiceSettingDefinition' } { $choiceSettingValue = @{} $choiceSettingValueChildren = @() $choiceSettingDefinitionChildren = $SettingTemplates.SettingDefinitions | Where-Object { ($_.AdditionalProperties.dependentOn.Count -gt 0 -and $_.AdditionalProperties.dependentOn.parentSettingId.Contains($SettingDefinition.Id)) -or ($_.AdditionalProperties.options.dependentOn.Count -gt 0 -and $_.AdditionalProperties.options.dependentOn.parentSettingId.Contains($SettingDefinition.Id)) } foreach ($childDefinition in $choiceSettingDefinitionChildren) { $childSettingName = $childDefinition.Name $childSettingType = $childDefinition.AdditionalProperties.'@odata.type'.Replace('Definition', 'Instance') $childSettingValueName = $childSettingType.Replace('#microsoft.graph.deviceManagementConfiguration', '').Replace('Instance', 'Value') $childSettingValueType = "#microsoft.graph.deviceManagementConfiguration$($childSettingValueName)" $childSettingValueName = $childSettingValueName.Substring(0, 1).ToLower() + $childSettingValueName.Substring(1, $childSettingValueName.length - 1 ) $childSettingValue = Get-IntuneSettingCatalogPolicySettingInstanceValue ` -DSCParams $DSCParams ` -SettingDefinition $childDefinition ` -SettingTemplates $SettingTemplates ` -SettingName $childSettingName ` -SettingType $childDefinition.AdditionalProperties.'@odata.type' ` -SettingValueName $childSettingValueName ` -SettingValueType $childSettingValueType ` if ($childSettingValue.Keys.Count -gt 0) { $childSettingValue.Add('settingDefinitionId', $childDefinition.Id) $childSettingValue.Add('@odata.type', $childSettingValueType) $choiceSettingValueChildren += $childSettingValue } } if ($choiceSettingDefinitionChildren.Count -gt 0) { $choiceSettingValue.Add('children', $choiceSettingValueChildren) } if ($null -ne $DSCParams[$SettingName]) { $value = "$($SettingDefinition.Id)_$($DSCParams[$SettingName])" $choiceSettingValue.Add('value', $value) $choiceSettingValue.Add('@odata.type', $SettingType.Replace('Instance', 'Value')) if (-Not [string]::IsNullOrEmpty($SettingValueTemplateId)) { $choiceSettingValue.Add('settingValueTemplateReference', @{'settingValueTemplateId' = $SettingValueTemplateId }) } } if ($choiceSettingValue.Keys.Count -gt 0) { $settingValuesToReturn.Add('choiceSettingValue', $choiceSettingValue) } } { $_ -eq '#microsoft.graph.deviceManagementConfigurationSimpleSettingCollectionInstance' -or $_ -eq '#microsoft.graph.deviceManagementConfigurationSimpleSettingCollectionDefinition' } { $values = @() foreach ($key in $DSCParams.Keys) { $matchCombined = $false $matchesId = $false $name = $SettingTemplates.SettingDefinitions.Name | Where-Object -FilterScript { $_ -eq $key } if ($name.Count -ne 1) { # Key might have been combined with parent setting, try to split it if ($key -like "*_*") { $parentSettingName = $key.Split('_')[0] $childSettingName = $key.Replace("$($parentSettingName)_", '') $parentDefinition = $SettingTemplates.SettingDefinitions | Where-Object { $_.Name -eq $parentSettingName } $childDefinition = $SettingTemplates.SettingDefinitions | Where-Object { $_.Name -eq $childSettingName -and $_.AdditionalProperties.dependentOn.parentSettingId.Contains($parentDefinition.Id) } if ($null -ne $parentDefinition -and $null -ne $childDefinition) { $matchCombined = $true } } if (-not $matchCombined) { # Parent was not combined, look for the id $SettingTemplates.SettingDefinitions | ForEach-Object { if ($_.Id -notin $global:excludedDefinitionIds -and $_.Name -eq $SettingName -and $_.Id -like "*$key") { $global:excludedDefinitionIds += $_.Id $matchesId = $true } } } } if (($name.Count -eq 1 -and $SettingName -eq $key) -or $matchCombined -or $matchesId) { $values = $DSCParams[$key] break } } $settingValueCollection = @() foreach ($v in $values) { $settingValueCollection += @{ value = $v '@odata.type' = $settingValueType } } if ($settingValueCollection.Count -gt 0) { $settingValuesToReturn.Add($settingValueName, $settingValueCollection) } } Default { $value = $null foreach ($key in $DSCParams.Keys) { $matchCombined = $false $matchesId = $false $name = $SettingTemplates.SettingDefinitions.Name | Where-Object -FilterScript { $_ -eq $key } if ($name.Count -ne 1) { # Key might have been combined with parent setting, try to split it if ($key -like "*_*") { $parentSettingName = $key.Split('_')[0] $childSettingName = $key.Replace("$($parentSettingName)_", '') $parentDefinition = $SettingTemplates.SettingDefinitions | Where-Object { $_.Name -eq $parentSettingName } $childDefinition = $SettingTemplates.SettingDefinitions | Where-Object { $_.Name -eq $childSettingName } if ($null -ne $parentDefinition -and $null -ne $childDefinition) { # Parent was combined with child setting $matchCombined = $true } } if (-not $matchCombined) { # Parent was not combined, look for the id $SettingTemplates.SettingDefinitions | ForEach-Object { if ($_.Id -notin $global:excludedDefinitionIds -and $_.Name -eq $SettingName -and $_.Id -like "*$key") { $global:excludedDefinitionIds += $_.Id $matchesId = $true } } } } if (($name.Count -eq 1 -and $SettingName -eq $key) -or $matchCombined -or $matchesId) { if ($SettingValueType -like "*Simple*" -or $SettingValueType -in @("#microsoft.graph.deviceManagementConfigurationIntegerSettingValue", "#microsoft.graph.deviceManagementConfigurationStringSettingValue")) { $value = $DSCParams[$key] } else { $value = "$($SettingDefinition.Id)_$($DSCParams[$key])" } break } } if ($null -eq $value) { return $null } $settingValue = @{} if (-Not [string]::IsNullOrEmpty($settingValueType)) { $settingValue.Add('@odata.type', $settingValueType) } if (-Not [string]::IsNullOrEmpty($settingValueTemplateId)) { $settingValue.Add('settingValueTemplateReference', @{'settingValueTemplateId' = $settingValueTemplateId }) } $settingValue.Add('value', $value) $settingValuesToReturn.Add($settingValueName, $settingValue) } } return $settingValuesToReturn } function Export-IntuneSettingCatalogPolicySettings { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param( [Parameter( Mandatory = $true, ParameterSetName = 'Start' )] $Settings, [Parameter( Mandatory = $true )] [System.Collections.Hashtable]$ReturnHashtable, [Parameter( Mandatory = $true, ParameterSetName = 'Setting' )] $SettingInstance, [Parameter( Mandatory = $true, ParameterSetName = 'Setting' )] $SettingDefinitions, [Parameter( ParameterSetName = 'Setting' )] [switch]$IsRoot ) if ($PSCmdlet.ParameterSetName -eq 'Start') { foreach ($setting in $Settings) { Export-IntuneSettingCatalogPolicySettings -SettingInstance $setting.SettingInstance -SettingDefinitions $setting.SettingDefinitions -ReturnHashtable $ReturnHashtable -IsRoot } return $ReturnHashtable } $addToParameters = $true $settingDefinition = $SettingDefinitions | Where-Object -FilterScript { $_.Id -eq $SettingInstance.settingDefinitionId } $settingName = $settingDefinition | Select-Object -ExpandProperty Name # Check if the name is unique $settingMatches = $SettingDefinitions | Where-Object -FilterScript { $_.Name -eq $settingName } if ($settingMatches.Count -gt 1) { if ($settingDefinition.AdditionalProperties.dependentOn.parentSettingId.Count -gt 0) { $parentSetting = $SettingDefinitions | Where-Object -FilterScript { $_.Id -eq $($settingDefinition.AdditionalProperties.dependentOn.parentSettingId | Select-Object -Unique -First 1) } } elseif ($settingDefinition.AdditionalProperties.options.dependentOn.parentSettingId.Count -gt 0) { $parentSetting = $SettingDefinitions | Where-Object -FilterScript { $_.Id -eq $($settingDefinition.AdditionalProperties.dependentOn.parentSettingId | Select-Object -Unique -First 1) } } $combinationMatches = $SettingDefinitions | Where-Object -FilterScript { $_.Name -eq $settingName -and ` (($_.AdditionalProperties.dependentOn.parentSettingId.Count -gt 0 -and $_.AdditionalProperties.dependentOn.parentSettingId.Contains($parentSetting.Id)) -or ` ($_.AdditionalProperties.options.dependentOn.parentSettingId.Count -gt 0 -and $_.AdditionalProperties.options.dependentOn.parentSettingId.Contains($parentSetting.Id))) } # If the combination of parent setting and setting name is unique, add the parent setting name to the setting name if ($combinationMatches.Count -eq 1) { $settingName = $($parentSetting.Name) + "_" + $settingName } # If the combination of parent setting and setting name is still not unique, grab the last part of the setting id else { $parentSettingIdProperty = $parentSetting.Id.Split('_')[-1] $parentSettingIdWithoutProperty = $parentSetting.Id.Replace("_$parentSettingIdProperty", "") # We can't use the entire setting here, because the child setting id does not have to come after the parent setting id $settingName = $settingDefinition.Id.Replace($parentSettingIdWithoutProperty + "_", "").Replace($parentSettingIdProperty + "_", "") } } $odataType = if ($IsRoot) { $SettingInstance.AdditionalProperties.'@odata.type' } else { $SettingInstance.'@odata.type' } switch ($odataType) { '#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance' { $settingValue = if ($IsRoot) { $SettingInstance.AdditionalProperties.simpleSettingValue.value } else { $SettingInstance.simpleSettingValue.value } } '#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance' { $settingValue = if ($IsRoot) { $SettingInstance.AdditionalProperties.choiceSettingValue.value } else { $SettingInstance.choiceSettingValue.value } $settingValue = $settingValue.Split('_') | Select-Object -Last 1 $childSettings = if ($IsRoot) { $SettingInstance.AdditionalProperties.choiceSettingValue.children } else { $SettingInstance.choiceSettingValue.children } foreach ($childSetting in $childSettings) { Export-IntuneSettingCatalogPolicySettings -SettingInstance $childSetting -SettingDefinitions $SettingDefinitions -ReturnHashtable $ReturnHashtable } } '#microsoft.graph.deviceManagementConfigurationGroupSettingCollectionInstance' { $values = @() $childSettings = if ($IsRoot) { $SettingInstance.AdditionalProperties.groupSettingCollectionValue.children } else { $SettingInstance.groupSettingCollectionValue.children } foreach ($value in $childSettings) { Export-IntuneSettingCatalogPolicySettings -SettingInstance $value -SettingDefinitions $SettingDefinitions -ReturnHashtable $ReturnHashtable $addToParameters = $false } } '#microsoft.graph.deviceManagementConfigurationSimpleSettingCollectionInstance' { $values = @() $childValues = if ($IsRoot) { $SettingInstance.AdditionalProperties.simpleSettingCollectionValue.value } else { $SettingInstance.simpleSettingCollectionValue.value } foreach ($value in $childValues) { $values += $value } $settingValue = $values } Default { $settingValue = $SettingInstance.value } } if ($addToParameters) { $ReturnHashtable.Add($settingName, $settingValue) } } function Update-IntuneDeviceConfigurationPolicy { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = 'true')] [System.String] $DeviceConfigurationPolicyId, [Parameter()] [System.String] $Name, [Parameter()] [System.String] $Description, [Parameter()] [System.String] $Platforms, [Parameter()] [System.String] $Technologies, [Parameter()] [System.String] $TemplateReferenceId, [Parameter()] [Array] $Settings ) try { $Uri = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies/$DeviceConfigurationPolicyId" $policy = @{ 'name' = $Name 'description' = $Description 'platforms' = $Platforms 'templateReference' = @{'templateId' = $TemplateReferenceId } 'technologies' = $Technologies 'settings' = $Settings } $body = $policy | ConvertTo-Json -Depth 20 #write-verbose -Message $body Invoke-MgGraphRequest -Method PUT -Uri $Uri -Body $body -ErrorAction Stop } catch { New-M365DSCLogEntry -Message 'Error updating data:' ` -Exception $_ ` -Source $($MyInvocation.MyCommand.Source) ` -TenantId $TenantId ` -Credential $Credential return $null } } |