Comment exécuter une action custom Windows Installer avec des privilèges élevés ?

Par défaut, lors du déploiement, une action custom s'exécute avec les privilèges de l'utilisateur courant. Celle-ci ne dispose d'aucun privilège administrateur. Le fait de fixer un scope perMachine comme ceci ne suffit pas :


<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 
  <Package 
     Name="..." 
     Scope="perMachine"> 

Une solution peut consister dans le fait de d'utiliser un terminal avec une élévation de privilège pour lancer msiexec (msiexec /i mon-fichier.msi).

Heureusement WiX Toolset, permet de demander à Windows Installer d'exécuter une action custom avec des privilèges élever facilement. Pour cela, il faut utiliser l'option deferred pour la propriété Execute, et no pour la propriété Impersonate. La raison de ce changement est expluqée ici : Immediate Custom Actions Always Impersonate - Visual Studio Setup (microsoft.com)

Exemple :


<!-- Actions personnalisées --> 
<Binary 
    Id="CustomActions" 
    SourceFile="CustomActions.CA.dll"/> 
<CustomAction Id="DoSomething" 
    BinaryRef="CustomActions" 
    DllEntry="DoSomething" 
    Return="check" 
    Execute="deferred" 
    Impersonate="no"/>

Bien évidemment, l'action custom DoSomething doit être présente dans la séquence InstallExecuteSequence. Son exécution doit être planifiée entre InstallExecute, et InstallFinalize.


<InstallExecuteSequence>
  <Custom Action="DoSomething" Before="InstallFinalize" /> 
</InstallExecuteSequence> 

Après avoir appliqué ces modifications, l'action custom s'exécutera toujours avec des privilèges élevés.

Jérémy Jeanson

Comment automatiser l’installation de MSI avec Azure DevOps, et Release Manager ?

Automatisé le déploiement de MSI, n’a jamais été une tâche triviale. Aujourd’hui, je vous propose d’effectuer celle-ci avec Azure DevOps.

Pour l’exemple, j’utilise un pipeline de Release, et j’y ai ajouté une tâche powerShell :

Tâche PowerShell d'un pipeline de release

Dans la propriété Inline, il suffit de coller le contenu du script qui suit, et le tour est joué :


$msi = Get-ChildItem $(System.ArtifactsDirectory)\*.msi -Recurse
Write-Host "Msi trouvé : $msi"

$log = "D:\Applications\MyApp\installation.log"
if (Test-Path $log) {
        Remove-Item $log -verbose
}
 
$arguments = @( "/i", $msi, "INSTALLFOLDER=""D:\Applications\MyApp""", "/quiet","/lv",$log)
 Write-Host $arguments 
 
Start-Process msiexec.exe -ArgumentList $arguments -Wait

Ce script va piloter msiexec en mode silencieux (c’est-à-dire sans interface), et attendre la fin du déploiement. Pour diagnostiquer les éventuels problèmes qui pourraient survenir lors du déploiement, un fichier de log est créé. Ce fichier est conservé jusqu’à la prochaine exécution.

Bien évidemment, il faudra adapter la variable $arguments en fonction de vos besoins, et les différents chemins.

Ce script peut aussi être utilisé avec un pipeline de type multistage, et un job de déploiement.

Jérémy Jeanson

Comment adapter le nom d'un job d'Azure Pipeline, en fonction d'un paramètre de build ?

Avec Azure Pipeline, changer le nom d'un job à la voler, ou du moins afficher un libellé propre n'est pas toujours de tout repos. Certaines syntaxes peuvent fonctionner avec la version Cloud d'Azure DevOps, et ne pas être prises en charge on-premise. D'autres ne sont pas possibles sur certaines propriétés.

Afin d'arriver à mes fins avec Azure DevOps, j'ai mis en place une stratégie simple, et efficace : passer par des variables.

Quelle que soit la plateforme :

  • On peut conditionner la création d'une variable.
  • On peut conditionner l'affectation d'une variable (via une tache powershell par exemple).
  • Toutes les propriétés acceptent des variables.

Voici un petit exemple avec la propriété displayName d'un job (un vrai cauchemar à conditionner). Le texte affiché pour présenter le job change en fonction d'un paramètre choisi par l'utilisateur au moment de lancer la build. Pour effectuer cette opération, je passe par une variable jobDisplayName dont la définition change en fonction du paramètre jobType.


parameters:
  - name: jobType
    displayName: Type de build
    type: string
    default: evolution
    values:
      - correctif
      - evolution

variables:
  ${{ if eq(parameters.jobType,'correctif') }}:
    jobDisplayName: "Compilation d'un correctif"
  ${{ else }}:
    jobDisplayName: "Compilation d'une nouvelle version"

jobs:
- job: Build
  displayName: ${{ variables.jobDisplayName }}

Simple, efficace, et déclinable à volonté.

Attention : le else utilisé ici ne fonctionnera pas sur une instance Azure DevOps Server qui n'est pas à jour. Il faudra alors utiliser un second if.

Jérémy Jeanson

Dans quelles situations faut-il utiliser les options de Clean des pipelines d’Azure DevOps ?

Diverses tâches d’Azure Pipeline disposent d’options de Clean permettant de supprimer les fichiers inutiles (artefacts, binaires des précédentes builds, dossiers bin/obj et...). Ces options sont disponibles sur les tâches de compilations, les tâches de copies de fichiers, et dossiers.

Des développeurs un peu trop zélés auraient tendance à activer ces options à défaut. Malheureusement, celles-ci peuvent augmenter inutilement la durée d’exécution de la build. Il convient donc de prendre un peu de temps pour réfléchir à l’utilité de telles options.

Pour savoir si l’on peut avoir besoin d’utiliser des options de Clean, il convient de répondre à quelques questions simples :

  • Où s’exécutent les builds ?
  • Quel gestionnaire de code source est utilisé ?
  • Les builds utilisent elles les dossiers de binaires en plus du staging des artefacts (BinariesDirectory) ?

L’usage des options de clean est conditionné par la combinaison de vos réponses à ces trois questions.

Voulant faire simple, j’ai choisi de construire un tableau indiquant les situations pour lesquelles, un Clean est utile.

Source control BinariesDirectory
Agent de build Git TFVC Oui Non
Microsoft hosted Non Non Non Non
Self hosted
/ Azure VMs Scale Set
Non Oui Oui Non

Pour résumer simplement les raisons qui ont conduit à apporter ces réponses :

  • Les agents de builds hébergés par Microsoft sont détruits après chaque exécution. Un agent ne conserve donc aucune trace des précédentes builds, contrairement aux VM Azure, et agents que l’on héberge soit même.
  • Avant de télécharger, du code versionné via git, un pipeline lance la commande git clean -ffdx (par défaut). Celle-ci effectue un clean du repository local. Le répertoire contant le code source est donc propre, quoi qu’il arrive (par défaut).
  • Le Workspace TFVC n’est pas nettoyé entre les builds. Il en est de même pour les dossiers des binaires, et des tests uhnitaires.

Conclusion

Aucun mécanisme de Clean n’est nécessaire pour un usage 100% Cloud d’Azure DevOps, sauf si on utilise des VM Azure. Il en est de même pour un usage hybride avec Azure DevOps Server, et des agents Microsoft hosted.

Un usage 100% on-premise demandera donc un peu plus de réflexion.

Jérémy Jeanson

Pourquoi winget affiche parfois des Id étranges ?

Par le passé, j'ai eu à expliquer à des développeurs qu'il ne fallait pas utiliser un Guid pour piloter winget. Car l'Id d’une application peut parfois prendre des formes étranges.

Sachant qu'un bon exemple vaut mieux que de longues explications, voici une petite démo de winget retournant une liste contenant un id hors norme :


PS C:\Users\jeremy> winget list "app"
Nom ID Version
-----------------------------------------------------------
App {A0B1C63D-F1E2-4306-4A58-A8140C917516} 1.0.0.9
App2 a8cf55b6c07a823e 1.0.0.1


Dans le cas présent, la première application a été déployée via un msi, la seconde ClickOnce.

Voici donc la raison pour laquelle il ne faut pas penser que winget n'utilise que des Guid ;)

Jérémy Jeanson