Classe : 5TQ Informatique
Durée : 2 heures en classe
Travail : Individuel
Prérequis : Syntaxe PHP de base, include, Bootstrap 5, notions PowerShell
| # | Objectif |
|---|---|
| 1 | Comprendre ce qu'est un inventaire de parc informatique et pourquoi c'est indispensable |
| 2 | Collecter des informations systĂšme avec PowerShell et les envoyer vers un serveur |
| 3 | Créer une API REST minimaliste en PHP pour recevoir des données JSON |
| 4 | Afficher un dashboard et une fiche détaillée par machine |
Avant de coder, lis attentivement ces 5 notions. Elles sont le cĆur du projet.
Dans toute entreprise ou école, il y a des dizaines (voire des centaines) d'ordinateurs. Un inventaire de parc informatique permet de savoir :
Sans inventaire, impossible de planifier les mises à jour, de détecter une machine en panne ou de gérer les licences logicielles. C'est un outil de base pour tout technicien informatique.
Une API REST est un service web qui reçoit des requĂȘtes HTTP et rĂ©pond en JSON.
Dans ce projet, le serveur PHP joue le rÎle d'une API : il écoute les données envoyées par les postes.
[Poste A] ââPOST JSONâââ¶ http://serveur/inventaire/api/enregistrer.php
[Poste B] ââPOST JSONâââ¶ http://serveur/inventaire/api/enregistrer.php
[Poste C] ââPOST JSONâââ¶ http://serveur/inventaire/api/enregistrer.php
Le verbe HTTP utilisé est POST car on envoie des données (comme remplir un formulaire, mais depuis un script).
Le JSON (JavaScript Object Notation) est le format universel pour échanger des données entre systÚmes.
PowerShell peut produire du JSON avec ConvertTo-Json. PHP peut le lire avec json_decode().
{
"hostname": "PC-LABO-12",
"host": {
"ram_go": 8,
"constructeur": "Dell Inc."
}
}
En version 1, on stocke les données dans des fichiers JSON sur le serveur.
Chaque machine a son propre fichier, nommé d'aprÚs son hostname :
data/
âââ PC-LABO-01.json
âââ PC-LABO-02.json
âââ PC-LABO-12.json
đ Le hostname est l'identifiant unique de la machine. Si la mĂȘme machine envoie ses infos deux fois, le fichier est Ă©crasĂ© (mis Ă jour).
php://input pour lire un POST en JSONQuand PHP reçoit un formulaire HTML classique, on lit les données avec $_POST.
Mais quand on reçoit du JSON brut (depuis PowerShell ou Postman), les données ne passent pas par $_POST.
Il faut lire le corps brut de la requĂȘte avec :
$contenu = file_get_contents('php://input');
$donnees = json_decode($contenu, true); // true = tableau associatif
C'est la particularitĂ© des API REST : les donnĂ©es arrivent dans le "body" de la requĂȘte, pas dans les paramĂštres habituels.
Crée cette arborescence dans ton dossier htdocs de Laragon :
inventaire/
âââ index.php â Dashboard : liste de tous les postes
âââ poste.php â Fiche dĂ©taillĂ©e d'un poste
âââ includes/
â âââ header.php â En-tĂȘte HTML commune
â âââ footer.php â Fermeture HTML commune
âââ api/
â âââ enregistrer.php â Endpoint REST (reçoit le JSON)
âââ data/ â Créé automatiquement (un .json par poste)
â ïž Le dossier
data/sera créé automatiquement par PHP. Tu n'as pas besoin de le créer manuellement.
Voici exactement le JSON que le script PowerShell envoie au serveur.
Garde cette structure sous les yeux pendant tout le projet : elle détermine comment lire les données en PHP.
{
"hostname": "PC-LABO-12",
"date_scan": "2026-04-22 10:35:00",
"host": {
"nom_machine": "PC-LABO-12",
"constructeur": "Dell Inc.",
"modele": "OptiPlex 7080",
"ram_go": 16,
"hyperviseur": false,
"nom_dns": "PC-LABO-12.cepes.local",
"utilisateur": "CEPES\\eleve05"
},
"cpu": [
{
"modele": "Intel(R) Core(TM) i5-10400 CPU @ 2.90GHz",
"coeurs": 6,
"threads": 12,
"identifiant": "BFEBFBFF000A0653",
"designation_courte": "Intel64 Family 6 Model 165 Stepping 3",
"fabricant": "GenuineIntel"
}
],
"os": {
"nom": "Microsoft Windows 11 Ăducation",
"version": "10.0.22621",
"build": "22621",
"langues_mui": "fr-BE",
"organisation": "CEPES Jodoigne",
"architecture": "64 bits"
}
}
đĄ Remarque : La clĂ©
cpuest un tableau (entre[ ]) car un ordinateur peut avoir plusieurs processeurs. En PHP, on y accĂšde avec$poste['cpu'][0]pour le premier.
Durée estimée : 30 min
Le script PowerShell tourne sur chaque poste client. Il collecte les infos et les envoie au serveur.
Enregistre ce fichier sous inventaire_poste.ps1 :
# =====================================================
# INVENTAIRE POSTE - Script PowerShell
# Collecte les infos systĂšme et les envoie au serveur
# =====================================================
# ---- Informations générales du poste (host) ----
$sys = Get-WmiObject Win32_ComputerSystem
$donnees_host = @{
nom_machine = $env:COMPUTERNAME
constructeur = $sys.Manufacturer
modele = $sys.Model
ram_go = [math]::Round($sys.TotalPhysicalMemory / 1GB, 2)
hyperviseur = [bool]$sys.HypervisorPresent
nom_dns = $sys.DNSHostName
utilisateur = $sys.UserName
}
# ---- Processeur(s) (cpu) ----
# Get-WmiObject peut retourner plusieurs CPU â on boucle avec ForEach-Object
$donnees_cpu = @(
Get-WmiObject Win32_Processor | ForEach-Object {
@{
modele = $_.Name.Trim()
coeurs = $_.NumberOfCores
threads = $_.NumberOfLogicalProcessors
identifiant = $_.ProcessorId.Trim()
designation_courte = $_.Caption
fabricant = $_.Manufacturer
}
}
)
# ---- SystĂšme d'exploitation (os) ----
$sys_os = Get-WmiObject Win32_OperatingSystem
$donnees_os = @{
nom = $sys_os.Caption
version = $sys_os.Version
build = $sys_os.BuildNumber
langues_mui = ($sys_os.MUILanguages -join ", ")
organisation = $sys_os.Organization
architecture = $sys_os.OSArchitecture
}
# ---- Assemblage du JSON final ----
$payload = @{
hostname = $env:COMPUTERNAME
host = $donnees_host
cpu = $donnees_cpu
os = $donnees_os
}
# ConvertTo-Json -Depth 5 car on a des objets imbriqués (cpu est un tableau d'objets)
$json = $payload | ConvertTo-Json -Depth 5
# ---- Envoi vers le serveur PHP ----
# â ïž Remplace XX.XX par l'adresse IP du serveur (le PC du prof / le serveur Laragon)
$url = "http://192.168.1.XX/inventaire/api/enregistrer.php"
Write-Host "đĄ Envoi des donnĂ©es vers $url ..."
try {
$reponse = Invoke-RestMethod -Uri $url -Method POST -Body $json -ContentType "application/json"
Write-Host "â
SuccÚs ! Fichier enregistré : $($reponse.fichier)"
} catch {
Write-Host "â Erreur lors de l'envoi : $_"
}
Set-ExecutionPolicy RemoteSigned.\inventaire_poste.ps1api/enregistrer.php)Durée estimée : 20 min
Ce fichier est le récepteur. Il tourne sur le serveur et attend les données des postes.
<?php
// ================================================
// api/enregistrer.php
// Reçoit un JSON depuis un poste et le sauvegarde
// ================================================
// Indique que notre réponse sera du JSON
header('Content-Type: application/json');
// Ătape 1 : lire le corps brut de la requĂȘte POST
// (pas $_POST car les données arrivent en JSON, pas en formulaire)
$contenu = file_get_contents('php://input');
// Ătape 2 : dĂ©coder le JSON en tableau PHP
// Le "true" transforme les objets JSON en tableaux associatifs PHP
$donnees = json_decode($contenu, true);
// Ătape 3 : vĂ©rification minimale
// On vérifie que le JSON est valide ET qu'il contient un hostname
if ($donnees === null || empty($donnees['hostname'])) {
http_response_code(400); // 400 = Bad Request
echo json_encode(['erreur' => 'Données invalides ou hostname manquant']);
exit;
}
// Ătape 4 : ajouter la date du scan (cĂŽtĂ© serveur, plus fiable)
$donnees['date_scan'] = date('Y-m-d H:i:s');
// Ătape 5 : crĂ©er le dossier data/ s'il n'existe pas encore
if (!is_dir('data')) {
mkdir('data', 0777, true);
}
// Ătape 6 : nettoyer le hostname pour en faire un nom de fichier valide
// preg_replace remplace tout caractÚre NON alphanumérique (sauf - et _) par _
$hostname_propre = preg_replace('/[^a-zA-Z0-9\-_]/', '_', $donnees['hostname']);
$fichier = 'data/' . $hostname_propre . '.json';
// Ătape 7 : sauvegarder le JSON dans le fichier
// JSON_PRETTY_PRINT = lisible par un humain
// JSON_UNESCAPED_UNICODE = garde les accents (Ă©, Ă , ĂȘ...)
file_put_contents($fichier, json_encode($donnees, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
// Ătape 8 : rĂ©pondre au client PowerShell
http_response_code(200); // 200 = OK
echo json_encode([
'succes' => true,
'fichier' => $fichier,
'message' => 'Poste ' . $donnees['hostname'] . ' enregistré avec succÚs'
]);
Tu peux tester l'API avec Postman ou Insomnia :
POSThttp://localhost/inventaire/api/enregistrer.phpraw â JSON â colle l'exemple JSON de la section prĂ©cĂ©denteSi tout fonctionne, tu verras apparaĂźtre un fichier dans data/.
includes/)<?php // includes/header.php ?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Inventaire Parc Informatique</title>
</head>
<body>
<nav>
<a href="index.php">Dashboard</a>
</nav>
<main>
<?php // includes/footer.php ?>
</main>
<footer>
<p>Inventaire Parc Informatique â 5TQ Informatique</p>
</footer>
</body>
</html>
đĄ La mise en forme (CSS, BootstrapâŠ) est Ă ta charge. Ces includes fournissent uniquement la structure HTML minimale.
index.php)Durée estimée : 25 min
La page principale liste tous les postes inventoriés dans un tableau. Chaque ligne pointe vers la fiche détaillée.
<?php
// ============================================
// index.php â Dashboard de l'inventaire
// ============================================
include 'includes/header.php';
// Ătape 1 : lire tous les fichiers .json dans data/
// glob() retourne un tableau avec les chemins de tous les fichiers correspondants
$fichiers = glob('data/*.json');
// Ătape 2 : charger les donnĂ©es de chaque fichier
$postes = [];
foreach ($fichiers as $chemin) {
$contenu = file_get_contents($chemin);
$poste = json_decode($contenu, true);
if ($poste !== null) {
$postes[] = $poste;
}
}
// Ătape 3 : quelques chiffres rĂ©sumĂ©s
$nb_postes = count($postes);
$total_ram = 0;
foreach ($postes as $p) {
$total_ram += $p['host']['ram_go'] ?? 0;
}
$ram_moyenne = ($nb_postes > 0) ? round($total_ram / $nb_postes, 1) : 0;
?>
<h1>Dashboard â Parc Informatique</h1>
<p>Postes inventoriés : <strong><?php echo $nb_postes; ?></strong></p>
<p>RAM moyenne : <strong><?php echo $ram_moyenne; ?> Go</strong></p>
<hr>
<?php if ($nb_postes === 0): ?>
<p>Aucun poste inventorié. Lance le script PowerShell sur un poste !</p>
<?php else: ?>
<table border="1">
<thead>
<tr>
<th>Hostname</th>
<th>Constructeur</th>
<th>ModĂšle</th>
<th>RAM (Go)</th>
<th>OS</th>
<th>Dernier scan</th>
<th>Détail</th>
</tr>
</thead>
<tbody>
<?php foreach ($postes as $poste): ?>
<tr>
<td><?php echo htmlspecialchars($poste['hostname'] ?? ''); ?></td>
<td><?php echo htmlspecialchars($poste['host']['constructeur'] ?? ''); ?></td>
<td><?php echo htmlspecialchars($poste['host']['modele'] ?? ''); ?></td>
<td><?php echo htmlspecialchars($poste['host']['ram_go'] ?? ''); ?></td>
<td><?php echo htmlspecialchars($poste['os']['nom'] ?? ''); ?></td>
<td><?php echo htmlspecialchars($poste['date_scan'] ?? ''); ?></td>
<td>
<a href="poste.php?hostname=<?php echo urlencode($poste['hostname']); ?>">
Voir
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php include 'includes/footer.php'; ?>
poste.php)Durée estimée : 25 min
Cette page reçoit un hostname en paramÚtre GET, charge le fichier JSON correspondant et affiche toutes ses informations.
<?php
// ============================================
// poste.php â Fiche dĂ©taillĂ©e d'un poste
// ============================================
include 'includes/header.php';
// Ătape 1 : rĂ©cupĂ©rer le hostname depuis l'URL ex: poste.php?hostname=PC-LABO-12
$hostname = $_GET['hostname'] ?? '';
// Ătape 2 : sĂ©curiser le nom et construire le chemin du fichier
$hostname_propre = preg_replace('/[^a-zA-Z0-9\-_]/', '_', $hostname);
$fichier = 'data/' . $hostname_propre . '.json';
// Ătape 3 : vĂ©rifier que le fichier existe
if (!file_exists($fichier)) {
echo '<p>Poste introuvable.</p>';
include 'includes/footer.php';
exit;
}
// Ătape 4 : charger les donnĂ©es
$poste = json_decode(file_get_contents($fichier), true);
$host = $poste['host'] ?? [];
$cpu = $poste['cpu'] ?? [];
$os = $poste['os'] ?? [];
?>
<h1>Fiche poste : <?php echo htmlspecialchars($poste['hostname'] ?? ''); ?></h1>
<p>Dernier scan : <?php echo htmlspecialchars($poste['date_scan'] ?? ''); ?></p>
<a href="index.php">â Retour au dashboard</a>
<hr>
<!-- Informations générales -->
<h2>Informations générales</h2>
<table border="1">
<tr><th>Nom de la machine</th> <td><?php echo htmlspecialchars($host['nom_machine'] ?? ''); ?></td></tr>
<tr><th>Constructeur</th> <td><?php echo htmlspecialchars($host['constructeur'] ?? ''); ?></td></tr>
<tr><th>ModĂšle</th> <td><?php echo htmlspecialchars($host['modele'] ?? ''); ?></td></tr>
<tr><th>RAM totale</th> <td><?php echo htmlspecialchars($host['ram_go'] ?? ''); ?> Go</td></tr>
<tr><th>Hyperviseur</th> <td><?php echo $host['hyperviseur'] ? 'Oui' : 'Non'; ?></td></tr>
<tr><th>Nom DNS</th> <td><?php echo htmlspecialchars($host['nom_dns'] ?? ''); ?></td></tr>
<tr><th>Utilisateur connecté</th> <td><?php echo htmlspecialchars($host['utilisateur'] ?? ''); ?></td></tr>
</table>
<hr>
<!-- Processeur(s) -->
<h2>Processeur(s)</h2>
<?php if (empty($cpu)): ?>
<p>Aucune information CPU disponible.</p>
<?php else: ?>
<?php foreach ($cpu as $index => $proc): ?>
<h3>CPU #<?php echo $index + 1; ?></h3>
<table border="1">
<tr><th>ModĂšle</th> <td><?php echo htmlspecialchars($proc['modele'] ?? ''); ?></td></tr>
<tr><th>Fabricant</th> <td><?php echo htmlspecialchars($proc['fabricant'] ?? ''); ?></td></tr>
<tr><th>CĆurs</th> <td><?php echo htmlspecialchars($proc['coeurs'] ?? ''); ?></td></tr>
<tr><th>Threads</th> <td><?php echo htmlspecialchars($proc['threads'] ?? ''); ?></td></tr>
<tr><th>Identifiant</th> <td><?php echo htmlspecialchars($proc['identifiant'] ?? ''); ?></td></tr>
<tr><th>Désignation courte</th> <td><?php echo htmlspecialchars($proc['designation_courte'] ?? ''); ?></td></tr>
</table>
<?php endforeach; ?>
<?php endif; ?>
<hr>
<!-- SystĂšme d'exploitation -->
<h2>SystĂšme d'exploitation</h2>
<table border="1">
<tr><th>Nom</th> <td><?php echo htmlspecialchars($os['nom'] ?? ''); ?></td></tr>
<tr><th>Version</th> <td><?php echo htmlspecialchars($os['version'] ?? ''); ?></td></tr>
<tr><th>Build</th> <td><?php echo htmlspecialchars($os['build'] ?? ''); ?></td></tr>
<tr><th>Langues MUI</th> <td><?php echo htmlspecialchars($os['langues_mui'] ?? ''); ?></td></tr>
<tr><th>Organisation</th> <td><?php echo htmlspecialchars($os['organisation'] ?? ''); ?></td></tr>
<tr><th>Architecture</th> <td><?php echo htmlspecialchars($os['architecture'] ?? ''); ?></td></tr>
</table>
<?php include 'includes/footer.php'; ?>
| Fichier | Ce qu'il fait | Technologie |
|---|---|---|
inventaire_poste.ps1 |
Collecte les infos et envoie le JSON | PowerShell |
api/enregistrer.php |
Reçoit le JSON et le sauvegarde | PHP |
includes/header.php |
En-tĂȘte HTML commune | PHP/HTML |
includes/footer.php |
Pied de page HTML commun | PHP/HTML |
index.php |
Dashboard : tableau de tous les postes | PHP/HTML |
poste.php |
Fiche détaillée d'un poste | PHP/HTML |
Réponds à ces questions dans un commentaire en haut de ton fichier index.php :
php://input et pas $_POST pour lire le JSON ?cpu est-elle un tableau et pas un objet simple ?json_encode() et json_decode() ?| CritĂšre | Points |
|---|---|
Script PowerShell : collecte correcte de host, cpu, os |
/4 |
| API PHP : réception et sauvegarde du JSON avec validation | /4 |
Dashboard (index.php) : tableau fonctionnel + lien vers fiche |
/3 |
Fiche poste (poste.php) : affichage complet des 3 sections + lien retour |
/3 |
Utilisation correcte de include (header/footer) |
/2 |
| Qualité du code : commentaires, lisibilité | /2 |
| Questions de compréhension | /2 |
| Total | /20 |