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:
148
Scripts/ConvertTo-IntuneBaseline.ps1
Normal file
148
Scripts/ConvertTo-IntuneBaseline.ps1
Normal 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."
|
||||
Reference in New Issue
Block a user