mirror of
https://github.com/Micke-K/IntuneManagement.git
synced 2026-09-28 19:05:38 +02:00
IntuneManagement 4.0.0-beta1
This commit is contained in:
@@ -0,0 +1,273 @@
|
||||
#ImportOrder 110
|
||||
|
||||
#########################################################################################
|
||||
#
|
||||
# Settings Catalog
|
||||
#
|
||||
#########################################################################################
|
||||
|
||||
# region Settings Catalog
|
||||
|
||||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("TypeNotFound","", Justification = "")]
|
||||
class SettingsCatalogTypeBase : IntunePolicyTypeBase
|
||||
{
|
||||
static [bool] $IsAbstract = $true
|
||||
Hidden [string[]]$_FamilyTypes = @()
|
||||
|
||||
SettingsCatalogTypeBase() : Base()
|
||||
{
|
||||
([SettingsCatalogTypeBase]$this).Init()
|
||||
}
|
||||
|
||||
Init()
|
||||
{
|
||||
$this._NameProperty = "name"
|
||||
$this._PolicyName = "Settings Catalog"
|
||||
$this._ID = "SettingsCatalogBase"
|
||||
$this._API = "deviceManagement/configurationPolicies"
|
||||
$this._PolicyBaseName = "Settings Catalog"
|
||||
$this._PropertiesToRemove = @('settingCount')
|
||||
$this._Permissions = @("DeviceManagementConfiguration.ReadWrite.All")
|
||||
$this._SubTypeColumn = "TemplateFamily=Family"
|
||||
$this._Expand = "Settings"
|
||||
$this._Icon = "DeviceConfiguration"
|
||||
$this._Dependencies = @("ReusableSettings")
|
||||
$this._ObjectClass = "SettingsCatalogObject"
|
||||
$this._VerifyObject = $true
|
||||
|
||||
$this._PolicyTypeOrder = 200
|
||||
}
|
||||
|
||||
[Hashtable]PreUpdateCommand([PSCustomObject]$PolicyObject, [PSCustomObject]$SourceObject)
|
||||
{
|
||||
# Settings Catalog updates must PUT the full body (including settings);
|
||||
# a PATCH carrying the settings payload is rejected by Graph. Same
|
||||
# contract as the portal and the original project
|
||||
# (Start-PreUpdateSettingsCatalog).
|
||||
return @{ "Method" = "PUT" }
|
||||
}
|
||||
|
||||
[Hashtable]GetCompareConfig()
|
||||
{
|
||||
return @{
|
||||
Prop = "settings"
|
||||
GetKey = { param($s) Get-SettingsCatalogSettingKey $s }
|
||||
GetValue = { param($s) Get-SettingsCatalogSettingValue $s }
|
||||
GetCategory = { param($s) Get-SettingsCatalogSettingCategory $s }
|
||||
}
|
||||
}
|
||||
|
||||
[Boolean]CheckPolicy([PSCustomObject]$PolicyObject)
|
||||
{
|
||||
if($PolicyObject.'@odata.type' -ne "#microsoft.graph.deviceManagementConfigurationPolicy") { return $false }
|
||||
|
||||
if($PolicyObject.templateReference.templateFamily -notin $this._FamilyTypes) {
|
||||
return $false
|
||||
}
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
[Hashtable]PreImportCommand([PSCustomObject]$PolicyObject)
|
||||
{
|
||||
if($PolicyObject.JsonObject.templateReference.templateId) {
|
||||
# I do not like this at all and it is a lazy but simple implementation...
|
||||
# It turns out that settingInstanceTemplateId and settingValueTemplateId are case sensitive
|
||||
# and there is ONE setting with a different casing in the Windows Baseline template.
|
||||
# The export saves it with lowercase which causes the import to fail.
|
||||
|
||||
Write-Log "Get template $($PolicyObject.JsonObject.templateReference.templateId)"
|
||||
$templateObj = Invoke-MSGraphAPI -Url "/deviceManagement/configurationPolicyTemplates('$($PolicyObject.JsonObject.templateReference.templateId)')"
|
||||
if($templateObj.lifecycleState -and $templateObj.lifecycleState -ne "active") {
|
||||
Write-Log "Template '$($templateObj.displayName)' '$($templateObj.displayVersion)' is in '$($templateObj.lifecycleState)' state. Current state: $($templateObj.lifecycleState). Import might fail." 2
|
||||
}
|
||||
#Todo: Should probably check for the latest active version and use that instead of the one in the templateReference
|
||||
|
||||
if(-not $script:baseLineTemplate) {
|
||||
$script:baseLineTemplate = @{}
|
||||
}
|
||||
if($script:baseLineTemplate.ContainsKey($PolicyObject.JsonObject.templateReference.templateId)) {
|
||||
$templateReference = $script:baseLineTemplate[$PolicyObject.JsonObject.templateReference.templateId]
|
||||
}
|
||||
else {
|
||||
Write-Log "Get template settings for '$($templateObj.displayName)' '$($templateObj.displayVersion)' ($($PolicyObject.JsonObject.templateReference.templateId))"
|
||||
$templateReference = Invoke-MSGraphAPI -Url "/deviceManagement/configurationPolicyTemplates('$($PolicyObject.JsonObject.templateReference.templateId)')/settingTemplates?`$expand=settingDefinitions&top=1000"
|
||||
$script:baseLineTemplate.Add($PolicyObject.JsonObject.templateReference.templateId, $templateReference)
|
||||
}
|
||||
|
||||
if($templateReference) {
|
||||
$settingsJson = $PolicyObject.JsonObject.Settings | ConvertTo-Json -Depth 50
|
||||
$templateIDs, $dummy = Get-DependencyIDs ($templateReference | ConvertTo-Json -Depth 50)
|
||||
$objectIDs, $dummy = Get-DependencyIDs $settingsJson
|
||||
$diff = Compare-Object @($templateIDs) @($objectIDs) -CaseSensitive
|
||||
$updated = $false
|
||||
foreach($diffItem in ($diff | Where-Object SideIndicator -eq "=>")) {
|
||||
$templateID = $templateIDs | Where-Object { $_ -eq $diffItem.InputObject }
|
||||
if($templateID) {
|
||||
# Found but with different casing
|
||||
$settingsJson = $settingsJson -replace $diffItem.InputObject, $templateID
|
||||
$updated = $true
|
||||
}
|
||||
}
|
||||
if($updated) {
|
||||
$PolicyObject.JsonObject.Settings = @($settingsJson | ConvertFrom-Json -Depth 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
return $null
|
||||
}
|
||||
}
|
||||
|
||||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("TypeNotFound","", Justification = "")]
|
||||
class SettingsCatalogObject : IntunePolicyBase
|
||||
{
|
||||
Hidden [String]$_TemplateFamilyName = $null
|
||||
|
||||
SettingsCatalogObject([PSCustomObject]$JsonObj) : Base($JsonObj) { $this.Init() }
|
||||
|
||||
SettingsCatalogObject() : Base()
|
||||
{
|
||||
$this.Init()
|
||||
}
|
||||
|
||||
Hidden Init()
|
||||
{
|
||||
$settingCatalogClasses = Get-IntuneSettingsCatalogClasses
|
||||
$policyType = $settingCatalogClasses | Where-Object { $_._FamilyTypes -contains $this.JsonObject.templateReference.templateFamily }
|
||||
if($policyType) {
|
||||
$this._PolicyType = $policyType
|
||||
}
|
||||
|
||||
$this._PolicyName = ?? $this.JsonObject.templateReference.templateDisplayName $this._PolicyType.PolicyBaseType
|
||||
|
||||
# Opt into the sub-resource batching contract so Invoke-PolicyHydrate
|
||||
# fans out the assignments fetch via $batch.
|
||||
$this._HasSubResourceBatch = $true
|
||||
|
||||
Add-ObjectProperty $this "TemplateVersion" { $this.JsonObject.templateReference.templateDisplayVersion }
|
||||
if($this.JsonObject.templateReference.templateFamily) {
|
||||
$this._TemplateFamilyName = (?? (Get-EndpointSecurityCategoryName $this.JsonObject.templateReference.templateFamily) $this.JsonObject.templateReference.templateFamily)
|
||||
}
|
||||
else {
|
||||
$this._TemplateFamilyName = $null
|
||||
}
|
||||
Add-ObjectProperty $this "TemplateFamily" { $this._TemplateFamilyName }
|
||||
Add-ObjectProperty $this "Category" { $this._TemplateFamilyName }
|
||||
}
|
||||
|
||||
#Hidden [String] GetName()
|
||||
#{
|
||||
# return $this.JsonObject.Name
|
||||
#}
|
||||
|
||||
# Sub-resource contract. The per-id body GET ($expand=assignments,settings)
|
||||
# returns an empty `assignments` array — known Graph quirk on
|
||||
# configurationPolicies. We hit the dedicated /assignments endpoint via
|
||||
# $batch instead so Invoke-PolicyHydrate fans them out in parallel.
|
||||
[PSCustomObject[]] GetSubResourceBatchRequests([int]$Phase)
|
||||
{
|
||||
if($Phase -ne 1) { return @() }
|
||||
return @([PSCustomObject]@{
|
||||
Key = 'assignments'
|
||||
Url = "$($this._PolicyType.API)/$($this.Id)/assignments"
|
||||
})
|
||||
}
|
||||
|
||||
[PSCustomObject[]] ApplySubResourceBatchResult([int]$Phase, [string]$Key, $Body)
|
||||
{
|
||||
if($Phase -eq 1 -and $Key -eq 'assignments') {
|
||||
# Comma-prefix forces an array reference through the if-expression —
|
||||
# without it, PowerShell unwraps a single-element @() on assignment
|
||||
# and ConvertTo-Json then emits a bare object instead of [{...}].
|
||||
# Lowercase `assignments` matches the Graph wire shape and the
|
||||
# `assignments@odata.*` metadata properties the body fetch leaves
|
||||
# alongside. PSObject is case-insensitive on read, so existing
|
||||
# callers reading `Assignments` keep working.
|
||||
$assignments = if($Body -and $Body.value) { ,@($Body.value) } else { ,@() }
|
||||
Add-Member -InputObject $this.JsonObject -MemberType NoteProperty -Name 'assignments' -Value $assignments -Force
|
||||
}
|
||||
return @()
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#########################################################################################
|
||||
#
|
||||
# Reusable Settings
|
||||
#
|
||||
#########################################################################################
|
||||
|
||||
# region Reusable Settings
|
||||
|
||||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("TypeNotFound","", Justification = "")]
|
||||
class ReusableSettingsTypeBase : IntunePolicyTypeBase
|
||||
{
|
||||
static [bool] $IsAbstract = $true
|
||||
|
||||
ReusableSettingsTypeBase() : Base()
|
||||
{
|
||||
([ReusableSettingsTypeBase]$this).Init()
|
||||
}
|
||||
|
||||
Init()
|
||||
{
|
||||
$this._PolicyName = "Reusable Settings"
|
||||
$this._ID = "ReusableSettingsBase"
|
||||
$this._API = "deviceManagement/reusablePolicySettings"
|
||||
$this._PolicyBaseName = "Reusable Settings"
|
||||
$this._PropertiesToRemove = @('Settings','@OData.Type')
|
||||
$this._Permissions=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
$this._ImportOrder = 70
|
||||
$this._ExpandAssignmentsList = $false
|
||||
$this._SkipRemoveProperties = @("@OData.Type")
|
||||
$this._ObjectClass = "ReusableSettingObject"
|
||||
$this._SupportsAssignments = $false
|
||||
|
||||
$this._PolicyTypeOrder = 210
|
||||
}
|
||||
|
||||
[Boolean]CheckPolicy([PSCustomObject]$PolicyObject)
|
||||
{
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
class ReusableSettingsObjectBase : IntunePolicyBase
|
||||
{
|
||||
ReusableSettingsObjectBase([PSCustomObject]$JsonObj) : Base($JsonObj) { $this.InitReusable() }
|
||||
|
||||
ReusableSettingsObjectBase() : Base() { $this.InitReusable() }
|
||||
|
||||
Hidden InitReusable()
|
||||
{
|
||||
# The list endpoint omits settingInstance; hydration fetches it via the
|
||||
# sub-resource contract (single GET coalesced into the hydrate $batch).
|
||||
# Owns the API in one place — was previously duplicated in
|
||||
# Sync-BulkExportReusableSettings (Internal/PolicyHydrateExtras.ps1).
|
||||
$this._HasSubResourceBatch = $true
|
||||
}
|
||||
|
||||
[PSCustomObject[]] GetSubResourceBatchRequests([int]$Phase)
|
||||
{
|
||||
if($Phase -ne 1) { return @() }
|
||||
if($this.JsonObject.settingInstance) { return @() } # already present
|
||||
return @([PSCustomObject]@{
|
||||
Key = 'reusableSettingInstance'
|
||||
Url = "$($this._PolicyType.API)/$($this.Id)?`$select=settinginstance,displayname,description"
|
||||
Headers = @{ Accept = 'application/json;odata.metadata=none' }
|
||||
})
|
||||
}
|
||||
|
||||
[PSCustomObject[]] ApplySubResourceBatchResult([int]$Phase, [string]$Key, $Body)
|
||||
{
|
||||
if($Phase -eq 1 -and $Key -eq 'reusableSettingInstance' -and $Body -and $Body.settingInstance) {
|
||||
Add-Member -InputObject $this.JsonObject -MemberType NoteProperty -Name 'settingInstance' -Value $Body.settingInstance -Force
|
||||
}
|
||||
return @()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
Reference in New Issue
Block a user