Tutoriels gratuits et ouverts à toutes et à tous · conçus au Lycée Chevrollier — AngersÀ propos

Série « Parcours PowerShell » · partie 6 / 8

PowerShell niveau 5 — Administrer un poste Windows

Services, processus, disques, réseau, registre, journaux et comptes locaux : niveau 5 du parcours PowerShell, avec exercices corrigés et QCM.

Systèmes✎ B. Jousseaume📅 17 octobre 2026⏱ 6 h📶 Intermédiaire

1. Services

Get-Service                                     # tous les services
Get-Service -Name Spooler | Format-List *       # toutes les propriétés
Get-Service | Where-Object { $_.Status -eq "Stopped" -and $_.StartType -eq "Automatic" }

Start-Service -Name Spooler
Restart-Service -Name Spooler -Force
Stop-Service   -Name Spooler -WhatIf

Set-Service -Name Spooler -StartupType Manual   # nécessite les droits administrateur

Services automatiques arrêtés : un classique du diagnostic

Get-Service | Where-Object { $_.StartType -eq "Automatic" -and $_.Status -ne "Running" } |
    Select-Object Name, DisplayName, Status

2. Processus

Get-Process | Sort-Object CPU -Descending | Select-Object -First 5 Name, Id, CPU
Get-Process -Name notepad | Stop-Process -WhatIf
Start-Process notepad
Start-Process "notepad" -ArgumentList "C:\Temp\note.txt"

# Qui a lancé ce processus, et depuis quel exécutable ?
Get-CimInstance Win32_Process -Filter "Name = 'notepad.exe'" |
    Select-Object ProcessId, ExecutablePath, CommandLine

3. Système et matériel : CIM

CIM (successeur de WMI) donne accès à l’inventaire complet de la machine.

ClasseContenu
Win32_OperatingSystemVersion, date d’installation, dernier démarrage
Win32_ComputerSystemModèle, fabricant, mémoire totale, domaine
Win32_BIOSNuméro de série, version du BIOS
Win32_LogicalDiskDisques logiques, taille et espace libre
Win32_ProcessorProcesseur, nombre de cœurs
Win32_NetworkAdapterConfigurationConfiguration IP détaillée
$os = Get-CimInstance Win32_OperatingSystem
$os.LastBootUpTime
(New-TimeSpan -Start $os.LastBootUpTime).Days          # uptime en jours

(Get-CimInstance Win32_BIOS).SerialNumber              # numéro de série (inventaire)

Get-CimInstance Win32_LogicalDisk -Filter "DriveType = 3" |
    Select-Object DeviceID,
        @{N = "TailleGo"; E = { [math]::Round($_.Size / 1GB, 1) }},
        @{N = "LibreGo";  E = { [math]::Round($_.FreeSpace / 1GB, 1) }},
        @{N = "Libre%";   E = { [math]::Round($_.FreeSpace / $_.Size * 100, 1) }}

4. Réseau

CommandeRemplaceUsage
Get-NetIPConfigurationipconfigVue d’ensemble d’une interface
Get-NetIPAddressipconfig /allAdresses détaillées
Test-NetConnectionping, telnetTest d’accès, y compris sur un port
Resolve-DnsNamenslookupRésolution DNS
Get-NetTCPConnectionnetstatConnexions et ports en écoute
Get-NetAdapter—État des cartes réseau
Test-NetConnection -ComputerName srv-fichiers -Port 445
Test-NetConnection www.wikipedia.org -InformationLevel Detailed

Resolve-DnsName lycee-chevrollier.fr -Type A

# Ports en écoute et processus associé : diagnostic et sécurité
Get-NetTCPConnection -State Listen |
    Select-Object LocalAddress, LocalPort, OwningProcess,
        @{N = "Processus"; E = { (Get-Process -Id $_.OwningProcess).Name }} |
    Sort-Object LocalPort

5. Registre

Le registre s’explore comme un système de fichiers, avec les mêmes commandes.

Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" |
    Select-Object ProductName, DisplayVersion, CurrentBuild

# Écriture (console administrateur)
New-Item -Path "HKCU:\Software\Lycee" -Force | Out-Null
New-ItemProperty -Path "HKCU:\Software\Lycee" -Name "Salle" -Value "207" -PropertyType String -Force
Get-ItemPropertyValue -Path "HKCU:\Software\Lycee" -Name "Salle"
Remove-Item "HKCU:\Software\Lycee" -Recurse

6. Journaux d’événements

# Le journal système, 20 dernières entrées
Get-WinEvent -LogName System -MaxEvents 20 | Select-Object TimeCreated, Id, LevelDisplayName, Message

# Filtrage efficace (FilterHashtable filtre côté journal, donc rapide)
Get-WinEvent -FilterHashtable @{
    LogName   = "System"
    Level     = 2                     # 2 = erreur
    StartTime = (Get-Date).AddDays(-7)
} | Group-Object ProviderName | Sort-Object Count -Descending | Select-Object Count, Name

# Échecs d'ouverture de session (journal Sécurité, console administrateur)
Get-WinEvent -FilterHashtable @{ LogName = "Security"; Id = 4625; StartTime = (Get-Date).AddDays(-1) } |
    Select-Object TimeCreated, @{N = "Compte"; E = { $_.Properties[5].Value }}

7. Comptes locaux et logiciels

Get-LocalUser | Select-Object Name, Enabled, LastLogon, PasswordExpires
Get-LocalGroupMember -Group "Administrateurs"

New-LocalUser -Name "tpuser" -Description "Compte de TP" -NoPassword
Add-LocalGroupMember -Group "Utilisateurs" -Member "tpuser"
Disable-LocalUser -Name "tpuser"
Remove-LocalUser -Name "tpuser"
# Logiciels installés : le registre est plus fiable que Win32_Product
$chemins = @(
    "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
    "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
)
Get-ItemProperty $chemins -ErrorAction SilentlyContinue |
    Where-Object DisplayName |
    Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
    Sort-Object DisplayName

8. Mini-projet : le rapport de diagnostic du poste

9. Bilan

Exercices corrigés

Tous ces exercices se pratiquent sur une machine virtuelle de test. Les commandes de lecture sont sans danger, mais les habitudes se prennent sur la maquette.

Exercice 5.1 — Services et processus

Voir le corrigé
# 1.
Get-Service | Where-Object { $_.StartType -eq "Automatic" -and $_.Status -ne "Running" } |
    Select-Object Name, DisplayName, Status |
    Export-Csv "$env:USERPROFILE\Desktop\services-ko.csv" -Delimiter ";" -Encoding UTF8 -NoTypeInformation

# 2.
Get-Process -IncludeUserName |
    Sort-Object WorkingSet -Descending |
    Select-Object -First 10 Name, Id, UserName,
        @{N = "Mo"; E = { [math]::Round($_.WorkingSet / 1MB, 1) }}

# 3.
function Restart-ServiceSafe {
    [CmdletBinding(SupportsShouldProcess)]
    param([Parameter(Mandatory)][string]$Nom)

    $service = Get-Service -Name $Nom -ErrorAction SilentlyContinue
    if (-not $service) {
        Write-Warning "Service $Nom introuvable"
        return
    }
    if ($service.Status -eq "Running") {
        Write-Verbose "$Nom fonctionne déjà"
        return
    }
    if ($PSCmdlet.ShouldProcess($Nom, "Démarrage")) {
        try {
            Start-Service -Name $Nom -ErrorAction Stop
            "$(Get-Date -Format s) - $Nom démarré par $env:USERNAME" | Add-Content services.log
        }
        catch { Write-Error "Échec du démarrage de $Nom : $($_.Exception.Message)" }
    }
}

Explication

-IncludeUserName exige une console administrateur : sans cela, la commande échoue avec un message explicite. Restart-ServiceSafe illustre le bon ordre : vérifier l’existence, vérifier l’état, agir seulement si nécessaire.

Exercice 5.2 — Diagnostic réseau

Voir le corrigé
# 1.
Get-NetIPConfiguration | Select-Object InterfaceAlias, IPv4Address, IPv4DefaultGateway, DNSServer

# 2.
foreach ($site in "www.wikipedia.org", "www.education.gouv.fr") {
    $ok = (Test-NetConnection $site -Port 443 -WarningAction SilentlyContinue).TcpTestSucceeded
    "{0} : {1}" -f $site, $(if ($ok) { "OK" } else { "Échec" })
}

# 3.
Get-NetTCPConnection -State Listen |
    Select-Object LocalPort, OwningProcess,
        @{N = "Processus"; E = { (Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).Name }} |
    Sort-Object LocalPort

# 4.
function Test-SiteWeb {
    param([string[]]$Domaine)
    foreach ($d in $Domaine) {
        $dns = $null
        try { $dns = Resolve-DnsName $d -Type A -ErrorAction Stop } catch {}
        $test = Test-NetConnection $d -Port 443 -WarningAction SilentlyContinue
        [PSCustomObject]@{
            Site       = $d
            DnsOk      = [bool]$dns
            Port443    = $test.TcpTestSucceeded
            TempsMs    = $test.PingReplyDetails.RoundtripTime
        }
    }
}
Test-SiteWeb -Domaine "www.wikipedia.org", "serveur-inexistant.local" | Format-Table -AutoSize

Explication

Test-NetConnection renvoie un objet complet : TcpTestSucceeded pour le port, PingSucceeded pour ICMP, RoundtripTime pour le délai. -WarningAction SilentlyContinue masque l’avertissement quand le ping échoue mais que le port répond.

Exercice 5.3 — Audit du poste

Voir le corrigé
# 1.
Get-LocalUser | Where-Object Enabled | Select-Object Name, LastLogon
Get-LocalGroupMember -Group "Administrateurs"

# 2.
Get-WinEvent -FilterHashtable @{ LogName = "System"; Level = 2; StartTime = (Get-Date).AddDays(-7) } |
    Group-Object ProviderName | Sort-Object Count -Descending | Select-Object Count, Name

# 3.
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" | ForEach-Object {
    if (Test-Path $_) {
        Get-ItemProperty $_ | Select-Object -Property * -ExcludeProperty PS*
    }
}

# 4.
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
                 "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
    Where-Object DisplayName |
    Select-Object DisplayName, DisplayVersion, Publisher |
    Sort-Object DisplayName |
    Export-Csv "$env:USERPROFILE\Desktop\logiciels.csv" -Delimiter ";" -Encoding UTF8 -NoTypeInformation

Explication

Get-WinEvent -FilterHashtable filtre côté journal, ce qui est incomparablement plus rapide que Get-WinEvent | Where-Object. La recherche dans les descriptions utilise -match avec (?i) pour ignorer la casse.

Pour aller plus loin

Ajoute -ErrorAction SilentlyContinue sur Get-WinEvent : si aucun événement ne correspond, la cmdlet considère cela comme une erreur.

Mini-projet

Voir le corrigé
[CmdletBinding()]
param(
    [string]$CheminRapport = "$env:USERPROFILE\Desktop\diagnostic_$env:COMPUTERNAME.html",
    [int]$SeuilDisquePct = 15
)

$os = Get-CimInstance Win32_OperatingSystem
$identite = [PSCustomObject]@{
    Machine    = $env:COMPUTERNAME
    Systeme    = $os.Caption
    Version    = $os.Version
    UptimeJours = (New-TimeSpan -Start $os.LastBootUpTime).Days
    Date       = Get-Date
}

$disques = Get-CimInstance Win32_LogicalDisk -Filter "DriveType = 3" |
    Select-Object DeviceID,
        @{N = "LibreGo"; E = { [math]::Round($_.FreeSpace / 1GB, 1) }},
        @{N = "LibrePct"; E = { [math]::Round($_.FreeSpace / $_.Size * 100, 1) }},
        @{N = "Alerte";   E = { if (($_.FreeSpace / $_.Size * 100) -lt $SeuilDisquePct) { "OUI" } else { "" } }}

$servicesKo = Get-Service | Where-Object { $_.StartType -eq "Automatic" -and $_.Status -ne "Running" } |
    Select-Object Name, DisplayName
$processus = Get-Process | Sort-Object WorkingSet -Descending | Select-Object -First 5 Name, Id,
    @{N = "Mo"; E = { [math]::Round($_.WorkingSet / 1MB, 1) }}
$reseau = Get-NetIPConfiguration | Select-Object InterfaceAlias, IPv4Address, IPv4DefaultGateway
$internet = (Test-NetConnection www.wikipedia.org -Port 443 -WarningAction SilentlyContinue).TcpTestSucceeded
$erreurs = Get-WinEvent -FilterHashtable @{ LogName = "System"; Level = 2; StartTime = (Get-Date).AddDays(-7) } -ErrorAction SilentlyContinue |
    Group-Object ProviderName | Sort-Object Count -Descending | Select-Object Count, Name -First 10
$admins = Get-LocalGroupMember -Group "Administrateurs" | Select-Object Name, ObjectClass

$style = "<style>body{font-family:Segoe UI,sans-serif;margin:2rem}table{border-collapse:collapse;margin-bottom:2rem}
th{background:#1A1A1A;color:#fff;text-align:left;padding:6px 10px}td{border-bottom:1px solid #ccc;padding:4px 10px}h2{margin-top:2rem}</style>"

$corps = @(
    ($identite   | ConvertTo-Html -Fragment -PreContent "<h2>Identité</h2>")
    ($disques    | ConvertTo-Html -Fragment -PreContent "<h2>Disques</h2>")
    ($servicesKo | ConvertTo-Html -Fragment -PreContent "<h2>Services automatiques arrêtés</h2>")
    ($processus  | ConvertTo-Html -Fragment -PreContent "<h2>Processus les plus gourmands</h2>")
    ($reseau     | ConvertTo-Html -Fragment -PreContent "<h2>Réseau (Internet : $internet)</h2>")
    ($erreurs    | ConvertTo-Html -Fragment -PreContent "<h2>Erreurs système (7 jours)</h2>")
    ($admins     | ConvertTo-Html -Fragment -PreContent "<h2>Administrateurs locaux</h2>")
) -join "`n"

ConvertTo-Html -Head $style -Body $corps -Title "Diagnostic $env:COMPUTERNAME" |
    Out-File $CheminRapport -Encoding utf8

[PSCustomObject]@{
    Machine         = $env:COMPUTERNAME
    ServicesArretes = $servicesKo.Count
    DisquesEnAlerte = ($disques | Where-Object Alerte).Count
    InternetOk      = $internet
    Rapport         = $CheminRapport
}

Explication

Le rapport assemble des fragments HTML produits séparément. Chaque section reste un objet jusqu’au dernier moment, ce qui permet à la fois le rendu HTML et la sortie d’un résumé exploitable.

Pour aller plus loin

Pour un rendu plus soigné, remplace le CSS en ligne par une feuille de style commune à tous tes rapports : ils deviennent reconnaissables et lisibles par n’importe quel collègue.

🧠 Testez vos connaissances

5 questions. Répondez puis cliquez sur « Corriger ». Rien n’est enregistré : c’est de l’auto-évaluation.

1. Quelle commande remplace netstat pour voir les ports en écoute ?

💬 Avec OwningProcess, on retrouve le processus qui écoute, ce qui est essentiel en diagnostic comme en sécurité.

2. Pourquoi éviter Get-CimInstance Win32_Product pour lister les logiciels ?

💬 On préfère lire les clés Uninstall du registre : instantané et sans effet de bord.

3. Que signifie l’événement de sécurité 4625 ?

💬 4624 correspond au succès, 4720 à une création de compte et 1102 à l’effacement du journal de sécurité.

4. Avant de modifier une clé de registre, que faut-il faire ?

💬 On travaille aussi de préférence dans HKCU: tant que la modification ne concerne pas toute la machine.

5. Pourquoi surveiller le groupe Administrateurs local ?

💬 C’est souvent le premier endroit où apparaît la trace d’une compromission.

© 2026 Bruno Jousseaume. Ce cours est publié sous licence CC BY-NC-SA 4.0 : vous pouvez le partager et l’adapter en citant l’auteur et un lien vers cette page, sans usage commercial, et en partageant vos adaptations sous la même licence.

Pour aller plus loin

Bastion Guacamole : dépanner les connexions RDP

Une démarche méthodique pour diagnostiquer une connexion RDP qui échoue à travers le bastion : journal de guacd, tableau de diagnostic, NLA et NTLM, réseau, fiche de suivi d’incident.

Avancé⏱ 1 h 30 · 8 octobre 2026