Public/DeploymentOrchestration.ps1
|
# DeploymentOrchestration.ps1 # Main orchestration function for deploying Winget apps to Intune function Deploy-WinGetApp { <# .SYNOPSIS Deploys a Winget application to Intune as a Win32 app with proactive remediation. .DESCRIPTION Orchestrates the full deployment workflow: creates directory, groups, scripts, proactive remediation, IntuneWin package, uploads to Intune, and assigns groups. #> [cmdletbinding()] param ( [Parameter(Mandatory = $true)] [string]$AppId, [Parameter(Mandatory = $true)] [string]$AppName, [Parameter(Mandatory = $true)] [string]$BasePath, [string]$InstallGroupName, [string]$UninstallGroupName, [ValidateSet("Device", "User", "Both", "None")] [string]$AvailableInstall = "None", [switch]$Force, [bool]$Remediation = $true ) Write-Host "========== Deploying $AppName ($AppId) ==========" -ForegroundColor Cyan Write-Verbose "Starting deployment for $AppName ($AppId)" # Check if app already exists $existingApp = Test-ExistingIntuneApp -AppName $AppName if ($existingApp.Exists) { Write-Host "WARNING: App '$AppName' already exists in Intune:" -ForegroundColor Yellow foreach ($app in $existingApp.Apps) { Write-Host " - $($app.displayName) [ID: $($app.id)]" -ForegroundColor Yellow } if (-not $Force) { Write-Host "Skipping deployment. Use -Force parameter to override." -ForegroundColor Yellow Write-Host "========== Skipped $AppName ==========" -ForegroundColor Yellow return } # -Force: remove existing app(s) and remediation before re-creating Write-Host "Force parameter detected. Removing existing resources before redeployment..." -ForegroundColor Green foreach ($app in $existingApp.Apps) { try { Invoke-MgGraphRequest -Uri "beta/deviceAppManagement/mobileApps/$($app.id)" -Method DELETE -ErrorAction Stop | Out-Null Write-Host " Removed existing app: $($app.displayName) [$($app.id)]" -ForegroundColor Yellow } catch { Write-Warning " Failed to remove app $($app.id): $_" } } # Remove existing proactive remediation to avoid duplicates $remediationName = "$AppName Proactive Update" $escapedRemName = $remediationName.Replace("'", "''") $remFilter = [uri]::EscapeDataString("displayName eq '$escapedRemName'") try { $existingRems = (Invoke-MgGraphRequest -Uri "beta/deviceManagement/deviceHealthScripts?`$filter=$remFilter" -Method GET -ErrorAction Stop).value foreach ($rem in $existingRems) { Invoke-MgGraphRequest -Uri "beta/deviceManagement/deviceHealthScripts/$($rem.id)" -Method DELETE -ErrorAction Stop | Out-Null Write-Host " Removed existing remediation: $($rem.displayName)" -ForegroundColor Yellow } } catch { Write-Verbose "Could not check/remove existing remediations: $_" } } # 1. Create app directory $appPath = Join-Path $BasePath $AppId New-Item -Path $appPath -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null Write-Verbose "Created app directory: $appPath" # 2. Create/get groups $installGroupId = Get-OrCreateAADGroup -AppId $AppId -AppName $AppName -GroupType "Install" -GroupName $InstallGroupName $uninstallGroupId = Get-OrCreateAADGroup -AppId $AppId -AppName $AppName -GroupType "Uninstall" -GroupName $UninstallGroupName # 3. Create scripts # Sanitize AppId for filenames - remove special characters that cause issues $safeAppId = $AppId -replace '[^a-zA-Z0-9._-]', '_' $installFilename = "install$safeAppId.ps1" $installScriptFile = Join-Path $appPath $installFilename New-WinGetScript -AppId $AppId -AppName $AppName -ScriptType "Install" | Out-File $installScriptFile -Encoding utf8 Write-Verbose "Created: $installScriptFile" $uninstallFilename = "uninstall$safeAppId.ps1" $uninstallScriptFile = Join-Path $appPath $uninstallFilename New-WinGetScript -AppId $AppId -AppName $AppName -ScriptType "Uninstall" | Out-File $uninstallScriptFile -Encoding utf8 Write-Verbose "Created: $uninstallScriptFile" $detectionFilename = "detection$safeAppId.ps1" $detectionScriptFile = Join-Path $appPath $detectionFilename New-WinGetScript -AppId $AppId -AppName $AppName -ScriptType "DetectionRemediation" | Out-File $detectionScriptFile -Encoding utf8 Write-Verbose "Created: $detectionScriptFile" # 4. Create proactive remediation (if enabled and licensed) if ($Remediation -and (Test-ProactiveRemediationLicense)) { New-ProactiveRemediation -AppId $AppId -AppName $AppName -GroupId $installGroupId | Out-Null } elseif (-not $Remediation) { Write-Host "Skipping Proactive Remediation creation - disabled for this app" -ForegroundColor Yellow } else { Write-Host "Skipping Proactive Remediation creation - not licensed" -ForegroundColor Yellow } # 5. Create IntuneWin package # The IntuneWin file will be created without the .ps1 extension in the name $intunewinFilename = $installFilename -replace '\.ps1$', '' $intunewinPath = Join-Path $BasePath "$intunewinFilename.intunewin" New-IntuneWinFile -appid $AppId -appname $AppName -apppath $appPath -setupfilename $installFilename -destpath $BasePath *>&1 | Out-Null Write-Verbose "Created: $intunewinPath" # Brief pause for file system Start-Sleep -Seconds 2 # 5.5. Search for app icon $appIcon = Get-AppIcon -AppId $AppId -AppName $AppName # 6. Upload Win32 app $installCmd = "powershell.exe -ExecutionPolicy Bypass -File $installFilename" $uninstallCmd = "powershell.exe -ExecutionPolicy Bypass -File $uninstallFilename" try { $appUploadResult = New-Win32App -appid $AppId -appname $AppName -appfile $intunewinPath -installcmd $installCmd -uninstallcmd $uninstallCmd -detectionfile $detectionScriptFile -largeIcon $appIcon if (-not $appUploadResult) { throw "Upload returned null - no app was created in Intune" } Write-Host "Uploaded $AppName to Intune successfully" -ForegroundColor Green # 7. Assign groups Grant-Win32AppAssignment -AppName $AppName -InstallGroupId $installGroupId -UninstallGroupId $uninstallGroupId -AvailableInstall $AvailableInstall Write-Host "Assigned groups to $AppName" } catch { Write-Error "Error uploading $AppName to Intune: $_" throw } Write-Host "========== Completed $AppName ==========" -ForegroundColor Green Write-Verbose "Completed deployment for $AppName" } |