mirror of
https://github.com/Micke-K/IntuneManagement.git
synced 2026-09-28 19:05:38 +02:00
196 lines
9.0 KiB
PowerShell
196 lines
9.0 KiB
PowerShell
# Generic Profile input provider.
|
|
#
|
|
# Ported from old Extensions/Documentation.psm1:2057 (Invoke-TranslateProfile-
|
|
# Object). Claims any @odata.type catalogued in Config/ObjectCategories.json
|
|
# that isn't already handled by a more-specific input provider or handler
|
|
# (first-match-wins dispatch in [DocumentationRegistry]).
|
|
#
|
|
# For each catalogued type:
|
|
# 1. Emit BasicInfo via Add-BasicDefaultValues (which itself reads
|
|
# ObjectCategories.json for Platform-supported + Profile-type rows)
|
|
# 2. Emit Created/Modified/Version via Add-BasicAdditionalValues
|
|
# 3. Find category files: either the explicit Categories list, or every
|
|
# file matching *_<policyType>.json under Config/ObjectInfo/
|
|
# 4. Load each as JSON, dispatch to Invoke-TranslateSection walker
|
|
#
|
|
# The walker handles all the per-prop dataType dispatching to translate
|
|
# primitives (Boolean/Option/MultiOption/Table/Duration etc.).
|
|
|
|
function Invoke-InitializeProfileInput {
|
|
Add-DocumentationInputProvider ([PSCustomObject]@{
|
|
Name = 'Profile'
|
|
Order = 60
|
|
Match = {
|
|
param($PolicyObject)
|
|
$odata = $PolicyObject.JsonObject.'@odata.type'
|
|
if (-not $odata) { return $false }
|
|
if (-not (Get-Command Get-PolicyObjectCategoryInfo -ErrorAction SilentlyContinue)) { return $false }
|
|
$info = Get-PolicyObjectCategoryInfo $odata
|
|
return ($null -ne $info -and $null -ne $info.PolicyType)
|
|
}
|
|
Translate = { param($PolicyObject, $Context) Invoke-TranslateProfilePolicyObject $PolicyObject $Context }
|
|
})
|
|
}
|
|
|
|
function Invoke-TranslateProfilePolicyObject {
|
|
param($PolicyObject, [DocumentationContext]$Context)
|
|
|
|
$obj = $PolicyObject.JsonObject
|
|
|
|
$objInfo = Get-PolicyObjectCategoryInfo $obj.'@odata.type'
|
|
if (-not $objInfo) { return }
|
|
|
|
# Header rows
|
|
Add-BasicDefaultValues $PolicyObject
|
|
Add-BasicAdditionalValues $PolicyObject
|
|
# Pin '@ObjectFromFile' so the walker's source-unavailable branches (linked
|
|
# certificates etc.) treat the input as a file-based object even when
|
|
# SourceTenantUnavailable isn't set.
|
|
if (-not $obj.PSObject.Properties['@ObjectFromFile']) {
|
|
$obj | Add-Member -MemberType NoteProperty -Name '@ObjectFromFile' -Value $true -Force
|
|
}
|
|
$Context.CurrentObject = $obj
|
|
Initialize-DocumentationObjectInfoObject $obj
|
|
|
|
# Resolve the list of ObjectInfo JSON files to walk for this PolicyType
|
|
$objectInfoDir = Join-Path $script:AppRootFolder 'Config\ObjectInfo'
|
|
$allFiles = @()
|
|
|
|
if ($objInfo.Categories -and $objInfo.Categories.Count -gt 0) {
|
|
foreach ($cat in $objInfo.Categories) {
|
|
$path = Join-Path $objectInfoDir "$($cat.ToLower())_$($objInfo.PolicyType.ToLower()).json"
|
|
if (Test-Path -LiteralPath $path) {
|
|
$allFiles += [IO.FileInfo]$path
|
|
}
|
|
else {
|
|
Write-Log "ObjectInfo file '$path' not found for $($objInfo.PolicyType)" 2
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
# Single-file path — find any *_<policyType>.json
|
|
$pattern = "*_$($objInfo.PolicyType.ToLower()).json"
|
|
if (Test-Path $objectInfoDir) {
|
|
$files = Get-ChildItem -Path $objectInfoDir -Filter $pattern -ErrorAction SilentlyContinue
|
|
if (-not $files) {
|
|
Write-Log "No ObjectInfo files matching '$pattern' for $($objInfo.PolicyType)" 2
|
|
}
|
|
foreach ($f in $files) { $allFiles += $f }
|
|
}
|
|
}
|
|
|
|
foreach ($fi in $allFiles) {
|
|
try {
|
|
$categoryObj = [IO.File]::ReadAllText($fi.FullName) | ConvertFrom-Json
|
|
$Context.CurrentSubCategory = ''
|
|
# Per-file custom handlers override the generic walker (old code:
|
|
# Invoke-CDDocumentTranslateSectionFile, called via docProvider.
|
|
# TranslateSectionFile hook from Invoke-TranslateProfileObject).
|
|
# Returns $true if the custom handler emitted rows; $false to fall
|
|
# through to Invoke-TranslateSection.
|
|
if (Invoke-DocCustomSectionFileTranslator -Obj $obj -FileInfo $fi -CategoryObj $categoryObj -ObjInfo $objInfo) {
|
|
continue
|
|
}
|
|
# Each ObjectInfo file wraps its section array under a key matching the file's basename
|
|
$sections = $categoryObj."$($fi.BaseName)"
|
|
if ($sections) {
|
|
Invoke-TranslateSection $obj $sections $objInfo
|
|
}
|
|
}
|
|
catch {
|
|
Write-LogError "Failed to translate ObjectInfo file $($fi.Name)" $_.Exception
|
|
}
|
|
}
|
|
}
|
|
|
|
# Custom per-(odata.type, fileBaseName) section-file translators. Mirrors old
|
|
# Extensions/DocumentationCustom.psm1's Invoke-CDDocumentTranslateSectionFile.
|
|
# Each block returns $true if it emitted rows and the generic walker should be
|
|
# skipped for this file, $false to fall through.
|
|
function Invoke-DocCustomSectionFileTranslator {
|
|
param($Obj, [IO.FileInfo]$FileInfo, $CategoryObj, $ObjInfo)
|
|
|
|
# --- Compliance: Custom Compliance category (Windows 10) ----------------
|
|
# Generic walker can't emit useful rows for `customcompliance_compliancewindows10`
|
|
# because the manifest's dataType=25 ("home screen", unused) is the child
|
|
# carrying the actual content, and the rules live on a separate
|
|
# $obj.deviceCompliancePolicyScript navigation property rather than on the
|
|
# boolean entityKey the parent points to. Three rows are emitted by hand:
|
|
# - Custom compliance (Require / Not configured)
|
|
# - Select your discovery script (resolved displayName)
|
|
# - Upload and validate the JSON file (base64-decoded rulesContent)
|
|
if ($Obj.'@odata.type' -eq '#microsoft.graph.windows10CompliancePolicy' -and
|
|
$FileInfo.BaseName -eq 'customcompliance_compliancewindows10') {
|
|
|
|
$category = Get-PolicyObjectCategoryString ($CategoryObj."$($FileInfo.BaseName)".category)
|
|
|
|
if ($null -eq $Obj.deviceCompliancePolicyScript) {
|
|
$propValue = Get-LanguageString 'BooleanActions.notConfigured'
|
|
$rawValue = 'notConfigured'
|
|
} else {
|
|
$propValue = Get-LanguageString 'BooleanActions.require'
|
|
$rawValue = 'require'
|
|
}
|
|
Add-CustomSettingObject ([PSCustomObject]@{
|
|
Name = Get-LanguageString 'SettingDetails.adminConfiguredComplianceSettingName'
|
|
Value = $propValue
|
|
EntityKey = 'deviceCompliancePolicyScript'
|
|
RawValue = $rawValue
|
|
Category = $category
|
|
SubCategory = $null
|
|
})
|
|
|
|
if ($Obj.deviceCompliancePolicyScript) {
|
|
# Resolve script displayName via shared cache. Offline runs / cache
|
|
# miss fall back to the script id so the row isn't blank.
|
|
$scriptId = [string]$Obj.deviceCompliancePolicyScript.deviceComplianceScriptId
|
|
$scriptName = $scriptId
|
|
if (-not [string]::IsNullOrEmpty($scriptId)) {
|
|
$cache = Get-CacheObject 'DocAllCustomCompliancePolicies'
|
|
# Custom compliance scripts are authored in the source tenant (not
|
|
# generic schema), so this is gated on source-tenant availability.
|
|
if (-not $cache -and -not (Get-CurrentDocumentationContext).SourceTenantUnavailable -and (Test-DocumentationGraphAvailable)) {
|
|
try {
|
|
$cache = @((Invoke-MSGraphAPI -Url "/deviceManagement/deviceComplianceScripts?`$select=displayName,id" -ODataMetadata 'minimal').value)
|
|
Set-CacheObject 'DocAllCustomCompliancePolicies' $cache
|
|
} catch {
|
|
Write-Log "Failed to fetch deviceComplianceScripts for resolution: $($_.Exception.Message)" 2
|
|
}
|
|
}
|
|
if ($cache) {
|
|
$match = $cache | Where-Object Id -EQ $scriptId | Select-Object -First 1
|
|
if ($match.displayName) { $scriptName = $match.displayName }
|
|
}
|
|
}
|
|
Add-CustomSettingObject ([PSCustomObject]@{
|
|
Name = Get-LanguageString 'CustomCompliance.FilePicker.scriptFileLabel'
|
|
Value = $scriptName
|
|
EntityKey = 'deviceComplianceScriptName'
|
|
Category = $category
|
|
SubCategory = $null
|
|
})
|
|
|
|
if ($Obj.deviceCompliancePolicyScript.rulesContent) {
|
|
$rules = try {
|
|
[System.Text.Encoding]::UTF8.GetString(
|
|
[System.Convert]::FromBase64String($Obj.deviceCompliancePolicyScript.rulesContent))
|
|
} catch {
|
|
[string]$Obj.deviceCompliancePolicyScript.rulesContent
|
|
}
|
|
Add-CustomSettingObject ([PSCustomObject]@{
|
|
Name = Get-LanguageString 'CustomCompliance.UploadFile.jsonFileLabel'
|
|
Value = $rules
|
|
EntityKey = 'jsonFileContent'
|
|
Category = $category
|
|
SubCategory = $null
|
|
})
|
|
}
|
|
}
|
|
return $true
|
|
}
|
|
|
|
return $false
|
|
}
|
|
|
|
Invoke-InitializeProfileInput
|