meta données pour cette page
Différences
Ci-dessous, les différences entre deux révisions de la page.
| Prochaine révision | Révision précédente | ||
| certif:procedure:miseenplaceserveur:serveurdotnet [2026/08/18 14:12] – créée nicolas | certif:procedure:miseenplaceserveur:serveurdotnet [2026/09/30 14:39] (Version actuelle) – nicolas | ||
|---|---|---|---|
| Ligne 1: | Ligne 1: | ||
| - | ====== | + | |{{: |
| + | |**Document technique** |Rattaché à [[certif: | ||
| + | ====== | ||
| - | //Précision importante | + | //Version enrichie suite à un déploiement réel (serveur app-logeas-ovh, |
| - | //Hypothèse retenue | + | //Projet |
| - | + | ||
| - | **Comment vérifier rapidement lequel s'applique à vous :** ouvrez le fichier | + | |
| - | + | ||
| - | Bonne nouvelle par rapport à Node.js ou Angular | + | |
| - | + | ||
| - | > Le sommaire ci-dessous est généré automatiquement par DokuWiki à partir des titres de section. | + | |
| ---- | ---- | ||
| Ligne 15: | Ligne 11: | ||
| ===== 1. Objectif et architecture ===== | ===== 1. Objectif et architecture ===== | ||
| - | Ce document décrit comment déployer | + | Ce guide déploie |
| - | Le modèle d' | + | **Schéma :** Angular (autre domaine) → appel HTTPS avec en-tête CORS → IIS (port 443, certificat SSL, liaison par nom d'hôte/SNI) → module |
| - | **Schéma | + | **Ce qui a posé problème lors du premier déploiement, |
| + | - Le rôle IIS et le site étaient en place, mais le **.NET Hosting Bundle n' | ||
| + | - Après installation du Hosting Bundle, IIS trouvait le module | ||
| + | - Une fois le runtime confirmé présent, le démarrage plantait quand même avec un code générique **0x8000ffff** → cause : le **Visual C++ Redistributable 2015-2022 | ||
| + | - Une fois ANCM capable de démarrer | ||
| + | - Une fois les droits corrigés, des tests ``curl`` intermittents donnaient encore des erreurs de négociation TLS — cause : un problème de méthodologie de test (SNI), pas du serveur (voir étape 12). | ||
| - | ===== 2. Prérequis ===== | + | ===== 2. Prérequis |
| - | * Accès administrateur au Windows Server | + | * Accès administrateur au Windows Server. |
| - | * Le rôle « Serveur Web (IIS) » disponible via Gestionnaire de serveur. | + | * Le rôle « Serveur Web (IIS) » installé. |
| - | * Le SDK .NET installé sur le poste/ | + | * Le SDK .NET 8 installé sur le poste/ |
| - | * Le code de l'application prêt, avec un ''.csproj'' | + | * Un nom de domaine dédié à l'API, distinct de celui de l'Angular (ex. ``api.mondomaine.fr``). |
| - | * Un nom de domaine | + | * Un certificat SSL couvrant ce domaine |
| - | * Un certificat SSL (interne | + | * Le fichier |
| - | ===== 3. Étape 1 — Installer le rôle IIS ===== | + | > **Nouveau, issu de l' |
| + | > - Le **.NET Hosting Bundle** (runtime + module | ||
| + | > - Le **Visual C++ Redistributable 2015-2022 (x64)** — dépendance native souvent oubliée, cause de l' | ||
| - | Si IIS n'est pas encore installé sur le serveur : | + | ===== 3. Étape 1 — Installer |
| <code powershell> | <code powershell> | ||
| - | # PowerShell, en tant qu' | ||
| Install-WindowsFeature -Name Web-Server -IncludeManagementTools | Install-WindowsFeature -Name Web-Server -IncludeManagementTools | ||
| </ | </ | ||
| - | Ou via l' | + | ===== 4. Étape 2 — Installer le .NET Hosting Bundle ET le VC++ Redistributable, puis vérifier ===== |
| - | ===== 4. Étape 2 — Installer le .NET Hosting Bundle ===== | + | **C' |
| - | Étape **indispensable et spécifique à ASP.NET Core** : sans elle, IIS ne sait pas exécuter une application .NET Core (erreur 500.19 ou 502.5 sinon). | + | ==== 4.1 Installer le Hosting Bundle ==== |
| - | * Télécharger | + | Téléchargez et exécutez |
| - | * Lancer | + | |
| - | * Redémarrer IIS pour que le nouveau module soit pris en compte | + | ==== 4.2 Installer/ |
| + | |||
| + | Le Hosting Bundle est censé | ||
| <code powershell> | <code powershell> | ||
| - | net stop was /y | + | Invoke-WebRequest -Uri " |
| - | net start w3svc | + | Start-Process " |
| </ | </ | ||
| - | * Vérifier que le module est bien enregistré : dans IIS Manager, sélectionner le nœud du serveur → « Modules » → chercher '' | + | ==== 4.3 Redémarrer complètement |
| - | ===== 5. Étape 3 — Publier l' | + | <code powershell> |
| + | Restart-Computer | ||
| + | </ | ||
| - | Sur le poste de build (ou directement sur le serveur si vous choisissez de builder sur place) : | + | Un simple ``iisreset`` ou ``net stop was`` / ``net start w3svc`` ne suffit pas toujours : le chargement |
| - | < | + | ==== 4.4 Vérifier — ne pas sauter cette sous-étape ==== |
| - | dotnet | + | |
| + | Après le redémarrage, | ||
| + | |||
| + | < | ||
| + | # Le module IIS doit exister physiquement | ||
| + | Test-Path " | ||
| + | # doit renvoyer True | ||
| + | |||
| + | # Le runtime .NET doit être listé | ||
| + | dotnet --list-runtimes | ||
| + | # doit afficher une ligne " | ||
| </ | </ | ||
| - | Le dossier | + | Si l'une de ces deux vérifications échoue, ne continuez pas — reprenez |
| - | ===== 6. Étape | + | ===== 5. Étape |
| - | Copier l'intégralité du dossier '' | + | Si votre code lit les origines autorisées depuis la configuration (pattern recommandé, |
| - | < | + | < |
| - | # Exemple avec robocopy, en local sur le serveur ou via un partage réseau | + | builder.Services.AddCors(options => |
| - | robocopy publish D:\Sites\questionnaire | + | { |
| + | options.AddPolicy(" | ||
| + | { | ||
| + | policy.WithOrigins(builder.Configuration.GetSection(" | ||
| + | .AllowAnyHeader() | ||
| + | .AllowAnyMethod(); | ||
| + | }); | ||
| + | }); | ||
| + | // ... | ||
| + | app.UseCors(" | ||
| </ | </ | ||
| - | ===== 7. Étape 5 — Créer le site dans IIS Manager ===== | + | > **Rappel important :** l' |
| - | - Ouvrir IIS Manager → clic droit sur « Sites » → Ajouter un site web. | + | ===== 6. Étape 4 — Base SQLite |
| - | - Nom du site : ex. QuestionnaireApp. | + | |
| - | - Chemin d' | + | |
| - | - Liaison (Binding) : type http, port 80 (et https / port 443 une fois le certificat installé, voir étape suivante), nom d' | + | |
| - | - Pool d' | + | |
| - | - Démarrer le site. | + | |
| - | > **Identité du pool d' | + | **Deuxième étape critique.** Si le chemin de la base pointe |
| - | ===== 8. Étape | + | ==== 6.1 Choisir un emplacement en dehors du dossier de déploiement |
| - | ==== 6.1 Déployer la clé de licence ==== | + | Un dossier **frère** du dossier de déploiement (ex. ``C: |
| - | Stimulsoft charge sa licence soit par code (clé en dur dans le '' | + | Si le code suit le pattern de ce projet (``Storage: |
| - | <code csharp> | + | ==== 6.2 Donner les droits d' |
| - | var path = Path.Combine(hostEnvironment.ContentRootPath, "Content\\license.key"); | + | |
| - | Stimulsoft.Base.StiLicense.LoadFromFile(path); | + | Identifiez d' |
| + | |||
| + | <code powershell> | ||
| + | Import-Module WebAdministration | ||
| + | Get-ItemProperty | ||
| </ | </ | ||
| - | — assurez-vous que ce fichier '' | + | Puis donnez les droits sur le dossier |
| - | ==== 6.2 Taille maximale des requêtes ==== | + | <code powershell> |
| + | icacls " | ||
| + | </ | ||
| - | Les formulaires Stimulsoft peuvent transporter des données volumineuses | + | > **Comment on a isolé ce problème en pratique :** l' |
| - | <code xml> | + | ===== 7. Étape 5 — Publier l' |
| - | <system.webServer> | + | |
| - | < | + | <code bash> |
| - | <requestFiltering> | + | dotnet publish |
| - | < | + | |
| - | <!-- 52428800 = 50 Mo, à ajuster selon vos besoins --> | + | |
| - | </requestFiltering> | + | |
| - | </ | + | |
| - | </ | + | |
| </ | </ | ||
| - | Si l' | + | Vérifiez que ``appsettings.Production.json`` et le fichier de licence Stimulsoft |
| - | ===== 9. Étape | + | ===== 8. Étape |
| - | Fortement recommandé dès que le questionnaire transmet des données, même peu sensibles. | + | <code bash> |
| + | robocopy publish C: | ||
| + | </ | ||
| - | * Obtenir un certificat SSL : certificat interne (PKI de l' | + | ===== 9. Étape 7 — Créer |
| - | | + | |
| - | | + | |
| - | | + | |
| + | | ||
| + | - Liaison : https, port 443, nom d'hôte = ``formulaire.mondomaine.fr`` | ||
| + | - Pool d'applications dédié, en « No Managed Code ». | ||
| + | - Démarrer | ||
| + | |||
| + | ===== 10. Étape 8 — Configuration de production | ||
| + | |||
| + | Créez ou complétez ``C: | ||
| + | |||
| + | <code json> | ||
| + | { | ||
| + | " | ||
| + | " | ||
| + | }, | ||
| + | " | ||
| + | " | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| - | ===== 10. Étape | + | ===== 11. Étape |
| - | Autoriser uniquement les ports nécessaires | + | **Ne testez pas directement via IIS en premier.** Lancer |
| <code powershell> | <code powershell> | ||
| - | New-NetFirewallRule -DisplayName "IIS HTTP" -Direction Inbound -Protocol TCP -LocalPort 80 -Action Allow | + | cd C: |
| - | New-NetFirewallRule -DisplayName "IIS HTTPS" -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow | + | dotnet .\Logeas.Forms.Server.dll |
| </ | </ | ||
| - | ===== 11. Étape 9 — Tester le déploiement ===== | + | Vous devez voir les migrations EF Core s' |
| + | < | ||
| + | info: Microsoft.Hosting.Lifetime[0] | ||
| + | Application started. Press Ctrl+C to shut down. | ||
| + | </ | ||
| - | - Depuis un poste client : accéder | + | Si ça plante ici (licence, connexion |
| - | - Vérifier que le questionnaire fonctionne de bout en bout (soumission, enregistrement des réponses). | + | |
| - | - Provoquer volontairement un arrêt du pool d' | + | |
| - | - Redémarrer | + | |
| - | ===== 12. Étape 10 — Activer les logs (diagnostic) ===== | + | ===== 12. Étape 10 — Tester via IIS (méthode fiable) ===== |
| - | Par défaut, les logs détaillés de l'application (stdout) ne sont pas activés — utile de les activer temporairement en cas de problème. Dans le '' | + | **Piège à éviter :** ``curl -H "Host: monapi.fr" |
| - | < | + | **La bonne commande**, qui force le bon SNI tout en testant en local : |
| - | < | + | |
| - | arguments=" | + | < |
| - | stdoutLogEnabled=" | + | curl.exe -v -k --resolve formulaire.mondomaine.fr: |
| - | stdoutLogFile=" | + | |
| - | hostingModel=" | + | |
| </ | </ | ||
| - | Créer le sous-dossier | + | Un ``404`` sur ``/`` est normal et attendu pour une API sans route racine — c'est le signe que l'app tourne. Testez ensuite |
| - | Les logs IIS classiques restent aussi disponibles dans '' | + | Puis testez depuis l'extérieur (sans ``--resolve`` ni ``-k``, depuis n'importe quel poste) pour valider le DNS public |
| - | ===== 13. Étape 11 — Sécuriser l' | + | <code powershell> |
| + | curl.exe -v https:// | ||
| + | </ | ||
| - | * Activer HSTS ('' | + | Enfin, testez depuis l'application Angular réelle, dans un navigateur, pour valider l'absence |
| - | * Valider et assainir les entrées du questionnaire côté serveur (pas seulement côté client). | + | |
| - | * Ne jamais stocker de secrets (chaînes de connexion, clés API) en clair dans '' | + | |
| - | * Restreindre les droits NTFS du dossier '' | + | |
| - | * Mettre en place une limite de débit si nécessaire (middleware '' | + | |
| - | ===== 14. Étape | + | ===== 13. Étape |
| - | - Sur le poste de build : récupérer la nouvelle version du code, puis relancer | + | Si malgré tout l' |
| - | - Avant de copier les nouveaux fichiers, déposer un fichier '' | + | |
| - | - Copier les nouveaux fichiers ('' | + | |
| - | - Supprimer '' | + | |
| - | - Vérifier rapidement (étape 9) que le site répond correctement. | + | |
| - | > **Bonnes pratiques :** pour limiter les interruptions, prévoir les mises à jour en dehors des heures | + | - **Le 500 générique masque le détail par défaut pour toute requête " |
| + | - **Lisez le code d' | ||
| + | - **Observateur | ||
| + | * ``IIS AspNetCore Module V2`` : erreurs de démarrage | ||
| + | * ``.NET Runtime`` : exceptions applicatives non gérées, avec pile d' | ||
| + | - **Lancez l'app manuellement** (``dotnet .\VotreApp.dll`` depuis le dossier | ||
| + | - **Vérifiez l' | ||
| + | - **Activez les logs stdout** (``stdoutLogEnabled=" | ||
| - | ===== 15. Checklist finale ===== | + | ===== 14. Étape 12 — Sécuriser l'API ===== |
| + | |||
| + | * Swagger/ | ||
| + | * CORS restreint à l' | ||
| + | * Droits NTFS restreints sur le dossier de déploiement et le dossier de données. | ||
| + | * Aucun secret (connection strings, licence) committé dans un dépôt Git public. | ||
| + | * Validation côté serveur de toutes les entrées, même déjà validées côté client. | ||
| + | |||
| + | ===== 15. Étape 13 — Procédure de mise à jour ===== | ||
| + | |||
| + | - ``dotnet publish -c Release -o ./ | ||
| + | - Sauvegarde de la base SQLite avant toute mise à jour de schéma (simple copie du fichier ``.db``, pool arrêté ou via l' | ||
| + | - Déposer ``app_offline.htm`` à la racine du site pour libérer les fichiers verrouillés. | ||
| + | - ``robocopy publish C: | ||
| + | - Supprimer ``app_offline.htm``. | ||
| + | - Revérifier avec la méthode de l' | ||
| + | |||
| + | ===== 16. Checklist finale | ||
| ^ Élément ^ Statut ^ | ^ Élément ^ Statut ^ | ||
| | Rôle IIS installé | ☐ | | | Rôle IIS installé | ☐ | | ||
| - | | .NET Hosting Bundle installé, module AspNetCoreModuleV2 vérifié | + | | .NET 8 Hosting Bundle installé | ☐ | |
| - | | Application publiée (dotnet | + | | **Vérifié** : Test-Path aspnetcorev2.dll = True | ☐ | |
| - | | Fichiers copiés | + | | **Vérifié** : dotnet --list-runtimes affiche Microsoft.AspNetCore.App 8.x | ☐ | |
| + | | Visual C++ Redistributable x64 installé/ | ||
| + | | Redémarrage complet du serveur effectué après ces installations | ☐ | | ||
| + | | Politique CORS configurée avec l' | ||
| + | | Dossier de données SQLite créé hors du dossier de déploiement | ☐ | | ||
| + | | Droits NTFS (IIS_IUSRS ou identité du pool) sur ce dossier | ||
| + | | appsettings.Production.json déployé (Cors: | ||
| + | | license.key présent | ||
| + | | Application testée manuellement | ||
| + | | Process manuel bien arrêté (Ctrl+C) avant test IIS | ☐ | | ||
| | Site créé dans IIS, pool en « No Managed Code » | ☐ | | | Site créé dans IIS, pool en « No Managed Code » | ☐ | | ||
| - | | Fichier license.key présent sur le serveur, au bon chemin | ☐ | | + | | Certificat SSL/binding |
| - | | Limite de taille des requêtes (maxAllowedContentLength) ajustée si besoin | ☐ | | + | |
| - | | Binding http configuré | ☐ | | + | |
| - | | Certificat SSL installé, | + | |
| - | | Redirection HTTP → HTTPS active | + | |
| | Pare-feu : ports 80/443 ouverts | ☐ | | | Pare-feu : ports 80/443 ouverts | ☐ | | ||
| - | | Test de bout en bout du questionnaire | + | | Test IIS via --resolve (pas -H Host seul) réussi |
| - | | Redémarrage serveur testé | + | | Test depuis l' |
| - | | Droits NTFS restreints | + | | Appel depuis l' |
| - | | Secrets | + | | Une vraie route API testée avec succès | ☐ | |
| + | | stdoutLogEnabled repassé à false après diagnostic | ☐ | | ||
| + | | Sauvegarde de la base SQLite planifiée | ☐ | | ||
| + | |||
| + | ===== 17. Annexe A — Table de décision : quel symptôme, quelle cause ===== | ||
| + | |||
| + | ^ Symptôme observé ^ Cause la plus probable ^ Où vérifier ^ | ||
| + | | 500 générique, | ||
| + | | 500.19, code 0x8007000d | Module ANCM non reconnu par IIS (Hosting Bundle absent/ | ||
| + | | 500.31 " | ||
| + | | Erreur générique 0x8000ffff au démarrage du module | VC++ Redistributable manquant ou non finalisé | Réinstaller vc_redist.x64.exe + redémarrage complet | | ||
| + | | 503 " | ||
| + | | Connexion TLS reset par intermittence en testant avec curl -H Host | SNI non pris en compte par curl (-H Host ne suffit pas) | Utiliser --resolve à la place | | ||
| + | | Aucun fichier stdout créé malgré stdoutLogEnabled=true | Droits d' | ||
| + | | Erreur CORS dans la console navigateur | Origine absente ou incorrecte dans Cors: | ||
| + | | Les données disparaissent après une mise à jour | Base SQLite encore dans le dossier de déploiement écrasé par /MIR | Déplacer vers un dossier persistant hors déploiement | ||
| - | ===== 16. Annexe — Dépannage rapide ===== | + | ===== 18. Annexe |
| ^ Symptôme ^ Piste de résolution ^ | ^ Symptôme ^ Piste de résolution ^ | ||
| - | | Erreur | + | | Erreur 500.19 | web.config invalide ou Hosting Bundle |
| - | | Erreur HTTP 500.30 (in-process start failure) | L' | + | | Erreur 500.31 | Runtime absent |
| - | | Erreur | + | | Event ID 1010, code 0x8000ffff |
| - | | Le site ne répond pas après une mise à jour | Vérifier qu''' | + | | 503 en boucle, app OK en manuel |
| - | | Erreur d' | + | | curl : Recv failure / Connection was reset | Utiliser --resolve au lieu de -H Host pour un test SNI correct. | |
| - | | Le site répond en HTTP mais pas en HTTPS | Vérifier le binding 443 et que le certificat sélectionné est valide et non expiré. | | + | | Erreur de licence Stimulsoft en prod uniquement |
| - | | Erreur de licence Stimulsoft en production (fonctionne en local) | + | | Erreur 404.13 |
| - | | Erreur 404.13 | + | | Erreur CORS dans la console navigateur | Origine incorrecte dans Cors: |
| ---- | ---- | ||
| - | // | + | // |