Private/WinGet/Invoke-IntuneWinGetAppTemplate.ps1

function Invoke-IntuneWinGetAppTemplate {
    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [psobject]$Template,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$DisplayName,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$WorkingDirectory
    )

    if ([string]::IsNullOrWhiteSpace($Template.packageIdentifier)) {
        $errorRecord = [System.Management.Automation.ErrorRecord]::new(
            [System.ArgumentException]::new("WinGet template '$($Template.templateId)' has a missing or blank packageIdentifier."),
            'WinGetTemplateMissingPackageIdentifier',
            [System.Management.Automation.ErrorCategory]::InvalidArgument,
            $Template
        )
        $PSCmdlet.ThrowTerminatingError($errorRecord)
    }

    $stagingRoot = Join-Path -Path $WorkingDirectory -ChildPath ('{0}-{1}' -f $Template.templateId, [guid]::NewGuid().ToString('N'))
    $contentRoot = Join-Path -Path $stagingRoot -ChildPath 'content'
    $uploadRoot = Join-Path -Path $stagingRoot -ChildPath 'upload'
    $createdAppId = $null
    $currentStep = 'Resolve package metadata'
    Write-Debug "Processing WinGet template '$($Template.templateId)' for package '$($Template.packageIdentifier)'. TemplatePath='$($Template.TemplatePath)', DisplayName='$DisplayName', StagingRoot='$stagingRoot', ContentRoot='$contentRoot', UploadRoot='$uploadRoot'."

    try {
        $packageMetadata = New-WinGetPackageMetadataFromTemplate -Template $Template
        Write-Debug "Resolved WinGet package metadata for template '$($Template.templateId)': PackageIdentifier='$($packageMetadata.PackageIdentifier)', PackageVersion='$($packageMetadata.PackageVersion)', ManifestPath='$($packageMetadata.ManifestPath)', SelectedInstaller=$(Format-WinGetInstallerSummary -Installer $packageMetadata.SelectedInstaller)."

        $currentStep = 'Create wrapper files'
        $wrapperFiles = New-WinGetWrapperFiles -Path $contentRoot -Template $Template
        Write-Debug "Created WinGet wrapper files for template '$($Template.templateId)': InstallScriptPath='$($wrapperFiles.InstallScriptPath)', UninstallScriptPath='$($wrapperFiles.UninstallScriptPath)'."

        $packagePath = Join-Path -Path $stagingRoot -ChildPath ('{0}.intunewin' -f $Template.templateId)
        $currentStep = 'Create packaging context'
        $installCommandLine = 'powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File .\Install-WinGetPackage.ps1'
        $uninstallCommandLine = 'powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File .\Uninstall-WinGetPackage.ps1'
        $packageContext = New-IntuneWinPackagingContext -PackageMetadata $packageMetadata -SourcePath $contentRoot -SetupFile ([System.IO.Path]::GetFileName($wrapperFiles.InstallScriptPath)) -OutputPath $packagePath -InstallCommandLine $installCommandLine -UninstallCommandLine $uninstallCommandLine
        Write-Debug "Created WinGet packaging context for template '$($Template.templateId)' with OutputPath='$packagePath'."

        $currentStep = 'Create .intunewin package'
        $packagedContent = New-IntuneWinPackage -PackagingContext $packageContext
        Write-Debug "Created .intunewin package for template '$($Template.templateId)' at '$($packagedContent.OutputPath)' (SetupFile='$($packagedContent.SetupFile)', UnencryptedSize=$($packagedContent.UnencryptedSize))."

        $iconAsset = $null
        $currentStep = 'Resolve app icon'
        try {
            $iconAsset = Resolve-WinGetAppIcon -Template $Template
            if ($iconAsset) {
                Write-Debug "Resolved icon for '$DisplayName' from '$($iconAsset.Source)' with MimeType='$($iconAsset.MimeType)'."
            }
        } catch {
            Write-Debug "Icon resolution failed for '$DisplayName'. Proceeding without icon. Error='$($_.Exception.Message)'."
            Write-HydrationLog -Message " Warning: Icon resolution failed for $DisplayName - $($_.Exception.Message)" -Level Warning
        }

        $configuration = @{
            PackageInformation    = @{
                SetupFile = 'Install-WinGetPackage.ps1'
            }
            Information           = @{
                DisplayName     = $DisplayName
                Description     = New-HydrationDescription -ExistingText ([string]$Template.description)
                Publisher       = if ($packageMetadata.Publisher) { $packageMetadata.Publisher } else { [string]$Template.publisher }
                Developer       = [string]$Template.publisher
                Owner           = [string]$Template.metadata.placeholders.owner
                Notes           = New-WinGetOwnershipNotes -Template $Template -PackageMetadata $packageMetadata
                AppVersion      = [string]$packageMetadata.PackageVersion
                RoleScopeTagIds = @($Template.metadata.placeholders.scopeTagIds)
            }
            Program               = @{
                InstallCommand          = $installCommandLine
                UninstallCommand        = $uninstallCommandLine
                InstallExperience       = [string]$Template.install.experience
                DeviceRestartBehavior   = [string]$Template.install.restartBehavior
                AllowAvailableUninstall = ($Template.uninstall.behavior -eq 'allow')
            }
            RequirementRule       = @{
                MinimumSupportedWindowsRelease = [string]$Template.requirements.minimumSupportedWindowsRelease
                Architecture                   = Get-RequirementArchitecture -Architecture $Template.requirements.architectures
                MinimumFreeDiskSpaceInMB       = $Template.requirements.minimumFreeDiskSpaceInMB
                MinimumMemoryInMB              = $Template.requirements.minimumMemoryInMB
            }
            DetectionRule         = @(Get-WinGetDetectionRules -Template $Template -PackageMetadata $packageMetadata)
            CustomRequirementRule = @()
        }

        $currentStep = 'Create Win32 app payload'
        $payload = New-IntuneWin32AppPayload -Configuration $configuration -PackageFileName ([System.IO.Path]::GetFileName($packagePath)) -SetupFilePath 'Install-WinGetPackage.ps1' -ScriptRootPath $contentRoot -IconBase64 $iconAsset.Base64 -IconMimeType $iconAsset.MimeType
        Write-Debug "Created Win32 app payload for '$DisplayName' with AppVersion='$($payload.appVersion)', Rules=$(@($payload.rules).Count), ScopeTags=$(@($payload.roleScopeTagIds).Count)."

        $currentStep = 'Create Graph mobile app'
        $createdApp = Invoke-HydrationGraphRequest -Method POST -Uri 'beta/deviceAppManagement/mobileApps' -Body $payload
        $createdAppId = $createdApp.id
        Write-Debug "Created WinGet Win32 app '$DisplayName' in Graph with AppId='$createdAppId'."

        $currentStep = 'Publish Win32 app content'
        $publishResult = Publish-IntuneWin32AppContent -AppId $createdAppId -IntuneWinPath $packagePath -WorkingDirectory $uploadRoot
        Write-Debug "Published Win32 app content for '$DisplayName'. ContentVersionId='$($publishResult.ContentVersionId)', ContentFileId='$($publishResult.ContentFileId)', UploadedChunkCount=$($publishResult.UploadedChunkCount), PackagePath='$($publishResult.PackagePath)'."
        Write-HydrationLog -Message " Created: $DisplayName" -Level Info
        return New-HydrationResult -Name $DisplayName -Id $createdAppId -Path $Template.TemplatePath -Type 'WinGetWin32App' -Action 'Created' -Status ([string]$packageMetadata.PackageVersion)
    } catch {
        Write-Debug "WinGet import failed for '$DisplayName' during step '$currentStep'. Error='$($_.Exception.Message)'."
        $errorMessage = Get-GraphErrorMessage -ErrorRecord $_
        if ($createdAppId) {
            try {
                Invoke-HydrationGraphRequest -Method DELETE -Uri "beta/deviceAppManagement/mobileApps/$createdAppId" | Out-Null
                Write-Debug "Deleted partially created WinGet Win32 app '$DisplayName' with AppId='$createdAppId' during cleanup."
                Write-HydrationLog -Message " CleanupDeleted: $DisplayName" -Level Warning
            } catch {
                Write-Debug "Failed to delete partially created WinGet Win32 app '$DisplayName' with AppId='$createdAppId' during cleanup. Error='$($_.Exception.Message)'."
                Write-HydrationLog -Message " CleanupFailed: $DisplayName - $($_.Exception.Message)" -Level Warning
            }
        }

        Write-HydrationLog -Message " Failed: $DisplayName - $errorMessage" -Level Warning
        return New-HydrationResult -Name $DisplayName -Path $Template.TemplatePath -Type 'WinGetWin32App' -Action 'Failed' -Status $errorMessage
    } finally {
        if (Test-Path -Path $stagingRoot) {
            Write-Debug "Removing WinGet staging directory '$stagingRoot' for template '$($Template.templateId)'."
            try {
                Remove-Item -Path $stagingRoot -Recurse -Force -ErrorAction Stop
            } catch {
                Write-Debug "Failed to remove WinGet staging directory '$stagingRoot'. Error='$($_.Exception.Message)'."
            }
        }
    }
}