Public/Import-NLBaselineToIntune.ps1

<#
.SYNOPSIS
    Imports baseline configurations into Intune as Device Configuration policies.
.DESCRIPTION
    Reads baseline JSON from the workspace, converts to OMA-URI settings, and creates
    windows10CustomConfiguration policies in Intune via Microsoft Graph.
.PARAMETER ComponentName
    Baseline component to import (e.g. MicrosoftDefender, BitLockerSettings). If omitted, imports all.
.PARAMETER DryRun
    Validate and convert only; do not create or update policies in Intune.
.EXAMPLE
    Import-NLBaselineToIntune -ComponentName MicrosoftDefender
#>

function Import-NLBaselineToIntune {
    [CmdletBinding()]
    param(
        [string]$ComponentName,
        [switch]$DryRun
    )

    $ErrorActionPreference = "Stop"
    $workspacePath = Get-WorkspacePath
    if (-not $workspacePath) {
        Write-Error "Workspace not configured. Run Initialize-NLBaseline first."
        return
    }

    $config = Get-Config -WorkspacePath $workspacePath
    if (-not $config -or [string]::IsNullOrEmpty($config.AppRegistration.ClientId) -or [string]::IsNullOrEmpty($config.AppRegistration.ClientSecret) -or [string]::IsNullOrEmpty($config.AppRegistration.TenantId)) {
        Write-Error "App Registration not configured in config.json (ClientId, ClientSecret, TenantId)."
        return
    }

    $baselinePath = Join-Path -Path $workspacePath -ChildPath "Baseline"
    if (-not (Test-Path -Path $baselinePath)) {
        Write-Error "Baseline folder not found: $baselinePath"
        return
    }

    $toProcess = @()
    if ($ComponentName) {
        $single = Join-Path -Path $baselinePath -ChildPath "$ComponentName.json"
        if (-not (Test-Path -Path $single)) {
            Write-Error "Baseline component not found: $ComponentName"
            return
        }
        $toProcess = @($single)
    }
    else {
        $toProcess = @(Get-ChildItem -Path $baselinePath -Filter "*.json" -File | Select-Object -ExpandProperty FullName)
    }

    if ($toProcess.Count -eq 0) {
        Write-Warning "No baseline files to import."
        return
    }

    if (-not $DryRun) {
        $connected = Connect-Intune -Config $config
        if (-not $connected) {
            Write-Error "Failed to connect to Microsoft Graph. Check App Registration and API permissions."
            return
        }
    }

    $created = [System.Collections.Generic.List[string]]::new()
    $failed = [System.Collections.Generic.List[string]]::new()
    $maxOma = 999

    foreach ($fp in $toProcess) {
        $baseName = [System.IO.Path]::GetFileNameWithoutExtension($fp)
        $displayName = "NLBaseline - $baseName"
        $raw = Get-Content -Path $fp -Raw
        $items = $raw | ConvertFrom-Json
        if (-not ($items -is [array])) {
            $items = @($items)
        }

        $omaAll = @(Convert-BaselineToOmaSettings -BaselineItems $items)
        if ($omaAll.Count -eq 0) {
            Write-Warning "No OMA settings produced for $baseName; skip."
            continue
        }

        $chunks = @()
        for ($i = 0; $i -lt $omaAll.Count; $i += $maxOma) {
            $end = [Math]::Min($i + $maxOma, $omaAll.Count)
            $chunk = @($omaAll[$i..($end - 1)])
            $chunks += , $chunk
        }

        for ($c = 0; $c -lt $chunks.Count; $c++) {
            $chunk = $chunks[$c]
            $name = $displayName
            if ($chunks.Count -gt 1) {
                $name = "$displayName (Part $($c + 1))"
            }
            $body = @{
                "@odata.type"  = "#microsoft.graph.windows10CustomConfiguration"
                displayName    = $name
                description   = "NLBaseline $baseName"
                omaSettings   = $chunk
            } | ConvertTo-Json -Depth 20

            if ($DryRun) {
                Write-Host "[DryRun] Would create policy: $name with $($chunk.Count) OMA settings." -ForegroundColor Cyan
                $created.Add($name) | Out-Null
                continue
            }

            try {
                $res = Invoke-IntuneGraphRequest -Method POST -Uri "https://graph.microsoft.com/v1.0/deviceManagement/deviceConfigurations" -Body $body
                $created.Add($name) | Out-Null
                Write-Host "Created: $name (id: $($res.id))" -ForegroundColor Green
            }
            catch {
                $errMsg = $_.Exception.Message
                if ($_.Exception.Response) {
                    try {
                        $reader = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())
                        $responseBody = $reader.ReadToEnd()
                        $reader.Close()
                        if ($responseBody) {
                            $errJson = $responseBody | ConvertFrom-Json -ErrorAction SilentlyContinue
                            if ($errJson.error.message) {
                                $errMsg = $errJson.error.message
                            }
                        }
                    }
                    catch {
                        $null = 0  # Ignore response-body parse; use $errMsg
                    }
                }
                $failed.Add("$name : $errMsg") | Out-Null
                Write-Warning "Failed to create $name : $errMsg"
                Write-Verbose "Full error: $($_.Exception | Format-List -Force | Out-String)"
            }
        }
    }

    $exportsDir = Join-Path -Path $workspacePath -ChildPath "Intune\Exports"
    if (-not (Test-Path -Path $exportsDir)) {
        New-Item -ItemType Directory -Path $exportsDir -Force | Out-Null
    }
    $metaPath = Join-Path -Path $exportsDir -ChildPath "import_$(Get-Date -Format 'yyyyMMdd_HHmmss').json"
    $meta = @{
        Timestamp   = (Get-Date -Format "o")
        DryRun      = $DryRun.IsPresent
        Created     = @($created)
        Failed      = @($failed)
        Components  = @($toProcess | ForEach-Object { [System.IO.Path]::GetFileNameWithoutExtension($_) })
    }
    $meta | ConvertTo-Json -Depth 5 | Set-Content -Path $metaPath -Force
    Write-Host "Import metadata saved: $metaPath" -ForegroundColor Gray

    if ($failed.Count -gt 0) {
        Write-Warning "Some policies failed: $($failed.Count)"
    }
    Write-Host "Import complete. Created: $($created.Count), Failed: $($failed.Count)" -ForegroundColor Cyan
}