# Déployer un site Web dynamique connecté à Azure MySQL > **Guide pas à pas** — VM Ubuntu + Nginx/Apache + PHP + Azure Database for MySQL Flexible Server > > Ce guide documente chaque étape du déploiement, de la création des ressources Azure jusqu'à l'interface finale du site. --- ## Table des matières 1. [Créer la base de données Azure MySQL](#1--créer-la-base-de-données-azure-mysql) 2. [Créer la machine virtuelle (VM)](#2--créer-la-machine-virtuelle-vm) 3. [Résoudre le problème de connexion SSH](#3--résoudre-le-problème-de-connexion-ssh) 4. [Installer et configurer le serveur web](#4--installer-et-configurer-le-serveur-web) 5. [Configurer le réseau (NSG / pare-feu)](#5--configurer-le-réseau-nsg--pare-feu) 6. [Déployer l'application PHP](#6--déployer-lapplication-php) 7. [Configurer la base de données](#7--configurer-la-base-de-données) 8. [Construire le dashboard (interface finale)](#8--construire-le-dashboard-interface-finale) --- ## 1 — Créer la base de données Azure MySQL ### 1.1 — Accéder au portail Azure et rechercher le service Dans le portail Azure, recherchez **"Azure Database for MySQL"** puis cliquez sur **Créer**. ![Recherche du service Azure Database for MySQL dans le portail](https://hackmd.io/_uploads/S1Bhv78HWx.png) > Ici, on accède au service Azure Database for MySQL depuis la barre de recherche du portail Azure. ![Sélection du type de serveur flexible](https://hackmd.io/_uploads/ryhVuQUHWe.png) > On choisit le mode **Serveur flexible**, qui est la version recommandée par Azure (plus économique et plus performante). ### 1.2 — Configurer le serveur MySQL ![Formulaire de création du serveur MySQL — informations de base](https://hackmd.io/_uploads/BkQO_78SZe.png) > Remplissez les champs principaux : nom du serveur (ex: `mysqlwebapp123`), la région, et les identifiants admin (utilisateur + mot de passe). #### Création avancée ![Options de création avancée du serveur](https://hackmd.io/_uploads/ByILKmIHbg.png) > Dans les options avancées, vous pouvez ajuster le tier de calcul, le stockage et la version MySQL. Pour un projet de test, le tier **Burstable (B1ms)** est suffisant. ![Configuration du calcul et du stockage](https://hackmd.io/_uploads/rk2UY7LHbg.png) > Sélectionnez la taille de calcul et le stockage adaptés à votre budget. Le mode Burstable est le plus économique. ### 1.3 — Configuration réseau ![Paramètres réseau du serveur MySQL](https://hackmd.io/_uploads/HkFAFQLB-e.png) > Configuration de l'accès réseau au serveur de base de données. ![Options de connectivité](https://hackmd.io/_uploads/rkXBFQLHWx.png) > On choisit l'accès public pour pouvoir se connecter depuis notre VM. ![Règles de pare-feu de la base de données](https://hackmd.io/_uploads/BypucQLSZe.png) > Les règles de pare-feu définissent quelles adresses IP peuvent accéder au serveur MySQL. ![Récapitulatif de la configuration réseau](https://hackmd.io/_uploads/rJeccmUrZx.png) > **Important :** On clique sur **"Créer un serveur sans règle de pare-feu"** car la VM n'est pas encore créée. On ajoutera la règle plus tard avec l'IP publique de la VM. ### 1.4 — Validation et création ![Récapitulatif avant création du serveur MySQL](https://hackmd.io/_uploads/ByE3iQLBbl.png) > Vérifiez le récapitulatif de toutes les options avant de cliquer sur **Créer**. ![Déploiement du serveur MySQL en cours](https://hackmd.io/_uploads/BkSAi7UB-l.png) > Le déploiement prend environ 2-5 minutes. Une fois terminé, le serveur est prêt. --- ## 2 — Créer la machine virtuelle (VM) ### 2.1 — Configuration de base ![Formulaire de création de la VM — onglet Basics](https://hackmd.io/_uploads/r1IIRmIB-l.png) > On crée une VM Ubuntu Server. Choisissez la même région que votre serveur MySQL pour minimiser la latence. ![Choix de l'image et de la taille de la VM](https://hackmd.io/_uploads/HJ3IRXUSWx.png) > Sélectionnez **Ubuntu Server 22.04 LTS** comme image. Pour la taille, une **B1s** ou **B2s** suffit largement pour un site web léger. ### 2.2 — Configuration du disque ![Sélection du type de disque](https://hackmd.io/_uploads/BJonJNIHWl.png) > **Astuce économique :** Changez le disque de **Premium SSD** à **Standard SSD** pour réduire les coûts. La différence de performance est négligeable pour notre usage. ### 2.3 — Configuration réseau ![Configuration du réseau virtuel et de l'IP publique](https://hackmd.io/_uploads/BycZg48Bbe.png) > Azure crée automatiquement un réseau virtuel, un sous-réseau et une IP publique. Laissez les valeurs par défaut. ![Règles de port entrant (NSG)](https://hackmd.io/_uploads/rJIhvNLB-e.png) > Autorisez le **port SSH (22)** dans les règles de sécurité. On ajoutera le port HTTP (80) après. ![Paramètres de gestion de la VM](https://hackmd.io/_uploads/HklRPVUr-x.png) > Options de monitoring et de gestion. Les valeurs par défaut conviennent pour un projet de test. ### 2.4 — Authentification SSH ![Configuration de la clé SSH](https://hackmd.io/_uploads/rkn8BPLSZl.png) > Choisissez l'authentification par **clé SSH** (plus sécurisé qu'un mot de passe). Azure peut générer la paire de clés pour vous. ![Téléchargement de la clé privée SSH](https://hackmd.io/_uploads/B1cT5NUrWl.png) > **Téléchargez et conservez la clé privée** (fichier `.pem`). Vous en aurez besoin pour chaque connexion SSH. Ne la partagez jamais. ### 2.5 — Validation et création ![Récapitulatif et validation de la VM](https://hackmd.io/_uploads/rJp3lBLSbx.png) > Vérifiez le récapitulatif puis cliquez sur **Créer**. Le déploiement prend environ 1-2 minutes. --- ## 3 — Résoudre le problème de connexion SSH ### 3.1 — Le problème Après la création de la VM, la connexion SSH peut échouer avec un timeout. > ❌ **Rien n'atteint la VM depuis Internet** > (`TcpTestSucceeded = False` + Ping timeout) > > Ce n'est PAS un problème de clé SSH, ni de Linux, ni du port 22. > 👉 **C'est le routage public Azure qui est cassé** — symptôme classique d'une IP publique mal attachée (fréquent après la création d'Azure Bastion). ### 3.2 — La solution : recréer l'IP publique Dissociez l'IP publique actuelle de la carte réseau de la VM, puis créez-en une nouvelle. ![Dissociation de l'ancienne IP et création d'une nouvelle](https://hackmd.io/_uploads/S1CaxHUB-g.png) > Dans les paramètres de la carte réseau (NIC), on dissocie l'ancienne IP publique et on en attache une nouvelle. ### 3.3 — Connexion réussie ![Connexion SSH réussie après le changement d'IP](https://hackmd.io/_uploads/SJQjgSLB-l.png) > La connexion SSH fonctionne maintenant avec la nouvelle IP publique. ![Terminal SSH connecté à la VM Ubuntu](https://hackmd.io/_uploads/Hk_lbBISWg.png) > On est connecté à la VM via SSH. On peut maintenant installer le serveur web. --- ## 4 — Installer et configurer le serveur web ### 4.1 — Mettre Ubuntu à jour Dans le terminal SSH : ```bash sudo apt update && sudo apt upgrade -y ``` ### 4.2 — Installer Nginx ```bash sudo apt install nginx -y sudo systemctl start nginx sudo systemctl enable nginx ``` ### 4.3 — Tester l'accès web Ouvrez dans votre navigateur : `http://VOTRE_IP_PUBLIQUE` Vous devriez voir la page **"Welcome to nginx"**. ![Page d'accueil Nginx — le serveur web fonctionne](https://hackmd.io/_uploads/S1w2-rIBZx.png) > ✅ Si vous voyez cette page, le serveur web est opérationnel. ### 4.4 — Si la page ne s'affiche pas ![Échec de l'accès — la page ne charge pas](https://hackmd.io/_uploads/B1jNfBLHWl.png) > ❌ Si la page ne s'affiche pas, c'est un problème de règles réseau (NSG). Le port 80 n'est pas ouvert. --- ## 5 — Configurer le réseau (NSG / pare-feu) ### 5.1 — Ouvrir le port HTTP (80) dans le NSG ![Accéder aux paramètres réseau de la VM](https://hackmd.io/_uploads/S1UBzBLrbx.png) > Allez dans les paramètres réseau de la VM pour modifier les règles du NSG (Network Security Group). ![Modification de la règle de sécurité entrante](https://hackmd.io/_uploads/HyqBwHUrWg.png) > Après avoir changé d'IP publique, il faut aussi **mettre à jour les règles NSG** pour autoriser le trafic HTTP sur le port 80. ### 5.2 — Ajouter la règle de pare-feu pour MySQL ![Ajout de la règle pour le port 80](https://hackmd.io/_uploads/H1MhPrLHbl.png) > Créez une nouvelle règle entrante autorisant le **port 80 (HTTP)** depuis n'importe quelle source. ### 5.3 — Mettre à jour le pare-feu de la base de données ![Mise à jour des règles de pare-feu Azure MySQL](https://hackmd.io/_uploads/HJSViH8rbl.png) > N'oubliez pas d'ajouter l'**IP publique de la VM** dans les règles de pare-feu du serveur Azure MySQL. Sans ça, PHP ne pourra pas se connecter à la base. ### 5.4 — Vérification — Nginx accessible ![Page Nginx accessible après configuration du NSG](https://hackmd.io/_uploads/ryuHoBUS-l.png) > ✅ Après la configuration correcte du NSG, la page Nginx s'affiche dans le navigateur. --- ## 6 — Déployer l'application PHP ### 6.1 — Préparer le dossier du site ```bash cd /var/www/html sudo mkdir abder-blog sudo chown -R $USER:$USER abder-blog cd abder-blog ``` ### 6.2 — Créer le fichier de configuration de la base de données ```bash nano config.php ``` Collez le contenu suivant (adaptez avec vos identifiants) : ```php <?php $host = "mysqlwebapp123.mysql.database.azure.com"; $db = "abder_blog"; $user = "abderapp"; $pass = "VotreMotDePasse"; $ssl_ca = "/etc/mysql/certs/DigiCertGlobalRootCA.crt.pem"; try { $pdo = new PDO( "mysql:host=$host;dbname=$db;charset=utf8mb4", $user, $pass, [ PDO::MYSQL_ATTR_SSL_CA => $ssl_ca, PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ] ); } catch (PDOException $e) { die("Erreur DB: " . $e->getMessage()); } ``` > 🔒 **Sécurité** : ne publiez jamais ce fichier sur un dépôt public. Il contient vos identifiants. ### 6.3 — Créer la page d'accueil (test de connexion) ```bash nano index.php ``` ```php <?php require "config.php"; $stmt = $pdo->query("SELECT COUNT(*) FROM posts"); $posts = $stmt->fetchColumn(); ?> <!DOCTYPE html> <html> <head><title>Abder Blog</title></head> <body> <h1>Bienvenue sur Abder Blog 🚀</h1> <p>Connexion MySQL : OK</p> <p>Articles dans la base : <?= $posts ?></p> </body> </html> ``` ### 6.4 — Test dans le navigateur Ouvrez : `http://VOTRE_IP_PUBLIQUE/abder-blog/` ![Page de test — connexion MySQL réussie](https://hackmd.io/_uploads/rkdijSIrbg.png) > ✅ La page affiche le nombre d'articles dans la base. La connexion entre PHP et Azure MySQL fonctionne. --- ## 7 — Configurer la base de données ### 7.1 — Vérifications dans le portail Azure ![Accès à la base de données via le portail Azure](https://hackmd.io/_uploads/HJmnoS8HWg.png) > On peut vérifier l'état de notre serveur MySQL depuis le portail Azure. ![Vue d'ensemble du serveur Azure MySQL](https://hackmd.io/_uploads/HkSpnHUr-g.png) > Le dashboard du serveur montre les métriques de connexion, le statut et les paramètres. ![Configuration du pare-feu du serveur MySQL](https://hackmd.io/_uploads/rJynWL8H-g.png) > Vérifiez que l'IP de votre VM est bien autorisée dans les règles de pare-feu du serveur. ![Paramètres du serveur MySQL](https://hackmd.io/_uploads/Skz3_PLBbe.png) > Les paramètres de configuration du serveur (version MySQL, SSL, etc.). ![Connexion réussie à la base de données](https://hackmd.io/_uploads/B1Hi_P8S-g.png) > Confirmation que la base de données est bien accessible et fonctionnelle. ### 7.2 — Se connecter à MySQL depuis la VM ```bash mysql -h mysqlwebapp123.mysql.database.azure.com -u abderapp -p abder_blog ``` ### 7.3 — Créer la table `messages` et insérer des données ```sql CREATE TABLE messages ( id INT PRIMARY KEY AUTO_INCREMENT, contenu VARCHAR(255), date_creation TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); INSERT INTO messages (contenu) VALUES ('Message 1'), ('Message 2'), ('Message 3'), ('Message 4'), ('Message 5'); SELECT * FROM messages; ``` Puis quittez : `exit;` ### 7.4 — Vérifier les données sur le site ![Le site affiche les messages de la base de données](https://hackmd.io/_uploads/BJROxuIrWe.png) > ✅ Les messages insérés en base apparaissent maintenant sur le site web. ![Liste des messages affichée dans le navigateur](https://hackmd.io/_uploads/BJOFlu8S-l.png) > L'interface commence à prendre forme avec les données dynamiques provenant d'Azure MySQL. ![Interface du site avec les données](https://hackmd.io/_uploads/SyAs-dLr-x.png) > Vue de l'interface du site affichant les messages. On peut voir le CRUD basique fonctionner. ### 7.5 — Installer l'extension PHP mbstring Si vous rencontrez une erreur `mb_strlen()` : ```bash sudo apt update sudo apt install -y php-mbstring sudo systemctl restart apache2 ``` Vérification : ```bash php -m | grep mbstring ``` ![Extension mbstring installée avec succès](https://hackmd.io/_uploads/rkp5LOwHZl.png) > ✅ L'extension `mbstring` est active. Les fonctions multibyte PHP fonctionnent correctement. ### 7.6 — Recréer la table avec l'encodage correct Si nécessaire, recréez la table avec l'encodage `utf8mb4` pour supporter les emojis et caractères spéciaux : ```bash mysql -h mysqlwebapp123.mysql.database.azure.com -u abderapp -p ``` ```sql SHOW DATABASES; USE abder_blog; SHOW TABLES; CREATE TABLE IF NOT EXISTS messages ( id INT PRIMARY KEY AUTO_INCREMENT, contenu VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, date_creation TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` ![Table messages créée avec l'encodage utf8mb4](https://hackmd.io/_uploads/rJhuPdwSZl.png) > ✅ La table `messages` est maintenant créée avec l'encodage `utf8mb4_unicode_ci`, compatible avec les emojis. --- ## 8 — Construire le dashboard (interface finale) ### 8.1 — Architecture des fichiers ``` /var/www/html/abder-blog/ ├── public/ │ ├── index.php ← Dashboard principal │ ├── add.php ← Ajout de messages (POST + CSRF) │ └── delete.php ← Suppression (POST + CSRF + bulk) ├── src/ │ └── db.php ← Connexion PDO └── config.php ← Configuration BDD ``` ### 8.2 — Créer le dashboard (`public/index.php`) ```bash sudo nano /var/www/html/abder-blog/public/index.php ``` Le dashboard comprend les fonctionnalités suivantes : | Fonctionnalité | Description | |---|---| | KPIs | Total des messages, dernières 24h, 7 jours | | Composer | Formulaire d'ajout avec protection CSRF | | Analytics | Graphique Chart.js (messages/jour sur 7 jours) | | Intelligence | Analyse des termes les plus fréquents | | Activity Feed | Liste paginée avec vue cartes ou tableau | | Bulk Actions | Sélection et suppression en masse | | Command Palette | Navigation rapide avec `Ctrl+K` | | Thème | Bascule clair/sombre | **Technologies utilisées :** | Technologie | Usage | |---|---| | Bootstrap 5.3 | Mise en page et composants | | Bootstrap Icons | Iconographie | | Chart.js | Graphiques analytics | | Google Fonts (Inter) | Typographie | | PHP PDO | Requêtes SQL sécurisées | | CSRF Token | Protection des actions POST | ### 8.3 — Créer `add.php` (ajout de messages) ```bash sudo nano /var/www/html/abder-blog/public/add.php ``` ```php <?php session_start(); require __DIR__ . '/../src/db.php'; $pdo = db(); $csrf = $_POST['csrf'] ?? ''; if (empty($_SESSION['csrf']) || !hash_equals($_SESSION['csrf'], $csrf)) { header("Location: index.php?err=" . urlencode("CSRF invalide.")); exit; } $contenu = trim($_POST['contenu'] ?? ''); if ($contenu === '') { header("Location: index.php?err=" . urlencode("Le contenu est vide.")); exit; } if (mb_strlen($contenu) > 255) { header("Location: index.php?err=" . urlencode("Max 255 caractères.")); exit; } $stmt = $pdo->prepare("INSERT INTO messages (contenu) VALUES (:c)"); $stmt->execute([':c' => $contenu]); header("Location: index.php?ok=1"); exit; ``` ### 8.4 — Créer `delete.php` (suppression individuelle et en masse) ```bash sudo nano /var/www/html/abder-blog/public/delete.php ``` ```php <?php session_start(); require __DIR__ . '/../src/db.php'; $pdo = db(); $csrf = $_POST['csrf'] ?? ''; if (empty($_SESSION['csrf']) || !hash_equals($_SESSION['csrf'], $csrf)) { header("Location: index.php?err=" . urlencode("CSRF invalide.")); exit; } // Bulk delete $bulk = trim($_POST['bulk_ids'] ?? ''); if ($bulk !== '') { $ids = array_filter(array_map('intval', explode(',', $bulk))); $ids = array_values(array_unique(array_filter($ids, fn($x) => $x > 0))); if (count($ids) > 0) { $placeholders = implode(',', array_fill(0, count($ids), '?')); $stmt = $pdo->prepare("DELETE FROM messages WHERE id IN ($placeholders)"); $stmt->execute($ids); } header("Location: index.php"); exit; } // Single delete $id = (int)($_POST['id'] ?? 0); if ($id > 0) { $stmt = $pdo->prepare("DELETE FROM messages WHERE id = :id"); $stmt->execute([':id' => $id]); } header("Location: index.php"); exit; ``` ### 8.5 — Recharger le serveur web ```bash sudo systemctl reload apache2 ``` ### 8.6 — Résultat final ![Dashboard final — vue d'ensemble avec KPIs et sidebar](https://hackmd.io/_uploads/r1tMuuPBbe.png) > L'interface finale du dashboard **"Lux Enterprise Console"**. On voit la sidebar avec le statut système, les KPIs (total messages, dernières 24h, 7 jours) et la navigation par sections. ![Dashboard final — Activity Feed et Analytics](https://hackmd.io/_uploads/SymQddwHWx.png) > La partie inférieure du dashboard avec le flux d'activité (liste des messages), les contrôles de filtrage/recherche, et le graphique analytics Chart.js sur 7 jours. --- ## Récapitulatif de l'architecture ``` ┌─────────────────┐ ┌──────────────────────────┐ │ Navigateur │ ──HTTP──▶│ VM Ubuntu (Azure) │ │ (Client) │ │ ├── Nginx / Apache │ └─────────────────┘ │ ├── PHP-FPM │ │ └── Application PHP │ └──────────┬───────────────┘ │ SSL/TLS ┌──────────▼───────────────┐ │ Azure Database for MySQL │ │ (Flexible Server) │ └──────────────────────────┘ ``` ### Sécurité mise en place | Mesure | Description | |---|---| | CSRF Token | Protection de toutes les actions POST | | PDO Prepared Statements | Protection contre les injections SQL | | `htmlspecialchars()` | Échappement de toutes les sorties HTML | | SSL/TLS | Connexion chiffrée entre PHP et MySQL | | NSG | Filtrage réseau au niveau de la VM | | Pare-feu MySQL | Restriction d'accès par IP | --- > **Guide réalisé par Abder** — Déploiement Azure VM + MySQL Flexible Server