feat(baseline): declarative Intune baseline deployer

- 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
This commit is contained in:
2026-04-14 14:59:29 +02:00
parent 87b7af25a7
commit c4b8f4aaf6
5 changed files with 881 additions and 0 deletions

View File

@@ -0,0 +1,148 @@
#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."