Optionnel : Automatisation : SetupComplete.cmd + InstallApps.ps1
Au premier démarrage, un script interactif s'affiche proposant de choisir les packs de logiciels à installer. Chaque pack s'installe silencieusement via winget.
Structure des fichiers
Deux fichiers sont nécessaires :
SetupComplete.cmd: lanceur batch (appelé par autounattend.xml)InstallApps.ps1: script PowerShell interactif avec les packs
SetupComplete.cmd
@echo off
setlocal enabledelayedexpansion
REM ============================================================
REM SetupComplete.cmd —- Lanceur du script d'installation
REM Emplacement : C:\Windows\Setup\Scripts\SetupComplete.cmd
REM ============================================================
echo.
echo ============================================
echo Preparation de l'installation des logiciels
echo ============================================
echo.
REM Attendre que le bureau soit bien charge
echo Attente du chargement du systeme (10 secondes)...
timeout /t 10 /nobreak >nul
REM Determiner le dossier du script (pour trouver InstallApps.ps1 a cote)
set "SCRIPT_DIR=%~dp0"
echo Dossier des scripts : %SCRIPT_DIR%
REM Verifier que InstallApps.ps1 existe
if not exist "%SCRIPT_DIR%InstallApps.ps1" (
echo.
echo ERREUR : InstallApps.ps1 introuvable dans %SCRIPT_DIR%
echo.
pause
exit /b 1
)
REM Ajouter le chemin WindowsApps au PATH (winget s'y trouve souvent)
set "PATH=%PATH%;%LOCALAPPDATA%\Microsoft\WindowsApps"
REM Verifier que winget est disponible (boucle max 2 minutes)
echo Verification de winget...
set RETRY=0
:WAIT_WINGET
winget --version >nul 2>&1
if %ERRORLEVEL% equ 0 goto WINGET_OK
set /a RETRY+=1
if %RETRY% geq 12 (
echo.
echo ERREUR : winget introuvable apres 2 minutes.
echo Installez-le via le Microsoft Store (App Installer).
echo Puis lancez manuellement :
echo powershell -ExecutionPolicy Bypass -File "%SCRIPT_DIR%InstallApps.ps1"
echo.
pause
exit /b 1
)
echo winget pas encore disponible, tentative !RETRY!/12 dans 10s...
timeout /t 10 /nobreak >nul
goto WAIT_WINGET
:WINGET_OK
echo winget detecte !
echo.
REM Lancer les tweaks premier demarrage (taches planifiees, Defender, etc.)
if exist "%SCRIPT_DIR%FirstBootTweaks.ps1" (
echo Lancement de FirstBootTweaks.ps1...
echo.
powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%SCRIPT_DIR%FirstBootTweaks.ps1"
echo.
)
REM Lancer le script PowerShell interactif
echo Lancement de InstallApps.ps1...
echo.
powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%SCRIPT_DIR%InstallApps.ps1"
REM Capturer le code retour de PowerShell
if %ERRORLEVEL% neq 0 (
echo.
echo ATTENTION : Le script PowerShell a retourne une erreur (code %ERRORLEVEL%).
echo.
)
REM Supprimer le raccourci du Startup pour ne pas relancer au prochain login
del "%ProgramData%\Microsoft\Windows\Start Menu\Programs\Startup\SetupComplete.cmd" >nul 2>&1
echo.
echo Script termine. Appuyez sur une touche pour fermer.
pause >nul
exit /b 0
InstallApps.ps1
<#
.SYNOPSIS
Installation interactive des logiciels au premier demarrage via winget
#>
# Forcer encodage UTF-8
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$Host.UI.RawUI.WindowTitle = "Installation des logiciels"
# ============================================================
# CONFIGURATION DES CATEGORIES ET APPLICATIONS
# ============================================================
$categories = [ordered]@{
"Drivers (manuels)" = @{
DefaultMode = "M"
Apps = @(
@{ Id = "MANUAL"; Name = "NVIDIA App"; Default = $false; Url = "https://www.nvidia.com/fr-fr/software/nvidia-app/" }
@{ Id = "MANUAL"; Name = "Intel DSA"; Default = $false; Url = "https://www.intel.fr/content/www/fr/fr/support/detect.html" }
@{ Id = "MANUAL"; Name = "AORUS X870E PRO X3D Drivers"; Default = $false; Url = "https://www.aorus.com/fr-fr/motherboards/X870E-AORUS-PRO-X3D/Support" }
@{ Id = "MANUAL"; Name = "Rufus"; Default = $false; Url = "https://rufus.ie/fr/" }
@{ Id = "MANUAL"; Name = "FileZilla"; Default = $false; Url = "https://filezilla-project.org/download.php?type=client" }
)
}
"Navigateurs" = @{
DefaultMode = "S"
Apps = @(
@{ Id = "Brave.Brave"; Name = "Brave"; Default = $false }
@{ Id = "Mozilla.Firefox"; Name = "Firefox"; Default = $true }
@{ Id = "eloston.ungoogled-chromium"; Name = "Chromium (ungoogled)"; Default = $false }
@{ Id = "TheBrowserCompany.Arc"; Name = "Arc"; Default = $false }
)
}
"Compression" = @{
DefaultMode = "S"
Apps = @(
@{ Id = "7zip.7zip"; Name = "7-Zip"; Default = $true }
)
}
"Jeux" = @{
DefaultMode = "I"
Apps = @(
@{ Id = "ElectronicArts.EADesktop"; Name = "EA App"; Default = $false }
@{ Id = "EpicGames.EpicGamesLauncher"; Name = "Epic Games"; Default = $false }
@{ Id = "Valve.Steam"; Name = "Steam"; Default = $false }
)
}
"Utilitaires" = @{
DefaultMode = "S"
Apps = @(
@{ Id = "CPUID.CPU-Z"; Name = "CPU-Z"; Default = $false }
@{ Id = "TechPowerUp.GPU-Z"; Name = "GPU-Z"; Default = $false }
@{ Id = "Wagnardsoft.DisplayDriverUninstaller"; Name = "DDU"; Default = $false }
@{ Id = "REALiX.HWiNFO"; Name = "HWiNFO"; Default = $false }
@{ Id = "DominikReichl.KeePass"; Name = "KeePass"; Default = $false }
@{ Id = "Microsoft.PowerToys"; Name = "PowerToys"; Default = $false }
@{ Id = "JAMSoftware.TreeSize.Free"; Name = "TreeSize Free"; Default = $false }
)
}
"ProTools IT" = @{
DefaultMode = "S"
Apps = @(
@{ Id = "WiresharkFoundation.Wireshark"; Name = "Wireshark"; Default = $false }
@{ Id = "WireGuard.WireGuard"; Name = "WireGuard"; Default = $false }
@{ Id = "PuTTY.PuTTY"; Name = "PuTTY"; Default = $false }
@{ Id = "WinSCP.WinSCP"; Name = "WinSCP"; Default = $false }
@{ Id = "Mobatek.MobaXterm"; Name = "MobaXterm"; Default = $false }
@{ Id = "Insecure.Nmap"; Name = "Nmap"; Default = $false }
@{ Id = "Bitwarden.Bitwarden"; Name = "Bitwarden"; Default = $false }
@{ Id = "angryziber.AngryIPScanner"; Name = "Angry IP Scanner"; Default = $false }
)
}
"Multimedia" = @{
DefaultMode = "I"
Apps = @(
@{ Id = "Spotify.Spotify"; Name = "Spotify"; Default = $false }
@{ Id = "darktable.darktable"; Name = "Darktable"; Default = $false }
@{ Id = "VideoLAN.VLC"; Name = "VLC"; Default = $true }
@{ Id = "Plex.Plex"; Name = "Plex"; Default = $false }
@{ Id = "OBSProject.OBSStudio"; Name = "OBS Studio"; Default = $false }
@{ Id = "HandBrake.HandBrake"; Name = "HandBrake"; Default = $false }
)
}
"Communication" = @{
DefaultMode = "S"
Apps = @(
@{ Id = "Discord.Discord"; Name = "Discord"; Default = $false }
@{ Id = "OpenWhisperSystems.Signal"; Name = "Signal"; Default = $false }
@{ Id = "SlackTechnologies.Slack"; Name = "Slack"; Default = $false }
@{ Id = "Telegram.TelegramDesktop"; Name = "Telegram"; Default = $false }
@{ Id = "Microsoft.Teams"; Name = "Teams"; Default = $false }
)
}
"Documents" = @{
DefaultMode = "S"
Apps = @(
@{ Id = "Adobe.Acrobat.Reader.64-bit"; Name = "Adobe Reader"; Default = $true }
@{ Id = "ToEverything.AFFiNE"; Name = "Affine"; Default = $false }
@{ Id = "Logseq.Logseq"; Name = "LogSeq"; Default = $false }
@{ Id = "TheDocumentFoundation.LibreOffice"; Name = "LibreOffice"; Default = $false }
)
}
"Developpement" = @{
DefaultMode = "I"
Apps = @(
@{ Id = "Docker.DockerDesktop"; Name = "Docker Desktop"; Default = $false }
@{ Id = "Notepad++.Notepad++"; Name = "Notepad++"; Default = $true }
@{ Id = "Microsoft.VisualStudioCode"; Name = "VS Code"; Default = $false }
@{ Id = "Microsoft.VisualStudio.2022.Community"; Name = "Visual Studio 2022"; Default = $false }
@{ Id = "Git.Git"; Name = "Git"; Default = $false }
@{ Id = "GitHub.GitHubDesktop"; Name = "GitHub Desktop"; Default = $false }
@{ Id = "GitHub.cli"; Name = "GitHub CLI"; Default = $false }
@{ Id = "Python.Python.3.12"; Name = "Python 3.12"; Default = $false }
)
}
}
# Option speciale WSL2 (feature Windows, pas winget)
$installWSL = $false
# ============================================================
# FONCTIONS D AFFICHAGE
# ============================================================
function Show-Header {
param([string]$Title)
Clear-Host
Write-Host ""
Write-Host " ============================================================" -ForegroundColor Cyan
Write-Host " INSTALLATION DES LOGICIELS" -ForegroundColor Cyan
Write-Host " ============================================================" -ForegroundColor Cyan
Write-Host ""
Write-Host " $Title" -ForegroundColor White
Write-Host ""
}
function Show-CategoryMenu {
param(
[string]$CategoryName,
[int]$CategoryIndex,
[int]$TotalCategories,
[array]$Apps,
[array]$Selected
)
Show-Header "Categorie $CategoryIndex/$TotalCategories : $CategoryName"
Write-Host " Utilisez les numeros pour cocher/decocher, puis Entree pour valider." -ForegroundColor Gray
Write-Host " [T] Tout cocher [N] Tout decocher [Entree] Valider et passer" -ForegroundColor Gray
Write-Host ""
for ($i = 0; $i -lt $Apps.Count; $i++) {
if ($Selected[$i]) {
$check = "[X]"
$color = "Green"
} else {
$check = "[ ]"
$color = "DarkGray"
}
Write-Host " $check " -ForegroundColor $color -NoNewline
Write-Host "$($i + 1). $($Apps[$i].Name)" -ForegroundColor White
}
# Cas special : categorie Developpement - proposer WSL2
if ($CategoryName -eq "Developpement") {
Write-Host ""
if ($script:installWSL) {
$wslCheck = "[X]"
$wslColor = "Green"
} else {
$wslCheck = "[ ]"
$wslColor = "DarkGray"
}
Write-Host " $wslCheck " -ForegroundColor $wslColor -NoNewline
Write-Host "W. WSL2 (feature Windows - necessite un redemarrage)" -ForegroundColor Yellow
}
Write-Host ""
}
# ============================================================
# PARCOURS SEQUENTIEL : CATEGORIE PAR CATEGORIE
# ============================================================
$allSelections = @{}
$categoryNames = @($categories.Keys)
$totalCat = $categoryNames.Count
for ($catIdx = 0; $catIdx -lt $totalCat; $catIdx++) {
$catName = $categoryNames[$catIdx]
$catConfig = $categories[$catName]
$apps = $catConfig.Apps
$defaultMode = $catConfig.DefaultMode
# Initialiser la selection avec les valeurs par defaut
$selected = @()
foreach ($app in $apps) {
$selected += $app.Default
}
# ---- Boucle : selection des apps ----
$done = $false
while (-not $done) {
Show-CategoryMenu -CategoryName $catName `
-CategoryIndex ($catIdx + 1) `
-TotalCategories $totalCat `
-Apps $apps `
-Selected $selected
$userInput = Read-Host " Choix"
switch ($userInput.ToUpper()) {
"T" {
for ($i = 0; $i -lt $selected.Count; $i++) { $selected[$i] = $true }
if ($catName -eq "Developpement") { $script:installWSL = $true }
}
"N" {
for ($i = 0; $i -lt $selected.Count; $i++) { $selected[$i] = $false }
if ($catName -eq "Developpement") { $script:installWSL = $false }
}
"W" {
if ($catName -eq "Developpement") {
$script:installWSL = -not $script:installWSL
}
}
"" {
$done = $true
}
default {
$nums = $userInput -split '[,\s]+' | Where-Object { $_ -match '^\d+$' }
foreach ($num in $nums) {
$idx = [int]$num - 1
if ($idx -ge 0 -and $idx -lt $selected.Count) {
$selected[$idx] = -not $selected[$idx]
}
}
}
}
}
# ---- Choix du mode (si au moins 1 app cochee et pas mode Manuel) ----
$hasSelection = ($selected | Where-Object { $_ -eq $true }).Count -gt 0
$chosenMode = $defaultMode
if ($hasSelection -and $defaultMode -ne "M") {
if ($defaultMode -eq "S") {
$modeLabel = "Silencieux"
} else {
$modeLabel = "Interactif"
}
Write-Host ""
Write-Host " Mode d installation pour ${catName} ?" -ForegroundColor White
Write-Host " [S] Silencieux - automatique, chemin par defaut sur C:\" -ForegroundColor Gray
Write-Host " [I] Interactif - installeur s ouvre, vous choisissez le chemin" -ForegroundColor Gray
Write-Host ""
$modeInput = Read-Host " Choix (par defaut: $modeLabel)"
if ($modeInput.ToUpper() -eq "I") {
$chosenMode = "I"
} elseif ($modeInput.ToUpper() -eq "S") {
$chosenMode = "S"
}
}
# Sauvegarder
$allSelections[$catName] = @{
Apps = $apps
Selected = $selected
Mode = $chosenMode
}
}
# ============================================================
# RECAPITULATIF AVANT INSTALLATION
# ============================================================
Show-Header "RECAPITULATIF - Verification avant installation"
$appsToInstall = @()
foreach ($catName in $categoryNames) {
$sel = $allSelections[$catName]
$selectedApps = @()
for ($i = 0; $i -lt $sel.Apps.Count; $i++) {
if ($sel.Selected[$i]) {
$selectedApps += $sel.Apps[$i]
$appEntry = @{
Id = $sel.Apps[$i].Id
Name = $sel.Apps[$i].Name
Mode = $sel.Mode
Cat = $catName
}
if ($sel.Apps[$i].Url) { $appEntry.Url = $sel.Apps[$i].Url }
$appsToInstall += $appEntry
}
}
if ($selectedApps.Count -gt 0) {
if ($sel.Mode -eq "M") {
$modeTag = " [MANUEL - liens de telechargement]"
$modeColor = "Magenta"
} elseif ($sel.Mode -eq "I") {
$modeTag = " [INTERACTIF]"
$modeColor = "Yellow"
} else {
$modeTag = " [SILENCIEUX]"
$modeColor = "Gray"
}
Write-Host " $catName" -ForegroundColor Cyan -NoNewline
Write-Host $modeTag -ForegroundColor $modeColor
foreach ($app in $selectedApps) {
if ($sel.Mode -eq "M") {
$url = ($sel.Apps | Where-Object { $_.Name -eq $app.Name }).Url
Write-Host " - $($app.Name) : $url" -ForegroundColor Magenta
} else {
Write-Host " - $($app.Name)" -ForegroundColor Green
}
}
} else {
Write-Host " $catName : (aucune)" -ForegroundColor DarkGray
}
}
if ($installWSL) {
Write-Host " WSL2 : Oui (necessite redemarrage)" -ForegroundColor Yellow
}
Write-Host ""
Write-Host " Total : $($appsToInstall.Count) application(s) a installer" -ForegroundColor White
if ($installWSL) {
Write-Host " + WSL2 (feature Windows)" -ForegroundColor Yellow
}
Write-Host ""
$confirm = Read-Host " Lancer installation ? (O/N)"
if ($confirm -ne "O" -and $confirm -ne "o") {
Write-Host ""
Write-Host " Installation annulee." -ForegroundColor Red
Write-Host " Vous pourrez relancer le script depuis :" -ForegroundColor Gray
Write-Host " C:\Windows\Setup\Scripts\InstallApps.ps1" -ForegroundColor Gray
Write-Host ""
Write-Host " Appuyez sur une touche..." -ForegroundColor DarkGray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
exit
}
# ============================================================
# INSTALLATION
# ============================================================
Show-Header "INSTALLATION EN COURS..."
# Verifier que winget est disponible
Write-Host " Verification de winget..." -ForegroundColor Gray
$wingetCheck = Get-Command winget -ErrorAction SilentlyContinue
if (-not $wingetCheck) {
Write-Host " winget non disponible, attente 30 secondes..." -ForegroundColor Yellow
Start-Sleep -Seconds 30
$wingetCheck = Get-Command winget -ErrorAction SilentlyContinue
if (-not $wingetCheck) {
Write-Host " ERREUR : winget introuvable. Installez-le via le Microsoft Store" -ForegroundColor Red
Write-Host " (App Installer / Programme d installation d application)" -ForegroundColor Red
Write-Host ""
Write-Host " Appuyez sur une touche..." -ForegroundColor DarkGray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
exit 1
}
}
Write-Host " winget OK" -ForegroundColor Green
Write-Host ""
$installed = 0
$failed = @()
$total = $appsToInstall.Count
$currentCat = ""
for ($i = 0; $i -lt $total; $i++) {
$app = $appsToInstall[$i]
$progress = "[$($i + 1)/$total]"
# Afficher le nom de la categorie quand on change
if ($app.Cat -ne $currentCat) {
$currentCat = $app.Cat
if ($app.Mode -eq "M") { $modeTag = "manuel" }
elseif ($app.Mode -eq "I") { $modeTag = "interactif" }
else { $modeTag = "silencieux" }
Write-Host ""
Write-Host " === $currentCat ($modeTag) ===" -ForegroundColor Cyan
}
if ($app.Mode -eq "M") {
# MODE MANUEL : ouvrir le lien dans le navigateur
Write-Host " $progress $($app.Name) - ouverture du lien..." -ForegroundColor Magenta
Write-Host " $($app.Url)" -ForegroundColor Gray
Start-Process $app.Url
$installed++
Start-Sleep -Seconds 2
} elseif ($app.Mode -eq "I") {
# MODE INTERACTIF : installeur s ouvre, le technicien choisit le chemin
Write-Host " $progress $($app.Name) - installeur interactif..." -ForegroundColor Yellow
Write-Host " Suivez les instructions." -ForegroundColor Gray
$result = winget install --id $app.Id --interactive --force --accept-package-agreements --accept-source-agreements 2>&1
} else {
# MODE SILENCIEUX : tout automatique
Write-Host " $progress Installation de $($app.Name)..." -ForegroundColor Yellow -NoNewline
$result = winget install --id $app.Id --silent --force --accept-package-agreements --accept-source-agreements 2>&1
}
if ($app.Mode -ne "M") {
if ($LASTEXITCODE -eq 0) {
if ($app.Mode -eq "I") {
Write-Host " OK" -ForegroundColor Green
} else {
Write-Host " OK" -ForegroundColor Green
}
$installed++
} else {
if ($app.Mode -eq "I") {
Write-Host " ECHEC" -ForegroundColor Red
} else {
Write-Host " ECHEC" -ForegroundColor Red
}
$failed += $app
}
}
}
# WSL2
if ($installWSL) {
Write-Host ""
Write-Host " Activation de WSL2..." -ForegroundColor Yellow
wsl --install --no-distribution 2>&1
Write-Host " WSL2 active. Un redemarrage sera necessaire." -ForegroundColor Yellow
Write-Host " Apres redemarrage, lancez : wsl --install -d Ubuntu" -ForegroundColor Yellow
}
# ============================================================
# RESUME FINAL
# ============================================================
Write-Host ""
Write-Host " ============================================================" -ForegroundColor Cyan
Write-Host " INSTALLATION TERMINEE" -ForegroundColor Cyan
Write-Host " ============================================================" -ForegroundColor Cyan
Write-Host ""
Write-Host " Installees avec succes : $installed / $total" -ForegroundColor Green
if ($failed.Count -gt 0) {
Write-Host ""
Write-Host " En echec :" -ForegroundColor Red
foreach ($app in $failed) {
Write-Host " - $($app.Name) (winget install --id $($app.Id))" -ForegroundColor Red
}
Write-Host ""
Write-Host " Pour reinstaller les echecs, copiez la commande ci-dessus." -ForegroundColor Gray
}
if ($installWSL) {
Write-Host ""
Write-Host " WSL2 : Redemarrage necessaire pour finaliser." -ForegroundColor Yellow
}
Write-Host ""
Write-Host " Appuyez sur une touche pour fermer..." -ForegroundColor DarkGray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Placer les fichiers dans l'ISO
Les fichiers doivent être placés dans le dossier $OEM$ de l'ISO. Ce dossier spécial est copié automatiquement par l'installateur Windows dans C:\Windows\Setup\Scripts\ lors de l'installation.
# Créer le dossier $OEM$
mkdir "C:\Win11_Custom\iso_extracted\`$OEM`$\`$`$\Setup\Scripts" -Force
# Copier les scripts
copy C:\SetupComplete.cmd "C:\Win11_Custom\iso_extracted\`$OEM`$\`$`$\Setup\Scripts\"
copy C:\InstallApps.ps1 "C:\Win11_Custom\iso_extracted\`$OEM`$\`$`$\Setup\Scripts\"
Vérifier la structure
dir "C:\Win11_Custom\iso_extracted\`$OEM`$\`$`$\Setup\Scripts\"
Vous devez voir SetupComplete.cmd et InstallApps.ps1.

