Déployer des réseaux Wi-Fi sur vos postes en mode D OS

Vos utilisateurs doivent pouvoir se connecter au Wi-Fi dès l'allumage du poste, sans saisir de mot de passe et sans intervention de votre support. Cette page explique comment intégrer vos réseaux Wi-Fi à votre poste master, pour que tous les postes déployés en mode D OS en bénéficient automatiquement.

Compatibilité : D OS ✅

Temps estimé : 30 minutes, hors création de version.


Ce que vous allez obtenir

À la fin de cette procédure :

  • vos réseaux Wi-Fi sont configurés sur tous les postes issus de votre master ;
  • la connexion est disponible dès l'écran d'ouverture de session, pour tous les utilisateurs ;
  • vous pouvez déclarer plusieurs réseaux, dans l'ordre de préférence de votre choix ;
  • chaque démarrage laisse une trace consultable en cas de problème.

Exemple concret
Un établissement dispose d'un réseau principal Etab-Pedago et d'un réseau de secours Etab-Secours dans un bâtiment annexe. Avec cette procédure, chaque poste connaît les deux réseaux, se connecte en priorité à Etab-Pedago, et bascule automatiquement sur Etab-Secours lorsqu'il est dans l'annexe.


Les scripts fournis

ScriptRôleOù l'utiliser
setWifi_v2.ps1Installe vos réseaux Wi-Fi à chaque démarrageSur le poste master, en script de démarrage
Verifier-SetWifi.ps1Contrôle l'installation : script, encodage, droits, profils, journalSur le master puis sur le poste de validation, à la demande

Le contenu complet des deux scripts est disponible en annexe, à la fin de cette page.


Comment ça marche

Le principe tient en trois étapes :

  1. Vous déclarez vos réseaux dans un script, SetWifi.
  2. Ce script est placé sur votre poste master et s'exécute à chaque démarrage de la machine.
  3. Les postes déployés à partir de ce master héritent du script : ils installent vos réseaux Wi-Fi à chaque démarrage.

Le script s'exécute avant l'ouverture de session, sans aucune action de l'utilisateur. S'il rencontre un problème avec un réseau, il le signale et passe au suivant : un réseau mal renseigné ne bloque ni les autres réseaux, ni le démarrage du poste.


Prérequis

Avant de commencer, vérifiez que vous disposez de :

  • les scripts setWifi_v2.ps1 et Verifier-SetWifi.ps1, fournis en annexe de cette page ;
  • un accès administrateur à votre poste master ;
  • pour chaque réseau : son nom exact (SSID), sa clé et son type de sécurité (WPA2 ou WPA3) ;
  • un poste de validation équipé d'une carte Wi-Fi, à portée des réseaux concernés.

💡 Où trouver le type de sécurité ?
Il est indiqué dans la configuration de votre borne ou de votre contrôleur Wi-Fi. En cas de doute, choisissez WPA2 : c'est le type le plus répandu.


Étape 1 — Renseigner vos réseaux

Récupérez le script depuis l'annexe :

  1. Rendez-vous à la section Script 1 — setWifi_v2.ps1.
  2. Copiez l'intégralité du bloc de code (bouton de copie en haut à droite du bloc, ou sélection complète).
  3. Collez-le dans un éditeur de texte (Bloc-notes ou Visual Studio Code).
  4. Enregistrez le fichier sous le nom setWifi_v2.ps1, en respectant l'encodage indiqué plus bas.

En haut du fichier, repérez le bloc $WifiNetworks et remplacez les lignes d'exemple par vos réseaux, une ligne par réseau :

$WifiNetworks = @(
    @{ SSID = 'Etab-Pedago';  Password = 'VotreCle1' }
    @{ SSID = 'Etab-Secours'; Password = 'VotreCle2' }
)

Dans la plupart des cas, le nom et la clé suffisent. Pour les situations particulières, ajoutez les options suivantes :

BesoinOption à ajouterExemple
Réseau en WPA3Authentication = 'WPA3SAE'@{ SSID = 'Etab-WPA3'; Password = 'VotreCle'; Authentication = 'WPA3SAE' }
Réseau masqué (nom non diffusé)Hidden = $true@{ SSID = 'Labo'; Password = 'VotreCle'; Hidden = $true }
Connexion manuelle uniquementAutoConnect = $false@{ SSID = 'Invites'; Password = 'VotreCle'; AutoConnect = $false }
Réseau ouvert, sans cléAuthentication = 'open'@{ SSID = 'Invites'; Authentication = 'open'; AutoConnect = $false }

📌 L'ordre compte
Le premier réseau de la liste est celui auquel le poste se connecte en priorité lorsque plusieurs sont disponibles. Placez votre réseau principal en premier.

📌 Règles à respecter

  • Le SSID doit être écrit exactement comme il apparaît sur le réseau, majuscules comprises.
  • La clé doit compter entre 8 et 63 caractères, sans accent.
  • Conservez les apostrophes ' autour des valeurs.

Enregistrez le fichier en « UTF-8 avec BOM ».

  • Dans le Bloc-notes : Fichier › Enregistrer sous, puis choisissez l'encodage UTF-8 avec BOM.
  • Dans Visual Studio Code : cliquez sur l'encodage en bas à droite, puis Enregistrer avec l'encodage › UTF-8 with BOM.

⚠️ Pourquoi c'est important
Sans cet encodage, les noms de réseaux contenant des accents (par exemple Médiathèque) ne sont pas reconnus, sans qu'aucune erreur ne s'affiche.


Étape 2 — Installer le script sur le poste master

Sur votre poste master, avec un compte administrateur :

  1. Appuyez sur Windows + R, tapez gpedit.msc et validez.
  2. Dans l'arborescence, ouvrez Configuration ordinateur › Paramètres Windows › Scripts (démarrage/arrêt).
  3. Double-cliquez sur Démarrage, puis ouvrez l'onglet Scripts PowerShell.
  4. Cliquez sur Afficher les fichiers… : un dossier s'ouvre.
  5. Copiez setWifi_v2.ps1 dans ce dossier, puis fermez-le.
  6. Revenez à la fenêtre Démarrage, cliquez sur Ajouter…, sélectionnez setWifi_v2.ps1 et validez par OK.
  7. Cliquez sur Appliquer, puis OK.

💡 Bien choisir la section
Le script doit être déclaré dans Configuration ordinateur et non dans Configuration utilisateur. C'est ce qui lui permet de s'exécuter avant toute ouverture de session.


Étape 3 — Protéger vos clés Wi-Fi

Le script contient vos clés Wi-Fi en clair. Par défaut, le dossier dans lequel vous l'avez copié est lisible par tous les utilisateurs du poste. Cette étape réserve sa lecture aux administrateurs.

Elle est indispensable : ne la sautez pas.

  1. Faites un clic droit sur le menu Démarrer et ouvrez Terminal (administrateur) ou Windows PowerShell (administrateur).
  2. Copiez la commande suivante, collez-la et validez :
icacls "C:\Windows\System32\GroupPolicy\Machine\Scripts\Startup\setWifi_v2.ps1" /inheritance:r /grant:r *S-1-5-18:F *S-1-5-32-544:F
  1. Vérifiez le résultat avec :
icacls "C:\Windows\System32\GroupPolicy\Machine\Scripts\Startup\setWifi_v2.ps1"

Seuls AUTORITE NT\Système et BUILTIN\Administrateurs doivent apparaître.

💡 Bon à savoir
La commande fonctionne quelle que soit la langue de Windows : les comptes y sont désignés par leur identifiant système plutôt que par leur nom.


Étape 4 — Vérifier sur le poste master

Redémarrez le poste master, puis ouvrez une session.

Vos réseaux sont-ils installés ? Dans un terminal, tapez :

netsh wlan show profiles

Vos réseaux doivent apparaître dans la rubrique Profils Tous les utilisateurs.

Le script s'est-il bien déroulé ? Ouvrez le journal :

notepad C:\Windows\Logs\SetWifi\SetWifi.log

La dernière ligne indique le bilan du démarrage, par exemple :

[INFO ] Bilan : 2 profil(s) importé(s), 0 échec(s) sur 2 réseau(x) configuré(s).

Si un réseau est en échec, la raison est indiquée quelques lignes plus haut. Consultez la section En cas de problème.

Pour tout vérifier d'un coup, utilisez le script de contrôle Verifier-SetWifi.ps1 :

  1. Enregistrez-le sur le poste (par exemple dans C:\Temp), en UTF-8 avec BOM.
  2. Dans un terminal ouvert en tant qu'administrateur, lancez :
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\Temp\Verifier-SetWifi.ps1"

Le script affiche un résultat [OK] ou [KO] pour chaque point : présence et encodage du script, lignes d'exemple restantes, protection des droits, profils installés, dernier bilan et derniers événements. Il ne modifie rien sur le poste.

💡 Conseil
Ce script de contrôle n'a pas besoin de rester sur le master : supprimez-le après usage pour ne pas l'intégrer à votre version.


Étape 5 — Valider puis déployer

Suivez votre processus habituel de création de version :

  1. Créez une nouvelle version à partir de votre poste master.
  2. Déployez-la sur votre poste de validation.
  3. Redémarrez le poste de validation et vérifiez :
    • que le poste se connecte au Wi-Fi avant l'ouverture de session ;
    • que le réseau prioritaire est bien celui choisi en premier ;
    • que le journal se termine par 0 échec(s).
  4. Une fois la validation concluante, déployez la version sur votre parc.

💡 Conseil
Testez la connexion en débranchant le câble réseau du poste de validation : vous êtes ainsi certain que c'est bien le Wi-Fi qui est utilisé.


Faire évoluer vos réseaux

Vous souhaitez…Ce qu'il faut faire
Ajouter un réseauAjoutez une ligne dans $WifiNetworks sur le master, réenregistrez en UTF-8 avec BOM, puis créez et déployez une nouvelle version.
Changer une cléModifiez la clé sur votre borne et dans le script du master, puis créez et déployez une nouvelle version.
Changer l'ordre de prioritéRéordonnez les lignes dans $WifiNetworks, puis créez et déployez une nouvelle version.
Retirer un réseauSupprimez sa ligne dans le script. Le retrait de la liste ne supprime pas le profil déjà installé sur les postes : contactez votre interlocuteur Wisper pour organiser sa suppression.

⚠️ Après chaque modification
Vérifiez que les droits de l'étape 3 sont toujours en place. Si vous avez remplacé le fichier par une nouvelle copie, appliquez de nouveau la commande icacls.


Suivre vos postes

À chaque démarrage, le script laisse une trace à deux endroits.

Le fichier journal : C:\Windows\Logs\SetWifi\SetWifi.log. Il détaille chaque réseau traité. Les clés Wi-Fi n'y sont jamais écrites.

L'Observateur d'événements Windows : journal Application, source SetWifi.

IdentifiantSignification
1000Tout s'est bien passé
1001Avertissement : au moins un réseau n'a pas pu être installé
1002Erreur : détail du problème rencontré

💡 Pour un grand parc
Si vous disposez d'un outil de supervision qui collecte les événements Windows, filtrez sur la source SetWifi et les identifiants 1001 et 1002 : vous repérez immédiatement les postes concernés, sans avoir à vous y connecter.


Sécurité : les bonnes pratiques

Ce que le script fait pour vous :

  • il ne laisse jamais vos clés sur le disque en dehors du script lui-même ;
  • il n'écrit jamais vos clés dans les journaux ;
  • il refuse les réseaux mal renseignés plutôt que de créer une configuration incorrecte.

Ce qui relève de votre responsabilité :

  • Protéger le script : appliquez systématiquement l'étape 3.
  • Considérer votre master comme sensible : il contient vos clés Wi-Fi, au même titre que vos versions.
  • Changer vos clés régulièrement, et immédiatement en cas de perte ou de vol d'un poste.
  • Isoler les réseaux invités de votre réseau interne et les configurer en connexion manuelle (AutoConnect = $false).

ℹ️ À savoir
Avec une clé Wi-Fi partagée (WPA2 ou WPA3 « Personnel »), la clé doit être présente sur le poste pour qu'il se connecte. Un administrateur du poste peut donc toujours la retrouver. Pour les réseaux sensibles, une authentification WPA2/WPA3-Entreprise par certificat supprime toute clé partagée. Votre interlocuteur Wisper peut vous accompagner dans cette réflexion.


En cas de problème

Ce que vous constatezCause probableQue faire
Aucun fichier journalLe script ne s'exécute pasVérifiez qu'il est déclaré dans Configuration ordinateur › … › Démarrage, onglet Scripts PowerShell (étape 2).
« Droits administrateur requis »Script déclaré dans Configuration utilisateurDéclarez-le dans Configuration ordinateur.
« Aucune carte Wi-Fi active détectée »Carte absente, désactivée ou pilote manquantVérifiez la carte dans le Gestionnaire de périphériques. Sur un poste fixe sans Wi-Fi, ce message est normal.
« Clé invalide »Clé trop courte, trop longue ou avec accentsVérifiez la clé : 8 à 63 caractères, sans accent.
« SSID non renseigné »Une ligne d'exemple est restée dans la listeRemplacez ou supprimez les lignes contenant A_REMPLACER.
Nom de réseau avec caractères étrangesFichier non enregistré en UTF-8 avec BOMRéenregistrez le fichier avec le bon encodage (étape 1).
Réseau installé mais pas de connexionClé, nom ou type de sécurité différents de la borneComparez avec la configuration de la borne, majuscules comprises.
Échec uniquement sur un réseau WPA3Carte ou pilote non compatible WPA3Mettez à jour le pilote, ou utilisez WPA2 pour ce réseau.

Si le problème persiste, contactez votre interlocuteur Wisper en joignant le fichier C:\Windows\Logs\SetWifi\SetWifi.log du poste concerné.


En résumé

  1. Renseignez vos réseaux dans setWifi_v2.ps1 et enregistrez en UTF-8 avec BOM.
  2. Déclarez le script en démarrage ordinateur sur votre poste master.
  3. Protégez le script avec la commande icacls.
  4. Vérifiez sur le master : profils installés et journal sans échec.
  5. Validez sur un poste de validation, puis déployez sur votre parc.

Annexe : les scripts

⚠️ Avant d'utiliser ces scripts

  • Copiez chaque script en entier, de la première à la dernière ligne.
  • Enregistrez-le en UTF-8 avec BOM (voir étape 1).
  • Ne publiez jamais une copie de setWifi_v2.ps1 contenant vos clés réelles (ticket, messagerie, partage de fichiers).

Script 1 — setWifi_v2.ps1

Installe vos réseaux Wi-Fi à chaque démarrage du poste. Seul le bloc $WifiNetworks, en haut du script, est à modifier.

\xEF\xBB\xBF<#
.SYNOPSIS
    Déploie un ou plusieurs profils Wi-Fi (tous utilisateurs) au démarrage de la machine.

.DESCRIPTION
    Prévu pour un script de démarrage GPO (Configuration ordinateur), donc exécuté sous SYSTEM
    sans session interactive : aucune pause, aucune saisie, pas d'affichage console.
    Journalisation dans C:\Windows\Logs\SetWifi\SetWifi.log (avec rotation) et, pour les
    avertissements/erreurs/bilan, dans le journal d'événements "Application" (source SetWifi).

    Les réseaux sont déclarés dans $WifiNetworks. L'ordre du tableau définit la priorité
    de connexion (le premier est préféré).

    Codes de sortie : 0 = tout est OK (ou rien à faire), 1 = au moins un réseau en échec,
                      2 = erreur bloquante.

.NOTES
    Version : 2.0
    - Enregistrer ce fichier en UTF-8 AVEC BOM, sinon PowerShell 5.1 corrompt les caractères
      accentués des SSID/clés.
    - Ce fichier contient des clés Wi-Fi en clair : restreindre ses ACL (SYSTEM + Administrateurs).
#>

#Requires -Version 5.1
[CmdletBinding()]
param()

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

# =====================================================================
#  CONFIGURATION
# =====================================================================
#  SSID           : nom du réseau (1 à 32 octets)
#  Password       : clé WPA (8 à 63 caractères ASCII, ou 64 caractères hexadécimaux)
#  Authentication : 'WPA2PSK' | 'WPA3SAE' | 'open'   (défaut : WPA2PSK)
#  Hidden         : $true si le SSID n'est pas diffusé            (défaut : $false)
#  AutoConnect    : $true = automatique, $false = manuelle        (défaut : $true)
#
#  Exemples :
#  @{ SSID = 'Corp-WiFi';  Password = 'MotDePasse1'; Authentication = 'WPA2PSK' }
#  @{ SSID = 'Corp-WPA3';  Password = 'MotDePasse2'; Authentication = 'WPA3SAE' }
#  @{ SSID = 'Labo-cache'; Password = 'MotDePasse3'; Hidden = $true }
#  @{ SSID = 'Invites';    Authentication = 'open';  AutoConnect = $false }
# =====================================================================
$WifiNetworks = @(
    @{ SSID = 'A_REMPLACER'; Password = 'A_REMPLACER'; Authentication = 'WPA2PSK'; Hidden = $false; AutoConnect = $true }
    @{ SSID = 'A_REMPLACER'; Password = 'A_REMPLACER'; Authentication = 'WPA2PSK'; Hidden = $false; AutoConnect = $true }
)

$AppName      = 'SetWifi'
$AppVersion   = '2.0'
$LogDir       = Join-Path $env:SystemRoot "Logs\$AppName"
$LogFile      = Join-Path $LogDir "$AppName.log"
$MaxLogSizeKB = 1024     # au-delà, le log courant est renommé en .old
$WlanWaitSec  = 60       # attente max du service WLAN au démarrage
$EventSource  = $AppName

$script:EventLogReady = $false
$script:ExitCode      = 0

# =====================================================================
#  JOURNALISATION
# =====================================================================
function Initialize-Log {
    if (-not (Test-Path -LiteralPath $LogDir)) {
        New-Item -ItemType Directory -Path $LogDir -Force | Out-Null
    }
    if ((Test-Path -LiteralPath $LogFile) -and ((Get-Item -LiteralPath $LogFile).Length / 1KB -gt $MaxLogSizeKB)) {
        Move-Item -LiteralPath $LogFile -Destination "$LogFile.old" -Force
    }
    try {
        if (-not [System.Diagnostics.EventLog]::SourceExists($EventSource)) {
            New-EventLog -LogName Application -Source $EventSource
        }
        $script:EventLogReady = $true
    }
    catch {
        $script:EventLogReady = $false
    }
}

function Write-Log {
    param(
        [ValidateSet('INFO', 'WARN', 'ERROR')]
        [string]$Level = 'INFO',
        [Parameter(Mandatory)]
        [string]$Message,
        [switch]$ToEventLog
    )
    $line = '[{0}] [{1,-5}] {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Message
    try { Add-Content -LiteralPath $LogFile -Value $line -Encoding UTF8 } catch { }
    Write-Verbose $line

    if ($script:EventLogReady -and ($ToEventLog -or $Level -ne 'INFO')) {
        $type = switch ($Level) { 'ERROR' { 'Error' } 'WARN' { 'Warning' } default { 'Information' } }
        $id   = switch ($Level) { 'ERROR' { 1002 }    'WARN' { 1001 }      default { 1000 } }
        try { Write-EventLog -LogName Application -Source $EventSource -EventId $id -EntryType $type -Message $Message } catch { }
    }
}

# =====================================================================
#  OUTILS
# =====================================================================
function Test-IsAdmin {
    $id = [Security.Principal.WindowsIdentity]::GetCurrent()
    return ([Security.Principal.WindowsPrincipal]$id).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}

function Invoke-Netsh {
    param([Parameter(Mandatory)][string[]]$Arguments)
    # En PS 5.1, stderr redirigé + ErrorActionPreference=Stop lève une exception : on l'évite ici.
    $ErrorActionPreference = 'Continue'
    $output = & netsh.exe @Arguments 2>&1 | Out-String
    return [pscustomobject]@{ ExitCode = $LASTEXITCODE; Output = $output.Trim() }
}

function Wait-WlanService {
    $svc = Get-Service -Name WlanSvc -ErrorAction SilentlyContinue
    if (-not $svc) {
        Write-Log INFO 'Service WLAN AutoConfig (WlanSvc) absent : aucune action.'
        return $false
    }
    if ($svc.StartType -eq 'Disabled') {
        Write-Log WARN 'Service WlanSvc désactivé : impossible de déployer les profils Wi-Fi.'
        return $false
    }
    if ($svc.Status -ne 'Running') {
        Write-Log INFO "Service WlanSvc à l'état '$($svc.Status)', démarrage et attente (max $WlanWaitSec s)..."
        try {
            if ($svc.Status -eq 'Stopped') { Start-Service -Name WlanSvc }
            $svc.WaitForStatus('Running', [TimeSpan]::FromSeconds($WlanWaitSec))
        }
        catch {
            Write-Log ERROR "Le service WlanSvc n'a pas démarré : $($_.Exception.Message)"
            return $false
        }
    }
    return $true
}

function Get-WifiAdapters {
    # NdisPhysicalMedium 9 = Native 802.11 (indépendant de la langue du système)
    return @(Get-NetAdapter -Physical -ErrorAction SilentlyContinue |
        Where-Object { $_.NdisPhysicalMedium -eq 9 -and $_.Status -ne 'Disabled' })
}

function Get-NetValue {
    param([hashtable]$Net, [string]$Key, $Default)
    if ($Net.ContainsKey($Key) -and $null -ne $Net[$Key] -and "$($Net[$Key])" -ne '') { return $Net[$Key] }
    return $Default
}

# Retourne $null si la configuration est valide, sinon un message d'erreur.
function Test-WifiConfig {
    param([hashtable]$Net)

    $ssid = [string](Get-NetValue $Net 'SSID' '')
    $auth = [string](Get-NetValue $Net 'Authentication' 'WPA2PSK')
    $pass = [string](Get-NetValue $Net 'Password' '')

    if ($ssid -eq '') { return 'SSID vide.' }
    $ssidBytes = [Text.Encoding]::UTF8.GetByteCount($ssid)
    if ($ssidBytes -gt 32) { return "SSID trop long ($ssidBytes octets, maximum 32)." }
    if ($ssid -match 'A_REMPLACER|remplacer') { return 'SSID non renseigné (valeur exemple).' }

    if ($auth -notin @('WPA2PSK', 'WPA3SAE', 'open')) {
        return "Authentification '$auth' non supportée (WPA2PSK, WPA3SAE ou open)."
    }
    if ($auth -ne 'open') {
        if ($pass -match 'A_REMPLACER|remplacer') { return 'Clé non renseignée (valeur exemple).' }
        if ($pass -notmatch '^[\x20-\x7E]{8,63}$' -and $pass -notmatch '^[0-9A-Fa-f]{64}$') {
            return 'Clé invalide : 8 à 63 caractères ASCII imprimables, ou 64 caractères hexadécimaux.'
        }
    }
    return $null
}

function New-WlanProfileXml {
    param([hashtable]$Net)

    $ssid    = [string](Get-NetValue $Net 'SSID' '')
    $auth    = [string](Get-NetValue $Net 'Authentication' 'WPA2PSK')
    $pass    = [string](Get-NetValue $Net 'Password' '')
    $hidden  = [bool](Get-NetValue $Net 'Hidden' $false)
    $autoCon = [bool](Get-NetValue $Net 'AutoConnect' $true)

    # Échappement XML (&, <, >, ", ') : un caractère spécial ne casse plus le profil
    $ssidXml = [Security.SecurityElement]::Escape($ssid)
    # Forme hexadécimale du SSID : fiable même avec accents ou caractères spéciaux
    $ssidHex = -join ([Text.Encoding]::UTF8.GetBytes($ssid) | ForEach-Object { $_.ToString('X2') })
    $mode    = if ($autoCon) { 'auto' } else { 'manual' }
    $nonBc   = if ($hidden) { 'true' } else { 'false' }

    if ($auth -eq 'open') {
        $security = @"
            <authEncryption>
                <authentication>open</authentication>
                <encryption>none</encryption>
                <useOneX>false</useOneX>
            </authEncryption>
"@
    }
    else {
        $keyType = if ($pass -match '^[0-9A-Fa-f]{64}$') { 'networkKey' } else { 'passPhrase' }
        $passXml = [Security.SecurityElement]::Escape($pass)
        $security = @"
            <authEncryption>
                <authentication>$auth</authentication>
                <encryption>AES</encryption>
                <useOneX>false</useOneX>
            </authEncryption>
            <sharedKey>
                <keyType>$keyType</keyType>
                <protected>false</protected>
                <keyMaterial>$passXml</keyMaterial>
            </sharedKey>
"@
    }

    return @"
<?xml version="1.0" encoding="UTF-8"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
    <name>$ssidXml</name>
    <SSIDConfig>
        <SSID>
            <hex>$ssidHex</hex>
            <name>$ssidXml</name>
        </SSID>
        <nonBroadcast>$nonBc</nonBroadcast>
    </SSIDConfig>
    <connectionType>ESS</connectionType>
    <connectionMode>$mode</connectionMode>
    <MSM>
        <security>
$security
        </security>
    </MSM>
</WLANProfile>
"@
}

function Add-WifiProfile {
    param([hashtable]$Net)

    $ssid = [string]$Net.SSID
    $xml  = New-WlanProfileXml -Net $Net

    # Fichier temporaire au nom aléatoire, créé en exclusif (CreateNew) :
    # impossible à pré-créer ou à lire par un utilisateur standard, supprimé quoi qu'il arrive.
    $tmp = Join-Path $env:SystemRoot ('Temp\{0}.xml' -f [guid]::NewGuid().ToString('N'))
    try {
        $bytes = (New-Object System.Text.UTF8Encoding($false)).GetBytes($xml)
        $fs = [IO.File]::Open($tmp, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::None)
        try { $fs.Write($bytes, 0, $bytes.Length) } finally { $fs.Dispose() }

        $r = Invoke-Netsh -Arguments @('wlan', 'add', 'profile', "filename=$tmp", 'user=all')
        if ($r.ExitCode -ne 0) {
            throw "netsh a échoué (code $($r.ExitCode)) : $($r.Output)"
        }
        Write-Log INFO "Profil '$ssid' importé. netsh : $($r.Output)"
    }
    finally {
        if (Test-Path -LiteralPath $tmp) {
            Remove-Item -LiteralPath $tmp -Force -ErrorAction SilentlyContinue
            if (Test-Path -LiteralPath $tmp) {
                Write-Log WARN "Impossible de supprimer le fichier temporaire $tmp (contient une clé en clair)."
            }
        }
    }
}

function Set-WifiPriority {
    param([string[]]$Profiles, [object[]]$Adapters)
    foreach ($adapter in $Adapters) {
        $priority = 1
        foreach ($name in $Profiles) {
            $r = Invoke-Netsh -Arguments @('wlan', 'set', 'profileorder', "name=$name", "interface=$($adapter.Name)", "priority=$priority")
            if ($r.ExitCode -ne 0) {
                Write-Log WARN "Priorité $priority non appliquée pour '$name' sur '$($adapter.Name)' : $($r.Output)"
            }
            $priority++
        }
    }
}

# =====================================================================
#  PROGRAMME PRINCIPAL
# =====================================================================
function Invoke-Main {
    Write-Log INFO "===== $AppName v$AppVersion - $env:COMPUTERNAME - compte : $([Security.Principal.WindowsIdentity]::GetCurrent().Name) ====="

    if (-not (Test-IsAdmin)) {
        Write-Log ERROR 'Droits administrateur requis (le script doit tourner en script de démarrage ordinateur).'
        $script:ExitCode = 2
        return
    }
    if (-not (Wait-WlanService)) { return }

    $adapters = Get-WifiAdapters
    if ($adapters.Count -eq 0) {
        Write-Log INFO 'Aucune carte Wi-Fi active détectée : aucune action.'
        return
    }
    Write-Log INFO ("Carte(s) Wi-Fi : " + (($adapters | ForEach-Object { "$($_.Name) [$($_.InterfaceDescription)]" }) -join ', '))

    $seen     = @{}
    $added    = New-Object System.Collections.Generic.List[string]
    $failures = 0
    $index    = 0

    foreach ($net in $WifiNetworks) {
        $index++
        $label = "Réseau #$index"
        try {
            $err = Test-WifiConfig -Net $net
            if ($err) { throw $err }

            $ssid  = [string]$net.SSID
            $label = "Réseau #$index '$ssid'"
            if ($seen.ContainsKey($ssid)) { throw 'SSID en double dans la configuration.' }
            $seen[$ssid] = $true

            Add-WifiProfile -Net $net
            $added.Add($ssid)
        }
        catch {
            $failures++
            Write-Log ERROR "$label : $($_.Exception.Message)"
        }
    }

    if ($added.Count -gt 1) {
        Set-WifiPriority -Profiles $added.ToArray() -Adapters $adapters
    }

    $summary = "Bilan : $($added.Count) profil(s) importé(s), $failures échec(s) sur $($WifiNetworks.Count) réseau(x) configuré(s)."
    if ($failures -gt 0) {
        Write-Log WARN $summary
        $script:ExitCode = 1
    }
    else {
        Write-Log INFO $summary -ToEventLog
    }
}

try {
    Initialize-Log
    Invoke-Main
}
catch {
    Write-Log ERROR "Erreur bloquante : $($_.Exception.Message)"
    $script:ExitCode = 2
}
exit $script:ExitCode

Script 2 — Verifier-SetWifi.ps1

Contrôle l'installation de SetWifi sur un poste. À lancer en administrateur ; ne modifie rien.

<#
.SYNOPSIS
    Contrôle l'installation de SetWifi sur le poste (script, droits, profils, journal).
.NOTES
    À lancer dans une console PowerShell ouverte en tant qu'administrateur.
    Enregistrer en UTF-8 avec BOM. Ce script ne modifie rien sur le poste.
#>

$ScriptPath = 'C:\Windows\System32\GroupPolicy\Machine\Scripts\Startup\setWifi_v2.ps1'
$LogPath    = 'C:\Windows\Logs\SetWifi\SetWifi.log'

function Show-Result {
    param([string]$Label, [bool]$Ok, [string]$Detail = '')
    if ($Ok) { Write-Host "  [OK]   $Label" -ForegroundColor Green }
    else     { Write-Host "  [KO]   $Label" -ForegroundColor Red }
    if ($Detail) { Write-Host "         $Detail" -ForegroundColor Gray }
}

Write-Host "`n=== Vérification SetWifi - $env:COMPUTERNAME ===" -ForegroundColor Cyan

# 1. Présence et encodage du script
Write-Host "`n1. Script de démarrage" -ForegroundColor Cyan
$present = Test-Path -LiteralPath $ScriptPath
Show-Result 'Script présent dans le dossier de démarrage' $present $ScriptPath
if ($present) {
    $bytes = [IO.File]::ReadAllBytes($ScriptPath)
    $bom = $bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF
    Show-Result 'Encodage UTF-8 avec BOM' $bom
    $placeholder = Select-String -LiteralPath $ScriptPath -Pattern "SSID\s*=\s*'A_REMPLACER'" -Quiet
    Show-Result 'Aucune ligne d''exemple restante' (-not $placeholder)
}

# 2. Droits d'accès au script
Write-Host "`n2. Protection du script" -ForegroundColor Cyan
if ($present) {
    $acl = Get-Acl -LiteralPath $ScriptPath
    $allowed = @('S-1-5-18', 'S-1-5-32-544')   # SYSTEM, Administrateurs
    $others = @()
    foreach ($rule in $acl.Access) {
        try   { $sid = $rule.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value }
        catch { $sid = $rule.IdentityReference.Value }
        if ($sid -notin $allowed) { $others += $rule.IdentityReference.Value }
    }
    Show-Result 'Héritage des droits désactivé' $acl.AreAccessRulesProtected
    Show-Result 'Accès réservé à SYSTEM et aux Administrateurs' ($others.Count -eq 0) `
        $(if ($others.Count) { 'Comptes en trop : ' + (($others | Sort-Object -Unique) -join ', ') })
}
else {
    Show-Result 'Contrôle impossible : script absent' $false
}

# 3. Profils Wi-Fi installés
Write-Host "`n3. Profils Wi-Fi installés" -ForegroundColor Cyan
netsh.exe wlan show profiles | ForEach-Object { if ($_.Trim()) { Write-Host "         $_" } }

# 4. Dernier bilan du journal
Write-Host "`n4. Dernier démarrage" -ForegroundColor Cyan
if (Test-Path -LiteralPath $LogPath) {
    $last = Select-String -LiteralPath $LogPath -Pattern 'Bilan' | Select-Object -Last 1
    if ($last) {
        Show-Result 'Dernier bilan sans échec' ($last.Line -match ' 0 échec') $last.Line
    }
    else {
        Show-Result 'Aucun bilan dans le journal' $false $LogPath
    }
}
else {
    Show-Result 'Journal absent : le script ne s''est pas encore exécuté' $false $LogPath
}

# 5. Derniers événements Windows
Write-Host "`n5. Derniers événements (journal Application, source SetWifi)" -ForegroundColor Cyan
$events = Get-WinEvent -FilterHashtable @{ LogName = 'Application'; ProviderName = 'SetWifi' } -MaxEvents 5 -ErrorAction SilentlyContinue
if ($events) {
    $events | ForEach-Object { Write-Host ("         {0:yyyy-MM-dd HH:mm}  [{1}]  {2}" -f $_.TimeCreated, $_.Id, ($_.Message -split "`n")[0]) }
}
else {
    Write-Host '         Aucun événement trouvé.' -ForegroundColor Gray
}
Write-Host ''

Did this page help you?