
Sommaire
I. Présentation
Ce point est particulièrement important lors des processus d’installation, et devrait être systématiquement stipulé. Faites éventuellement appel à votre fonction de journalisation pour consigner le début et la fin du processus dans un journal.
Pour le cas particulier du processus d’installation MSIEXEC, je vous conseille la lecture de mon article “Eviter les conflits d’installation concurrente sous MSIEXEC“
II. Attendre la fin des processus pour un séquencement maîtrisé
1. Techniques de base
En batch, il suffit de préfixer l’action d’installation par la commande “start /wait” comme par exemple ::
start /wait msiexec /i MonPackage.msi /qb
En vbscript, vous pouvez utiliser la méthode “Run” de l’objet “Shell”. Si la valeur booléenne “bWaitOnReturn” est vraie, le script attend la terminaison du processus avant de poursuivre.
Const bWaitOnReturn = True Set WshShell = CreateObject("Wscript.Shell") WshShell.Run ("msiexec /i MonPackage.msi /qb",,bWaitOnReturn)
Note : Sauf pour les cas de gestion des , je vous déconseille le recours à la méthode “Exec” qui nécessite l’usage d’une boucle d’attente et l’évaluation de la propriété “.status” afin de contrôler la fin du processus.
En Powershell, le plus évident consiste à recourir à l’applet de commande “Start-Process -Wait” :
Start-Process -Wait -FilePath "$env:windir\system32\msiexec.exe" -ArgumentList @("/i","MonPackage.msi","/qb")
Dans certains cas particuliers, vous pouvez avoir besoin de recourir aux taches d’arrière plan (Alias “Jobs”). Il existe de nombreux articles sur le sujet mais pour en dresser les grandes lignes, sachez que Powershell propose des applets de commande dédiées, telles que “Start-Job, Wait-Job, Receive-Job” mais sur ceratines commandes, le commutateur “-asJob“, typiquement lors des appels WMI.
Ajoutez le code suivant dans votre script, pour vous assurer que toutes les taches d’arrière plan sont achevées avant de poursuivre.
# Exemple de requete WMI en arrière-plan Get-WmiObject win32_product -AsJob # Attente de terminaison get-job | wait-job
2. Maîtrise des sorties d’affichage
Cet aparté est issu d’un petit retour d’expérience : En effet, si vous utilisez une technique telle que “start-transcript / stop-transcript” pour consigner les retours d’exécution de vos scripts Powershell lancé par le MDT, vous constaterez que la perte d’information est très importante, voire inexistante dans certains cas.
Ceci est lié au comportement par défaut de l’applet de commande “Start-Process” et la notion de console $Host vers laquelle les flux et sorties d’affichage sont redirigés par défaut. Au besoin, reportez-vous à mon article sur le sujet http://www.it-connect.fr/powershell-pour-les-debutants-4eme-partie/
Une première solution serait d’écrire une fonction alternative à “Start-Process” comme suit :
Function Execute-Command ($commandTitle, $commandPath, $commandArguments) { $pinfo = New-Object System.Diagnostics.ProcessStartInfo $pinfo.FileName = $commandPath $pinfo.RedirectStandardError = $true $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false $pinfo.Arguments = $commandArguments $p = New-Object System.Diagnostics.Process $p.StartInfo = $pinfo $p.Start() | Out-Null $p.WaitForExit() [pscustomobject]@{ commandTitle = $commandTitle stdout = $p.StandardOutput.ReadToEnd() stderr = $p.StandardError.ReadToEnd() ExitCode = $p.ExitCode } } $LogFile = "C:\Scripts\install.log" Start-Transcript -Path $LogFile -Verbose $Cmd = "$env:windir\System32\cscript.exe" $arglist = @( "//Nologo", "c:\Scripts\BIOS.vbs" ) Write-Output "Exécution de la commande :`n$Cmd $arglist" # $Process = Start-Process -FilePath $cmd -ArgumentList $ArgList -wait -NoNewWindow -PassThru $Result = Execute-command -commandTitle "Script" -commandPath $Cmd -commandArguments $arglist Write-Output $Result.Stdout Write-Output "* Code de retour : $($Result.ExitCode) **** (0 = OK, pas d'erreur)" Stop-Transcript
Une seconde approche, inspirée de l’exemple du site https://deploymentbunny.com/category/powershell/ , consiste à “améliorer” et affiner l’appel de la commande :
Function Invoke-Exe{ [CmdletBinding(SupportsShouldProcess=$true)] param( [parameter(mandatory=$true,position=0)] [ValidateNotNullOrEmpty()] [string]$Executable, [parameter(mandatory=$false,position=1)] [string]$Arguments ) if($Arguments -eq "") { Write-Verbose "Execution de Start-Process -FilePath $Executable -ArgumentList $Arguments -NoNewWindow -Wait -Passthru" $ReturnFromEXE = Start-Process -FilePath $Executable -NoNewWindow -Wait -Passthru }else{ Write-Verbose "Execution de Start-Process -FilePath $Executable -ArgumentList $Arguments -NoNewWindow -Wait -Passthru" $ReturnFromEXE = Start-Process -FilePath $Executable -ArgumentList $Arguments -NoNewWindow -Wait -Passthru } Write-Verbose "Code retour : $($ReturnFromEXE.ExitCode)" Return $ReturnFromEXE.ExitCode }