- Add Deploy-IntuneBaseline.ps1 for YAML-driven policy + assignment deployment - Add ConvertTo-IntuneBaseline.ps1 to convert export folders to baseline manifests - Add example OpenIntuneBaseline YAML in Baselines/ - Supports mutations, group auto-creation, idempotency, and WhatIf mode
149 lines
5.0 KiB
PowerShell
149 lines
5.0 KiB
PowerShell
#requires -Version 7.0
|
|
<#
|
|
.SYNOPSIS
|
|
Converts an existing IntuneManagement export folder into a baseline YAML manifest.
|
|
.DESCRIPTION
|
|
Scans a toolkit export directory, infers policy types from folder names,
|
|
extracts display names from JSON files, and emits a baseline YAML skeleton
|
|
with empty assignment blocks ready for editing.
|
|
.EXAMPLE
|
|
./Scripts/ConvertTo-IntuneBaseline.ps1 -ExportPath ./Exports/2025-01-15 -OutputPath ./Baselines/mybaseline.yaml
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ExportPath,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$OutputPath,
|
|
|
|
[string]$BaselineName = "ConvertedBaseline"
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
#region Dependency check
|
|
$yamlModule = Get-Module -ListAvailable -Name powershell-yaml | Select-Object -First 1
|
|
if(-not $yamlModule)
|
|
{
|
|
Write-Warning "powershell-yaml module not found. Installing..."
|
|
Install-Module powershell-yaml -Scope CurrentUser -Force
|
|
}
|
|
Import-Module powershell-yaml -Force
|
|
#endregion
|
|
|
|
$exportPathResolved = Resolve-Path $ExportPath | Select-Object -ExpandProperty Path
|
|
$outputPathResolved = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputPath)
|
|
|
|
if(-not (Test-Path $exportPathResolved))
|
|
{
|
|
throw "Export path not found: $ExportPath"
|
|
}
|
|
|
|
# Folder-to-type whitelist (matches toolkit export folders and baseline types)
|
|
$folderTypeMap = @{
|
|
"DeviceConfiguration" = "DeviceConfiguration"
|
|
"SettingsCatalog" = "SettingsCatalog"
|
|
"CompliancePolicies" = "CompliancePolicies"
|
|
"CompliancePoliciesV2" = "CompliancePoliciesV2"
|
|
"AdministrativeTemplates" = "AdministrativeTemplates"
|
|
"EndpointSecurity" = "EndpointSecurity"
|
|
"DeviceManagementIntents" = "DeviceManagementIntents"
|
|
"AppProtection" = "AppProtection"
|
|
"AppConfigurationManagedDevice" = "AppConfigurationManagedDevice"
|
|
"PlatformScripts" = "PlatformScripts"
|
|
"MacScripts" = "MacScripts"
|
|
"DeviceHealthScripts" = "DeviceHealthScripts"
|
|
"MacCustomAttributes" = "MacCustomAttributes"
|
|
"EnrollmentRestrictions" = "EnrollmentRestrictions"
|
|
"EnrollmentStatusPage" = "EnrollmentStatusPage"
|
|
"Autopilot" = "Autopilot"
|
|
"TermsAndConditions" = "TermsAndConditions"
|
|
"PolicySets" = "PolicySets"
|
|
"UpdatePolicies" = "UpdatePolicies"
|
|
"FeatureUpdates" = "FeatureUpdates"
|
|
"QualityUpdates" = "QualityUpdates"
|
|
"Applications" = "Applications"
|
|
}
|
|
|
|
$policies = @()
|
|
|
|
foreach($folder in Get-ChildItem -Path $exportPathResolved -Directory)
|
|
{
|
|
$folderName = $folder.Name
|
|
if(-not $folderTypeMap.ContainsKey($folderName))
|
|
{
|
|
Write-Verbose "Skipping unrecognized folder: $folderName"
|
|
continue
|
|
}
|
|
$typeName = $folderTypeMap[$folderName]
|
|
$nameProp = if($typeName -eq "SettingsCatalog" -or $typeName -eq "CompliancePoliciesV2") { "name" } else { "displayName" }
|
|
|
|
$jsonFiles = Get-ChildItem -Path $folder.FullName -Filter "*.json"
|
|
foreach($file in $jsonFiles)
|
|
{
|
|
# Skip *_Settings.json companion files
|
|
if($file.BaseName -like "*_Settings")
|
|
{
|
|
continue
|
|
}
|
|
|
|
try
|
|
{
|
|
$json = Get-Content $file.FullName -Raw | ConvertFrom-Json -Depth 10
|
|
$displayName = $json.$nameProp
|
|
if(-not $displayName)
|
|
{
|
|
$displayName = $json.displayName
|
|
}
|
|
if(-not $displayName)
|
|
{
|
|
$displayName = $file.BaseName
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
Write-Warning "Could not parse $($file.FullName); using filename as display name."
|
|
$displayName = $file.BaseName
|
|
}
|
|
|
|
$relativePath = "." + $file.FullName.Substring($exportPathResolved.Length).Replace("\", "/")
|
|
|
|
$policies += [ordered]@{
|
|
sourcePath = $relativePath
|
|
type = $typeName
|
|
assignments = @()
|
|
}
|
|
|
|
Write-Host "Mapped: [$typeName] $displayName -> $relativePath"
|
|
}
|
|
}
|
|
|
|
if($policies.Count -eq 0)
|
|
{
|
|
throw "No convertible policies found in $exportPathResolved"
|
|
}
|
|
|
|
$baseline = [ordered]@{
|
|
baseline = [ordered]@{
|
|
name = $BaselineName
|
|
conflictResolution = "Skip"
|
|
whatIf = $false
|
|
tenantMutation = [ordered]@{
|
|
search = ""
|
|
replace = ""
|
|
}
|
|
groups = @()
|
|
policies = $policies
|
|
}
|
|
}
|
|
|
|
$yaml = ConvertTo-Yaml -Data $baseline
|
|
$yaml | Set-Content -Path $outputPathResolved -Encoding UTF8
|
|
|
|
Write-Host "`nBaseline skeleton written to: $outputPathResolved" -ForegroundColor Green
|
|
Write-Host "Policies found: $($policies.Count)" -ForegroundColor Green
|
|
Write-Host "Next steps:" -ForegroundColor Cyan
|
|
Write-Host " 1. Edit the YAML to add group names and assignments."
|
|
Write-Host " 2. Run Deploy-IntuneBaseline.ps1 against a target tenant."
|