Le blog technique

Toutes les astuces #tech des collaborateurs de PI Services.

#openblogPI

Retrouvez les articles à la une

AutoPilot: Post ESP – Retour d’expérience et améliorations de la solution (v2)

Contexte

Dans un précédent article, nous avons présenté une solution basée sur deux scripts PowerShell permettant d’installer une application Win32 après la fin de l’ESP (Enrollment Status Page) lors d’un enrôlement Windows Autopilot.

Après déploiement en production, deux comportements inattendus ont été identifiés. Cet article décrit ces problèmes et les corrections apportées dans la version 2 des scripts.

Problèmes identifiés

Problème 1 — La tâche planifiée se déclenchait sur la session defaultuser0

Dans un scénario Autopilot Pre-Provisioning (White Glove), la phase Device Setup est exécutée par le processus CloudExperienceHostBroker.exe. À la fin de cette phase, ce processus déclenche un redémarrage automatique du device.

Après ce redémarrage, Windows ouvre automatiquement une session sous le compte defaultuser0 et affiche la page Reseal — la page de fin du Pre-Provisioning qui invite le technicien à sceller le device avant sa livraison à l’utilisateur.

Le point clé : la session defaultuser0 est déjà active à ce stade, même si personne n’a encore interagi avec le device. La tâche planifiée avec le trigger AtLogOn se déclenchait donc immédiatement après le redémarrage, pendant que le device attendait simplement sur la page Reseal sans aucune action utilisateur.

Autopilot Pre-Provisioning — Device Setup
        │
        ▼
CloudExperienceHostBroker.exe termine la phase Device Setup
        │
        ▼
Redémarrage automatique du device
        │
        ▼
Windows ouvre la session defaultuser0
Device affiche la page Reseal (en attente du technicien)
        │
        ▼  ← à ce moment, defaultuser0 est déjà connecté
Trigger AtLogOn se déclenche immédiatement  ❌
        │
        ▼
Script d'installation démarre en arrière-plan
(sans que personne n'ait cliqué quoi que ce soit)
        │
        ▼
Boucle ESP → attend jusqu'à 5h inutilement

Solution _ Problème 1 : Guard defaultuser0

Un contrôle a été ajouté au début du script pour arrêter immédiatement l’installation si la session détectée est defaultUser0

# ------------------------------------------------------------------
# 0. Guard 1 : defaultuser0 → exit immédiat ──
# ------------------------------------------------------------------
$currentUser = (Get-WmiObject -Class Win32_ComputerSystem -ErrorAction SilentlyContinue).UserName
Write-Log "Current logged user : $currentUser"

if ([string]::IsNullOrEmpty($currentUser) -or $currentUser -like "*defaultuser0*") {
    Write-Log "defaultuser0 session - exiting immediately. Task will retry at real user logon."
    exit 0 
}
defaultuser0 logon
        │
        ▼
Guard Win32_ComputerSystem.UserName = "defaultuser0"
        │
        ▼
exit 0 immédiat (< 1 seconde)
        │
        ▼
Tâche planifiée reste active → réessaie au vrai logon utilisateur ✅

Problème 2 — Timeout de la boucle ESP sans vérification de l’état réel

La boucle d’attente ESP de la version 1 utilisait la condition suivante :

do {
    # vérifie les checks ESP
    if ($c1 -and $c2 -and $c3 -and $c4) { break }

    Start-Sleep -Seconds $pollInterval
    $espWaitSeconds += $pollInterval

} while ($espWaitSeconds -lt $espMaxWait)

# ← installation lancée ici, sans savoir pourquoi on est sorti de la boucle

Si le timeout de 5h était atteint sans que les checks ESP soient validés, le script sortait de la boucle et continuait vers l’installation — exactement comme si l’ESP était terminée.

Cause

La condition while ($espWaitSeconds -lt $espMaxWait) provoque deux sorties possibles :

SortieRaisonComportement v1
breakTous les checks $true✅ Installation correcte
Fin de boucleTimeout 5h, checks toujours $false❌ Installation quand même

Solution _ Problème 2 : Flag $espCompleted

Un flag explicite permet de distinguer les deux cas de sortie :

$espCompleted = $false  # ← nouveau flag

do {
    $c1 = Test-IsSyncDone
    $c2 = Test-SidecarCompleted
    $c3 = Test-HasProvisioningCompleted
    $c4 = Test-TrackingPoliciesCreated

    Write-Log "ESP check at ${espWaitSeconds}s → IsSyncDone=$c1 | Sidecar=$c2 | Provisioning=$c3 | Policies=$c4"

    if ($c1 -and $c2 -and $c3 -and $c4) {
        $espCompleted = $true  # ← toutes conditions validées
        break
    }

    Start-Sleep -Seconds $pollInterval
    $espWaitSeconds += $pollInterval

} while ($espWaitSeconds -lt $espMaxWait)

# ── Vérifier POURQUOI on est sorti ──
if (-not $espCompleted) {
    Write-Log "TIMEOUT: ESP did not complete after $($espMaxWait/3600)h"
    Write-Log "Checks → IsSyncDone=$c1 | Sidecar=$c2 | Provisioning=$c3 | Policies=$c4"
    Write-Log "Aborting - scheduled task remains for next logon retry."
    exit 1  # ← ne pas installer, conditions pas remplies
}

Write-Log "ESP completed after ${espWaitSeconds}s - proceeding with installation."
# ← ici on est certain que tous les checks sont true
Boucle ESP démarre
        │
        ├── Checks tous true → $espCompleted = true → break → Installe ✅
        │
        └── Timeout 5h → $espCompleted = false → exit 1
                                │
                                ▼
                        Tâche planifiée reste active
                        Retry au prochain logon utilisateur ✅

Récapitulatif des corrections

#ProblèmeVersion 1Version 2
1Tâche déclenchée sur defaultuser0Trigger AtLogOnConsoleConnect + Guard ✅
2Timeout ESP → installation forcéeInstalle quand même ❌Flag $espCompletedexit 1

Script 1

# ============================================================
# Install_XXXX_PostESP.ps1 - Payload Script (runs at logon)
# Purpose : Wait for Autopilot ESP Phase 3 to finish, then
#           install XXXX MSI with retry logic.
#
# Triggered by : Scheduled Task "XXXXInstallIfMissing"
# RunAs        : SYSTEM
# Author       : Lazher YAAKOUBI
# Version      : 2.7.3
# ============================================================

$XXXXFolder  = "C:\ProgramData\XXXX\XXXX_Source"
$LogPath        = "$env:WINDIR\Temp\install_XXXX.log"
$LogFile        = "$env:WINDIR\Temp\XXXX-install-attempt.log"
$TaskName       = "XXXXInstallIfMissing"
$filePath       = "C:\Program Files\XXXX\XXXX"

# Check MSI file...
$InstallerSourceMSI = Get-ChildItem -Path $XXXXFolder -Filter "*.msi" | Select-Object -First 1
if (-not $InstallerSourceMSI) {
    
    Add-Content -Path $LogFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') : ERROR - No MSI found in $XXXXFolder. Exiting." -ErrorAction SilentlyContinue
    exit 1
}

$InstallerName = $InstallerSourceMSI.Name
$InstallerPath = Join-Path $XXXXFolder $InstallerName

# e.g. "XXXX-windows-4.2.0.172-x64" -> "4.2.0.172"
$MSIVersion = [System.IO.Path]::GetFileNameWithoutExtension($InstallerPath) `
              -replace 'XXXX-windows-', '' `
              -replace '-x64', '' `
              -replace '-corp', '' `
              -replace '-[a-zA-Z].*$', ''   # catch any other suffix

function Write-Log {
    param([string]$Message)
    $line = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') : $Message"
    Add-Content -Path $LogFile -Value $line -ErrorAction SilentlyContinue
}

# ------------------------------------------------------------------
# 0. Guard 1 : defaultuser0 → exit immédiat  ──
# ------------------------------------------------------------------
$currentUser = (Get-WmiObject -Class Win32_ComputerSystem -ErrorAction SilentlyContinue).UserName
Write-Log "Current logged user : $currentUser"

if ([string]::IsNullOrEmpty($currentUser) -or $currentUser -like "*defaultuser0*") {
    Write-Log "defaultuser0 session - exiting immediately. Task will retry at real user logon."
    exit 0 
}

# -------------------------------------------------------
# 1. Guard 2 : skip if XXXX is already installed
# -------------------------------------------------------
if (Test-Path $filePath) {
    if ($rawFileVersion -eq $MSIVersion) {
     #>
        Write-Log "XXXX $rawFileVersion already installed - removing scheduled task and exiting."
       
        if (Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue) 
        {
            Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue
            Write-Log "Scheduled task '$TaskName' removed."
        } else 
            {
            Write-Log "Scheduled task '$TaskName' does not exist."
            }

        exit 0
        <#
    } else {
        Write-Log "Installed version ($rawFileVersion) differs from MSI version ($MSIVersion) - proceeding with installation."
    }
    #>
}

Write-Log "Waiting for ESP completion (using IME-equivalent checks)..."

$espWaitSeconds = 0
$espMaxWait     = 18000  # 5 h
$pollInterval   = 15     # seconds between checks
$espCompleted   = $false

# ---------- Check 1: IsSyncDone ----------
function Test-IsSyncDone {
    $enrollments = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Enrollments" -ErrorAction SilentlyContinue
    foreach ($enrollment in $enrollments) {
        $providerID = (Get-ItemProperty -Path $enrollment.PSPath -Name "ProviderID" -ErrorAction SilentlyContinue).ProviderID
        if ($providerID -ne "MS DM Server") { continue }

        $firstSyncPath = Join-Path $enrollment.PSPath "FirstSync"
        $userSidKeys = Get-ChildItem -Path $firstSyncPath -ErrorAction SilentlyContinue
        foreach ($sidKey in $userSidKeys) {
            $isSyncDone = (Get-ItemProperty -Path $sidKey.PSPath -Name "IsSyncDone" -ErrorAction SilentlyContinue).IsSyncDone
            if ($isSyncDone -eq 1) { return $true }
        }
    }
    return $false
}

# ---------- Check 2: Sidecar InstallationState ----------update must be performed to resolve issues related to failed autopilot device!!!
function Test-SidecarCompleted {
    $enrollments = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Enrollments" -ErrorAction SilentlyContinue
    foreach ($enrollment in $enrollments) {
        $sidecarPath = Join-Path $enrollment.PSPath "PolicyProviders\Sidecar"
        $state = (Get-ItemProperty -Path $sidecarPath -Name "InstallationState" -ErrorAction SilentlyContinue).InstallationState
        if ($state -eq "Completed") { return $true }
    }
    # If Sidecar key doesn't exist at all, it's not blocking ESP
    return $true
}

# ---------- Check 3: HasProvisioningCompleted (WMI) ----------
function Test-HasProvisioningCompleted {
    try {
        $result = Get-WmiObject -Namespace "root\cimv2\mdm\dmmap" `
            -Query "SELECT HasProvisioningCompleted FROM MDM_EnrollmentStatusTracking_Setup01" `
            -ErrorAction Stop
        if ($null -ne $result -and $result.HasProvisioningCompleted -eq $true) { return $true }
    } catch {
        return $true
    }
    return $false
}

# ---------- Check 4: TrackingPoliciesCreated (WMI) ----------
function Test-TrackingPoliciesCreated {
    try {
        $result = Get-WmiObject -Namespace "root\cimv2\mdm\dmmap" `
            -Query "SELECT TrackingPoliciesCreated FROM MDM_EnrollmentStatusTracking_PolicyProviders03_01" `
            -ErrorAction Stop
        if ($null -ne $result -and $result.TrackingPoliciesCreated -eq $true) { return $true }
    } catch {
        return $true
    }
    return $false
}

# ---------- Check 5: WWAHost.exe Running ----------
function Test-WWAHostVisible {
    $wwa = Get-Process -Name "WWAHost" -ErrorAction SilentlyContinue |
           Where-Object { $_.SessionId -gt 0 }
    return ($null -ne $wwa)
}

# ---------- Main polling loop ----------
do {
    $c1 = Test-IsSyncDone
    $c2 = Test-SidecarCompleted
    $c3 = Test-HasProvisioningCompleted
    $c4 = Test-TrackingPoliciesCreated
    $c5 = Test-WWAHostVisible

    Write-Log ("ESP/WHfB status at ${espWaitSeconds}s - " +
               "IsSyncDone=$c1 | SidecarCompleted=$c2 | " +
               "HasProvisioningCompleted=$c3 | TrackingPoliciesCreated=$c4 | " +
               "WWAHostVisible=$c5")

    if ($c1 -and $c2 -and $c3 -and $c4 -and (-not $c5)) {
        Write-Log "All checks passed - ESP done and WWAHost closed. Proceeding after ${espWaitSeconds}s."
        $espCompleted = $true
        break
    }

    Start-Sleep -Seconds $pollInterval
    $espWaitSeconds += $pollInterval

} while ($espWaitSeconds -lt $espMaxWait)

if (-not $espCompleted)
{
    Write-Log "TIMEOUT ESP did not completed after $($espMaxWait/3600)H - IsSyncDone = $c1, SidecarCompleted = $c2, HasProvisioningCompleted = $c3, TrackingPoliciesCreated = $c4"
    Write-Log "Aborting installation - scheduled task remains for next logon retry..."
    exit 1
}

Write-Log "ESP completed after ${espWaitSeconds}s - proceeding with installation..."

# -------------------------------------------------------
# 2. INSTALL XXXX WITH RETRY LOGIC
# -------------------------------------------------------
if (-Not (Test-Path $InstallerPath)) {
    Write-Log "ERROR: Installer not found at $InstallerPath"
    exit 1
}

$msiArgs = "/i `"$InstallerPath`" /qn /l*v `"$LogPath`" " +
           "STRICTENFORCEMENT=1 " +
           "CLOUDNAME=XXXXX " +
           "USERDOMAIN=XXXXX " +
           "POLICYTOKEN=343837323A333A30323236653933352D383433632D343531352D396338312D326333383135613036613665 " +
           "REBOOT=ReallySuppress"

$maxAttempts = 10
$attempts    = 0
$success     = $false

while (-not $success -and $attempts -lt $maxAttempts) {
    $attempts++
    Write-Log "Installation attempt $attempts of $maxAttempts"

    try {
        $process = Start-Process "msiexec.exe" `
            -ArgumentList $msiArgs `
            -Wait -PassThru -NoNewWindow -ErrorAction Stop

        Write-Log "msiexec exit code: $($process.ExitCode)"

        switch ($process.ExitCode) {
            0    { $success = $true ; Write-Log "Installation succeeded." }
            3010 { $success = $true ; Write-Log "Installation succeeded (reboot required - suppressed)." }
            1618 { Write-Log "Another MSI installation in progress (1618) - retrying in 3 min..." ; Start-Sleep -Seconds 180 }
            1619 { Write-Log "Package could not be opened (1619) - retrying in 3 min..." ; Start-Sleep -Seconds 180 }
            default {
                Write-Log "Unexpected exit code $($process.ExitCode) - retrying in 3 min..."
                Start-Sleep -Seconds 180
            }
        }
    } catch {
        Write-Log "ERROR launching msiexec: $_"
        Start-Sleep -Seconds 180
    }
}

# -------------------------------------------------------
# 3. FINAL STATUS
# -------------------------------------------------------
if ($success) {
    Write-Log "XXXX installed successfully - removing scheduled task."
    Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue

    Write-Log "Cleaning up XXXX_Source folder (full removal)..."
    #Remove-Item -Path "$XXXXFolder\*" -Exclude $InstallerName -Confirm:$false -Recurse -ErrorAction Stop
    Remove-Item -Path "$XXXXFolder" -Confirm:$false -Recurse -ErrorAction Stop
    exit 0
} else {
    Write-Log "FAILED after $maxAttempts attempts - scheduled task remains for next logon retry."
    exit 1
} 

Script 2 Copy

# ============================================================
# Copy-XXXX-task.ps1 - Payload Script (runs at AutoPilot phase 2)
# Purpose : Update source files if needed and create scheduled task.
#
# Triggered by : Intune application deployement services"
# RunAs        : SYSTEM
# Author       : Lazher YAAKOUBI
# Version      : 2.7.3
# ============================================================

# Folder where XXXX installation files must be copied
$XXXXFolder = "C:\ProgramData\XXXX\XXXX_Source"
$LogFile  = "$env:WINDIR\Temp\XXXX-Copy-TaskCreation.log"
#$filePath = "C:\Program Files\XXXX\"

# Find the MSI and script files
$InstallerSourceMSI = Get-ChildItem -Path $PSScriptRoot -Filter "*.msi" | Select-Object -First 1
$InstallerSourcePS1 = Get-ChildItem -Path $PSScriptRoot -Filter "*.ps1" | Where-Object { $_.Name -like "*Install_XXXX*" } | Select-Object -First 1

function Write-Log {
    param([string]$Message)
    $line = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') : $Message"
    Add-Content -Path $LogFile -Value $line -ErrorAction SilentlyContinue
}

# Check source files existance MSI&PS1
if (-not $InstallerSourceMSI) {
    Write-Log "Error: No MSI file found in $PSScriptRoot."
    exit
}

if (-not $InstallerSourcePS1) {
    Write-Log "Error: No installation script found in $PSScriptRoot."
    exit
}

$InstallerName = $InstallerSourceMSI.Name
$InstallerSource = $InstallerSourceMSI.FullName
$ScriptName = $InstallerSourcePS1.Name
$ScriptSource = $InstallerSourcePS1.FullName

$InstallerDest = Join-Path $XXXXFolder $InstallerName
$ScriptDest    = Join-Path $XXXXFolder $ScriptName

# Name of the scheduled task
$TaskName = "XXXXInstallIfMissing"

# Check if folder exists
if (-Not (Test-Path $XXXXFolder)) {
    #
    try {
        New-Item -ItemType Directory -Path $XXXXFolder -Force | Out-Null
        Write-Log "Folder $XXXXFolder created successfully."
    } catch {
        Write-Log "Error: Failed to create folder $XXXXFolder. $_"
        exit
    }

    Copy-Item -Path $InstallerSource -Destination $InstallerDest -Force
    Copy-Item -Path $ScriptSource    -Destination $ScriptDest    -Force
} else {
    Write-Log "XXXX folder already exists. Checking for updates..."

    # Update MSI if needed
    $ExistingMSI = Get-ChildItem -Path $XXXXFolder -Filter "*.msi" -File
    if ($ExistingMSI) {
        $ExistingMSIVersion = [System.IO.Path]::GetFileNameWithoutExtension($ExistingMSI.Name) -replace 'XXXX-windows-', '' -replace '-x64', '' -replace '-corp', ''
        $NewMSIVersion      = [System.IO.Path]::GetFileNameWithoutExtension($InstallerName)    -replace 'XXXX-windows-', '' -replace '-x64', '' -replace '-corp', ''

        # Version comparison
        if (([version]$ExistingMSIVersion) -lt ([version]$NewMSIVersion)) {
            Write-Log "Newer MSI version detected ($NewMSIVersion). Updating..."
            Remove-Item -Path "$XXXXFolder\*" -Force
            Copy-Item -Path $InstallerSource -Destination $InstallerDest -Force
            Copy-Item -Path $ScriptSource -Destination $InstallerDest -Force
        } else {
            Write-Log "Existing MSI version ($ExistingMSIVersion) is up to date."
        }
    } else {
        Copy-Item -Path $InstallerSource -Destination $InstallerDest -Force
    }

    # Always overwrite the script so it stays in sync with the MSI
    Copy-Item -Path $ScriptSource -Destination $ScriptDest -Force
    Write-Log "Installation script updated."
}

$TaskXML = @"
<Task xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task" version="1.3">
<Triggers>
    <LogonTrigger>
        <Enabled>true</Enabled>
    </LogonTrigger>
</Triggers>
<Principals>
    <Principal id="Author">
        <UserId>SYSTEM</UserId>
        <RunLevel>HighestAvailable</RunLevel>
    </Principal>
</Principals>
<Settings>
        <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
        <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
        <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
        <AllowHardTerminate>true</AllowHardTerminate>
        <StartWhenAvailable>true</StartWhenAvailable>
        <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
        <StopOnIdleEnd>true</StopOnIdleEnd>
        <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession>
    <UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
    <Priority>7</Priority>
</Settings>
<Actions Context="Author">
    <Exec>
        <Command>powershell.exe</Command>
        <Arguments>-ExecutionPolicy Bypass -WindowStyle Hidden -File "$ScriptDest"</Arguments>
    </Exec>
</Actions>
</Task>
"@

# Check if the scheduled task already exists
$ExistingTask = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if ($ExistingTask) {
    Write-Log "Scheduled task '$TaskName' already exists. Skipping creation."
} else {
    try {
        Register-ScheduledTask -TaskName $TaskName -Xml $TaskXML -Force -ErrorAction Stop
        Write-Log "Scheduled task '$TaskName' created successfully." #| Out-File -FilePath "$env:WINDIR\Temp\TaskCreation.log" -Append
    } catch {
        Write-Log "Failed to create scheduled task: $_" #| Out-File -FilePath "$env:WINDIR\Temp\TaskCreation.log" -Append
    }
} 

RDS – Le durcissement des serveurs casse le SSO

L’application de stratégies de durcissement (hardening) standard telles que les baselines Microsoft ou les benchmarks CIS peut avoir des effets de bord parfois compliqués à élucider.

Dans le cas qui nous intéresse, le Single Sign On ne fonctionnait plus sur une ferme Remote Desktop suite à l’application des baselines Microsoft : lors de l’ouverture d’une RemoteApp via le portail RDWeb, une popup d’authentification apparaissait au lieu de démarrer l’application de façon transparente.


La stratégie responsable de ce comportement est Computer Configuration\Policies\Administrative Templates\Windows
Components\Remote Desktop Services\Remote Desktop Session
Host\Security\Always prompt for password upon connection : comme son nom l’indique, elle force le service Remote Desktop à demander un mot de passe lors de la connexion ce qui bloque implicitement le SSO.

Il est donc nécessaire de créer une GPO qui va venir « remplacer » ce paramètre de durcissement uniquement pour les serveurs de la ferme RDS, en l’appliquant dans leur OU ou en filtrant l’application de la GPO sur le groupe dont ils sont membres.

Point bonus : si le SSO échoue dès l’accès au site RDWeb et que vous voyez la mire d’authentification à la place, pensez à ajouter son URL aux « sites Intranet local » (PAS aux « sites de confiance »! Et attention à bien ajouter tout alias, nom court, fqdn… qui pourrait être utilisé)!
Vous pouvez réaliser cette manipulation soit manuellement (plutôt pour tester), soit idéalement par GPO également : User/ Policies/administrative templates/Windows Components/Internet Explorer/Internet Controle Panel/Security Page/Site to Zone Assignment List


Connexion d’une machine Linux à Active Directory avec SSSD

Intégrer des serveurs ou des postes Linux dans un environnement Windows Active Directory (AD) est un excellent moyen de centraliser la gestion des utilisateurs et des accès. Finie l’époque où il fallait jongler avec des configurations LDAP et Kerberos complexes et fragiles. Aujourd’hui, SSSD (System Security Services Daemon) fait le travail de manière robuste et sécurisée.

Voici un guide rapide pour lier votre machine Linux à votre domaine AD en quelques minutes.

Pourquoi choisir SSSD ?

  • Gestion du cache : Permet aux utilisateurs de se connecter même si le contrôleur de domaine est temporairement inaccessible.
  • Performance : Centralise les requêtes d’authentification et réduit la charge sur le réseau.
  • Simplicité : S’intègre parfaitement avec realm, un outil qui automatise la majeure partie de la configuration.

Étape 1 : Les prérequis indispensables

Avant de lancer les commandes, assurez-vous que :

  1. Votre machine Linux a une adresse IP fixe ou statique via DHCP.
  2. Le fichier /etc/resolv.conf pointe bien vers les serveurs DNS de votre Active Directory.
  3. L’heure de votre machine Linux est parfaitement synchronisée avec celle du contrôleur de domaine (le protocole Kerberos ne tolère pas plus de 5 minutes de décalage).

Étape 2 : Installation des paquets

Sur une distribution basée sur Ubuntu / Debian, installez les outils nécessaires avec la commande suivante :

Bash

sudo apt update
sudo apt install sssd sssd-tools realmd adcli krb5-user packagekit sssd-ad

(Note : Durant l’installation, Kerberos peut vous demander le nom de votre royaume. Inscrivez-le en MAJUSCULES, ex: MONENTREPRISE.LOCAL).

Étape 3 : Découverte et jonction au domaine

C’est ici que la magie de realmd opère. Tout d’abord, vérifiez que Linux voit bien votre domaine AD :

Bash

realm discover monentreprise.local

Si tout est correct, lancez la jonction au domaine. Vous aurez besoin des identifiants d’un compte Administrateur de l’AD (ou d’un compte ayant les droits de joindre des machines au domaine) :

Bash

sudo realm join -U Administrateur monentreprise.local

Entrez le mot de passe lorsque demandé. Si la commande s’exécute sans erreur, félicitations : votre machine fait officiellement partie du domaine !

Étape 4 : Utilisation et vérification

Pour vérifier que SSSD communique bien avec votre Active Directory, vous pouvez tester la résolution d’un utilisateur de l’AD avec la commande id :

Bash

id utilisateur_ad@monentreprise.local

Le terminal devrait vous renvoyer l’UID, le GID et les groupes Windows de l’utilisateur.

Astuce : Se connecter sans taper le nom de domaine

Par défaut, SSSD vous oblige à vous connecter sous la forme username@domain. Si vous préférez utiliser uniquement le nom d’utilisateur simple (username), modifiez le fichier /etc/sssd/sssd.conf et passez cette option à False :

Ini, TOML

use_fully_qualified_names = False

N’oubliez pas de redémarrer le service après modification :

Bash

sudo systemctl restart sssd

En conclusion

En utilisant le combo realmd + SSSD, l’intégration d’une machine Linux dans un Active Directory Windows devient un jeu d’enfant. Vous bénéficiez d’une authentification centralisée, sécurisée et d’une gestion simplifiée pour vos administrateurs système.