Public/Imports/Import-IntuneWinGetApp.ps1

function Import-IntuneWinGetApp {
    <#
    .SYNOPSIS
        Imports WinGet-backed Win32 apps from bundled catalog or preset templates.
    .DESCRIPTION
        Creates Intune Win32 apps that wrap WinGet install and uninstall commands,
        publishes the generated .intunewin content, and tags created apps with both
        the hydration marker and WinGet ownership metadata so future deletes are safe.
    .PARAMETER TemplateId
        One or more specific bundled WinGet template IDs to import.
    .PARAMETER PresetId
        Optional preset ID to resolve from Templates/MobileApps/Windows/WinGet/Presets.
    .PARAMETER RemoveExisting
        Deletes matching WinGet hydration-owned Win32 apps instead of creating them.
    .PARAMETER WorkingDirectory
        Directory used to stage wrapper scripts, packages, and upload artifacts.
    .PARAMETER RemediationEnabled
        When enabled, creates or updates proactive remediation scripts for the selected
        WinGet app set. A system-scoped script is generated for machine apps and a
        user-scoped script is generated for user apps. No assignments are created.
    .EXAMPLE
        Import-IntuneWinGetApp -PresetId 'starter-pack'
    .EXAMPLE
        Import-IntuneWinGetApp -TemplateId 'google-chrome'
    .EXAMPLE
        Import-IntuneWinGetApp -RemoveExisting -PresetId 'starter-pack'
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject[]])]
    param(
        [Parameter()]
        [string[]]$TemplateId,

        [Parameter()]
        [string]$PresetId,

        [Parameter()]
        [switch]$RemoveExisting,

        [Parameter()]
        [string]$WorkingDirectory,

        [Parameter()]
        [switch]$RemediationEnabled
    )

    if (-not $WorkingDirectory) {
        $WorkingDirectory = Join-Path -Path (Get-Location).Path -ChildPath '.intunehydrationkit/winget-apps'
    }

    if (-not (Test-Path -Path $WorkingDirectory)) {
        $null = New-Item -Path $WorkingDirectory -ItemType Directory -Force -ErrorAction Stop
    }

    Write-Debug "Starting WinGet Win32 import. RemoveExisting=$($RemoveExisting.IsPresent), WorkingDirectory='$WorkingDirectory', PresetId='$PresetId', TemplateIdCount=$(@($TemplateId).Count)."

    $templates = @(Get-HydrationWinGetAppTemplates -TemplateId $TemplateId -PresetId $PresetId)
    if ($templates.Count -eq 0) {
        Write-Verbose 'No WinGet app templates found to process'
        return @()
    }

    $templateIds = (@($templates.templateId)) -join "', '"
    Write-Debug "Loaded $($templates.Count) WinGet template(s): '$templateIds'."

    $knownTemplateNames = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
    foreach ($template in $templates) {
        if (-not [string]::IsNullOrWhiteSpace($template.displayName)) {
            $templateDisplayName = [string]$template.displayName
            foreach ($nameVariant in Get-HydrationMobileAppNameVariant -DisplayName $templateDisplayName) {
                [void]$knownTemplateNames.Add($nameVariant)
            }
        }
    }

    $existingApps = Get-ExistingWinGetApps -KnownTemplateNames $knownTemplateNames
    Write-Debug "Resolved $($existingApps.Items.Count) existing app(s) from Graph for WinGet ownership evaluation. LookupEntries=$($existingApps.Lookup.Count)."
    $results = @()

    $validTemplates = [System.Collections.Generic.List[psobject]]::new()
    foreach ($template in $templates) {
        if ([string]::IsNullOrWhiteSpace($template.packageIdentifier)) {
            $templateDisplayName = [string]$template.displayName
            $displayName = if ([string]::IsNullOrWhiteSpace($templateDisplayName)) {
                $template.templateId ?? 'UnknownTemplate'
            } else {
                Get-HydrationMobileAppDisplayName -DisplayName $templateDisplayName
            }
            Write-HydrationLog -Message " Failed: $displayName - Missing or blank packageIdentifier for template '$($template.templateId)'" -Level Warning
            $results += New-HydrationResult -Name $displayName -Path $template.TemplatePath -Type 'WinGetWin32App' -Action 'Failed' -Status "Missing or blank packageIdentifier for template '$($template.templateId)'"
            continue
        }
        $validTemplates.Add($template)
    }
    $templates = $validTemplates

    if ($RemoveExisting) {
        $results += Remove-IntuneWinGetApps -ExistingApps $existingApps -KnownTemplateNames $knownTemplateNames -PSCmdlet $PSCmdlet -WhatIfPreference:$WhatIfPreference

        if ($RemediationEnabled.IsPresent) {
            $results += Sync-IntuneWinGetProactiveRemediation -Templates $templates -RemoveExisting -WhatIfEnabled:$WhatIfPreference
        }
        return $results
    }

    foreach ($template in $templates) {
        $templateDisplayName = [string]$template.displayName
        if ([string]::IsNullOrWhiteSpace($templateDisplayName)) {
            $results += New-HydrationResult -Name ($template.templateId ?? 'UnknownTemplate') -Path $template.TemplatePath -Type 'WinGetWin32App' -Action 'Failed' -Status 'Missing displayName'
            continue
        }

        $displayName = Get-HydrationMobileAppDisplayName -DisplayName $templateDisplayName
        $matchedName = Get-HydrationMobileAppExistingMatch -ExistingApps $existingApps.Lookup -DisplayName $templateDisplayName -OwnershipProperty IsOwned
        if ($matchedName) {
            $matchedApp = $existingApps.Lookup[$matchedName]
            Write-Debug "Create decision: skipping '$displayName' because matching WinGet-owned app '$($matchedApp.DisplayName)' already exists with AppId='$($matchedApp.Id)'."
            Write-HydrationLog -Message " Skipped: $displayName" -Level Info
            $results += New-HydrationResult -Name $displayName -Id $matchedApp.Id -Path $template.TemplatePath -Type 'WinGetWin32App' -Action 'Skipped' -Status 'Already exists'
            continue
        }

        if (-not $PSCmdlet.ShouldProcess($displayName, 'Create WinGet Win32 app')) {
            if ($WhatIfPreference) {
                Write-Debug "Create decision: WhatIf prevented creation for '$displayName'."
                $results += Add-HydrationDryRunResult -Action 'WouldCreate' -Name $displayName -Path $template.TemplatePath -Type 'WinGetWin32App'
            }

            continue
        }

        $importParams = @{
            Template         = $template
            DisplayName      = $displayName
            WorkingDirectory = $WorkingDirectory
        }
        $results += Invoke-IntuneWinGetAppTemplate @importParams
    }

    if ($RemediationEnabled.IsPresent) {
        $results += Sync-IntuneWinGetProactiveRemediation -Templates $templates -WhatIfEnabled:$WhatIfPreference
    }

    return $results
}