meta données pour cette page
  •  

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Prochaine révision
Révision précédente
certif:procedure:miseenplaceserveur:serveurdotnet [2026/08/18 14:12] – créée nicolascertif:procedure:miseenplaceserveur:serveurdotnet [2026/09/30 14:39] (Version actuelle) – nicolas
Ligne 1: Ligne 1:
-====== Guide de déploiement — Application .NET (module Forms Stimulsoft) sur Windows Server + IIS ======+|{{:undo-2.svg?30|}} [[certif:procedure:miseenplaceserveur|Retour à Proc#144 - Installation et mise à jour des serveurs et des applications]]|| 
 +|**Document technique** |Rattaché à [[certif:procedure:miseenplaceserveur|Proc#144]]. Non soumis à approbation ([[certif:procedure:maitrisedocumentaire|Proc#163]]) : tenu à jour par l'équipe de développement.| 
 +====== Document technique — API .NET (Stimulsoft.PDF.Forms + EF Core SQLite) sur Windows Server + IIS ======
  
-//Précision importante : ce guide couvre le déploiement de **votre application .NET** qui intègre le module Forms de Stimulsoft (bibliothèque/NuGet, ex. Stimulsoft.Forms.Web ou équivalent) — pas l'installation du produit packagé « Stimulsoft Server ». Il s'agit donc d'un déploiement .NET classique, avec quelques points d'attention spécifiques à Stimulsoft (licence, taille des requêtes) détaillés à l'étape 6.//+//Version enrichie suite à un déploiement réel (serveur app-logeas-ovh, site "Formulaire") : chaque piège rencontré en production a été intégré à ce guide, avec sa cause exacte et sa correction. Suivre ce guide dans l'ordre, en particulier l'étape 2 (souvent sous-estimée), évite la quasi-totalité des problèmes rencontrés lors du premier déploiement.//
  
-//Hypothèse retenue : application **ASP.NET Core** (.NET 6/7/8/9), le cas le plus courant pour un projet récent (les exemples officiels Stimulsoft pour Forms sont fournis en ASP.NET Core). Si votre application est en réalité en **.NET Framework classique** (4.x), prévenez-moi — la démarche est plus simple encore (pas de module à installer, IIS gère nativement le pipeline .NET Framework).// +//Projet : ASP.NET Core Web API, .NET 8, Stimulsoft.PDF.Forms (formulaires PDF, en paire avec le package Angular ''stimulsoft-forms'' côté client) et Microsoft.EntityFrameworkCore.Sqlite. API déployée sur un domaine séparé de l'Angular (CORS nécessaire).//
- +
-**Comment vérifier rapidement lequel s'applique à vous :** ouvrez le fichier ''.csproj'' du projet. S'il contient une ligne ''<TargetFramework>net8.0</TargetFramework>'' (ou net6.0, net7.0, net9.0...), c'est de l'ASP.NET Core → ce guide s'applique. S'il contient plutôt ''<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>'' (ancien format de fichier projet, souvent plus long et verbeux), c'est du .NET Framework classique → dites-le-moi, je vous ferai la version adaptée. +
- +
-Bonne nouvelle par rapport à Node.js ou Angular : IIS et .NET sont conçus par le même éditeur (Microsoft) pour fonctionner ensemble nativement. Il n'y a **pas besoin de NSSM, pas de reverse proxy à configurer manuellement, pas de port applicatif à gérer** — IIS héberge directement le process .NET via un module dédié (ASP.NET Core Module), et redémarre l'application automatiquement si elle plante. C'est le déploiement le plus simple des trois guides que je vous ai faits. +
- +
-> 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 une application ASP.NET Core sur un Windows Server existant, hébergée par IIS.+Ce guide déploie une API ASP.NET Core (.NET 8) sur Windows Server via IIS, en tant que backend séparé pour une application Angular. IIS héberge l'API **in-process** via le module ASP.NET Core (ANCM) : pas de process séparé à surveiller, pas de reverse proxy manuel — IIS démarre/redémarre l'API automatiquement.
  
-Le modèle d'hébergement par défaut est dit **« in-process »** : l'application .NET Core tourne directement à l'intérieur du process de travail IIS (''w3wp.exe''), via un module natif appelé **ASP.NET Core Module (ANCM)**. Contrairement à Node.js, il n'y a donc pas de process Node séparé à démarrer/surveiller (pas de NSSM), ni de reverse proxy à configurer à la main (pas de règle URL Rewrite vers un port local) — IIS gère tout nativement, y compris le démarrage automatique au boot et le redémarrage en cas de plantage.+**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 ANCM → API .NET (Stimulsoft.PDF.Forms + EF Core) → fichier SQLite sur le serveur, dans un dossier persistant distinct du dossier de déploiement.
  
-**Schéma :** Internet → IIS (port 443, certificat SSL) → module ASP.NET Core (ANCM) → application .NET hébergée directement dans le worker process IIS.+**Ce qui a posé problème lors du premier déploiement, dans l'ordre où ça s'est manifesté** (pour comprendre pourquoi certaines étapes ci-dessous insistent autant sur la vérification) : 
 +  - Le rôle IIS et le site étaient en place, mais le **.NET Hosting Bundle n'avait en réalité jamais été installé correctement** → erreur 500.19 (config invalide, code 0x8007000d) parce qu'IIS ne reconnaissait pas l'élément ``<aspNetCore>``. 
 +  - Après installation du Hosting Bundle, IIS trouvait le module mais pas le runtime → erreur 500.31 ("Failed to load ASP.NET Core runtime"). 
 +  - 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 (x64)** n'était pas installé (dépendance native d'ANCM, censée être installée par le Hosting Bundle mais pas toujours effective sans réparation + redémarrage complet du serveur). 
 +  - Une fois ANCM capable de démarrer le process, l'app restait bloquée en **503 "Application Shutting Down"** en boucle → cause : droits NTFS insuffisants pour le compte du pool IIS sur le dossier contenant la base SQLite (l'app fonctionnait très bien lancée manuellement sous un compte Administrateur, ce qui a permis d'isoler le problème aux droits, pas au code). 
 +  - 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 — à vérifier avant tout déploiement =====
  
-  * Accès administrateur au Windows Server (2016, 2019, 2022 ou équivalent). +  * 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/pipeline de build (pas nécessairement sur le serveur) pour publier l'application. +  * Le SDK .NET 8 installé sur le poste/pipeline de build. 
-  * Le code de l'application prêt, avec un ''.csproj'' valide. +  * Un nom de domaine dédié à l'API, distinct de celui de l'Angular (ex. ``api.mondomaine.fr``). 
-  * Un nom de domaine ou sous-domaine interne pointant vers l'IP du serveur, si un accès via nom d'hôte est souhaité. +  * Un certificat SSL couvrant ce domaine (souvent un wildcard déjà utilisé par d'autres sites du même serveur). 
-  * Un certificat SSL (interne ou public) si l'application doit être servie en HTTPS — fortement recommandé dès lors que le questionnaire collecte des données.+  * Le fichier ou la clé de licence Stimulsoft.
  
-===== 3. Étape 1 — Installer le rôle IIS =====+> **Nouveau, issu de l'expérience réelle :** avant même de commencer, vérifiez que les deux dépendances suivantes sont déjà présentes sur le serveur cible si des applications .NET Core y tournent déjà — sinon, prévoyez leur installation dès l'étape 4 : 
 +> - Le **.NET Hosting Bundle** (runtime + module IIS), PAS seulement le SDK ou le runtime "autonome". 
 +> - Le **Visual C++ Redistributable 2015-2022 (x64)** — dépendance native souvent oubliée, cause de l'erreur la plus difficile à diagnostiquer de tout ce guide (voir étape 4 et l'annexe B).
  
-Si IIS n'est pas encore installé sur le serveur :+===== 3. Étape 1 — Installer le rôle IIS =====
  
 <code powershell> <code powershell>
-# PowerShell, en tant qu'administrateur 
 Install-WindowsFeature -Name Web-Server -IncludeManagementTools Install-WindowsFeature -Name Web-Server -IncludeManagementTools
 </code> </code>
  
-Ou via l'interface graphique : Gestionnaire de serveur → Ajouter des rôles et fonctionnalités → Serveur Web (IIS) → cocher au minimum : Contenu statique, Document par défaut, Journalisation HTTP, Filtrage des requêtes, Console de gestion IIS.+===== 4. Étape 2 — Installer le .NET Hosting Bundle ET le VC++ Redistributable, puis vérifier =====
  
-===== 4. Étape 2 — Installer le .NET Hosting Bundle =====+**C'est l'étape la plus critique de tout le guide.** Ne passez pas à la suite sans avoir validé chaque commande de vérification ci-dessous — c'est exactement ce qui a manqué lors du premier déploiement, et qui a coûté la majorité du temps de dépannage.
  
-É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 le « ASP.NET Core Runtime – Windows Hosting Bundle » correspondant à la version cible du projet (ex. .NET 8) depuis dotnet.microsoft.com/download/dotnet — bien prendre le **Hosting Bundle**, pas seulement le runtime standard : lui seul installe le module IIS ANCM. +Téléchargez et exécutez le **« ASP.NET Core Runtime 8.0.x – Windows Hosting Bundle »** depuis https://dotnet.microsoft.com/download/dotnet/8.0 — bien le lien **"Hosting Bundle"**, pas "Runtime" seul (qui n'installe pas le module IIS). 
-  * Lancer l'installeur (.exe) sur le serveur. + 
-  * Redémarrer IIS pour que le nouveau module soit pris en compte :+==== 4.2 Installer/réparer le Visual C++ Redistributable ==== 
 + 
 +Le Hosting Bundle est censé l'installer automatiquement, mais ce n'est pas toujours effectif. Installez-le (ou réparez-le s'il est déjà présent) explicitement :
  
 <code powershell> <code powershell>
-net stop was /y +Invoke-WebRequest -Uri "https://aka.ms/vs/17/release/vc_redist.x64.exe" -OutFile "$env:TEMP\vc_redist.x64.exe" 
-net start w3svc+Start-Process "$env:TEMP\vc_redist.x64.exe" -ArgumentList "/install /quiet /norestart" -Wait
 </code> </code>
  
-  * Vérifier que le module est bien enregistré : dans IIS Manager, sélectionner le nœud du serveur → « Modules » → chercher ''AspNetCoreModuleV2'' dans la liste.+==== 4.3 Redémarrer complètement le serveur (pas seulement IIS) ====
  
-===== 5. Étape 3 — Publier l'application =====+<code powershell> 
 +Restart-Computer 
 +</code>
  
-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 de bibliothèques natives (hostfxr, VC++ runtime) peut nécessiter un redémarrage complet pour libérer des fichiers verrouillés ou finaliser l'installation.
  
-<code bash> +==== 4.4 Vérifier — ne pas sauter cette sous-étape ==== 
-dotnet publish -c Release -o ./publish+ 
 +Après le redémarrage, dans une **nouvelle** session PowerShell (le PATH n'est chargé qu'à l'ouverture de session) : 
 + 
 +<code powershell> 
 +# Le module IIS doit exister physiquement 
 +Test-Path "C:\Program Files\IIS\Asp.Net Core Module\V2\aspnetcorev2.dll" 
 +# doit renvoyer True 
 + 
 +# Le runtime .NET doit être listé 
 +dotnet --list-runtimes 
 +# doit afficher une ligne "Microsoft.AspNetCore.App 8.x.x" ET une ligne "Microsoft.NETCore.App 8.x.x"
 </code> </code>
  
-Le dossier ''publish/'' généré contient les DLL de l'application, ses dépendances, et surtout un fichier **''web.config'' déjà généré automatiquement** par la commande publish — il contient la configuration nécessaire pour qu'IIS sache démarrer l'application via ANCM (chemin vers l'exécutable/DLL, modèle d'hébergement in-process, etc.). Il n'y a normalement rien à modifier dedans pour un déploiement standard.+Si l'une de ces deux vérifications échoue, ne continuez pas — reprenez l'installation avant d'aller plus loin. C'est ce contrôle, sauté lors du premier déploiement, qui aurait évité une bonne partie du dépannage.
  
-===== 6. Étape 4 — Copier les fichiers vers le serveur =====+===== 5. Étape 3 — Configurer CORS dans le code =====
  
-Copier l'intégralité du dossier ''publish/'' vers un dossier dédié sur le serveur, par exemple ''D:\Sites\questionnaire\''.+Si votre code lit les origines autorisées depuis la configuration (pattern recommandé, déjà en place dans ce projet via ``Cors:Origins``), il n'y a rien à changer dans le code — seulement dans ``appsettings.Production.json`` (étape 8). Sinon, dans ``Program.cs`` :
  
-<code bash> +<code csharp> 
-# Exemple avec robocopy, en local sur le serveur ou via un partage réseau +builder.Services.AddCors(options => 
-robocopy publish D:\Sites\questionnaire /MIR+{ 
 +    options.AddPolicy("logeas-web", policy => 
 +    { 
 +        policy.WithOrigins(builder.Configuration.GetSection("Cors:Origins").Get<string[]>() ?? []) 
 +              .AllowAnyHeader() 
 +              .AllowAnyMethod(); 
 +    }); 
 +}); 
 +// ... 
 +app.UseCors("logeas-web");
 </code> </code>
  
-===== 7. Étape 5 — Créer le site dans IIS Manager =====+> **Rappel important :** l'origine à autoriser est celle du site qui **appelle** l'API (l'Angular, ex. ``interface.logeas-web.fr``), jamais celle de l'API elle-même. CORS ne protège pas contre un appel direct (curl/Postman) — il ne concerne que les appels JavaScript faits depuis un navigateur, depuis une autre origine.
  
-  - Ouvrir IIS Manager → clic droit sur « Sites » → Ajouter un site web. +===== 6. Étape 4 — Base SQLite : emplacement persistant et droits =====
-  - Nom du site : ex. QuestionnaireApp. +
-  - Chemin d'accès physique : D:\Sites\questionnaire\ (le dossier contenant le web.config généré par dotnet publish). +
-  - Liaison (Binding) : type http, port 80 (et https / port 443 une fois le certificat installé, voir étape suivante), nom d'hôte = le domaine/sous-domaine prévu. +
-  - Pool d'applications : créer ou assigner un pool dédié, en mode **« No Managed Code »** — contrairement à une application .NET Framework classique, ASP.NET Core ne passe pas par le pipeline géré historique d'IIS, c'est le module ANCM qui gère tout. +
-  - Démarrer le site.+
  
-> **Identité du pool d'application :** vérifier que le compte utilisé par le pool (par défaut ''ApplicationPoolIdentity'') a les droits de lecture sur le dossier ''D:\Sites\questionnaire\'', et d'écriture sur le sous-dossier de logs si la journalisation stdout est activée (étape 10).+**Deuxième étape critique.** Si le chemin de la base pointe (par défaut ou par erreur de configuration) vers un dossier à l'intérieur du dossier de déploiement, deux problèmes surviennent : la base est effacée à chaque mise à jour (``robocopy /MIR``), et le compte du pool IIS peut ne pas avoir les droits d'écriture nécessaires.
  
-===== 8. Étape 6 — Points spécifiques au module Forms Stimulsoft =====+==== 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:\RemoteApp\Formulaire-Data`` à côté de ``C:\RemoteApp\Formulaire``) convient parfaitement : un ``robocopy /MIR`` ciblant uniquement ``Formulaire`` ne le touche pas. Un chemin totalement séparé (ex. ``D:\Data\...``) fonctionne tout aussi bien.
  
-Stimulsoft charge sa licence soit par code (clé en dur dans le ''Program.cs''/contrôleur), soit par fichier (''license.key''). Si votre code utilise la seconde méthode — typiquement :+Si le code suit le pattern de ce projet (``Storage:DatabasePath`` lu depuis la configuration, avec ``Directory.CreateDirectory`` automatique), il suffit de renseigner ce chemin dans ``appsettings.Production.json`` (étape 8) — aucune modification de code nécessaire.
  
-<code csharp> +==== 6.2 Donner les droits d'écriture au compte du pool IIS ==== 
-var path = Path.Combine(hostEnvironment.ContentRootPath, "Content\\license.key"); + 
-Stimulsoft.Base.StiLicense.LoadFromFile(path);+Identifiez d'abord l'identité du pool (souvent ``ApplicationPoolIdentity`` par défaut, ce qui correspond au compte virtuel ``IIS AppPool\<NomDuPool>`` et qui est automatiquement membre du groupe local ``IIS_IUSRS``) : 
 + 
 +<code powershell> 
 +Import-Module WebAdministration 
 +Get-ItemProperty "IIS:\AppPools\<NomDuPool>" -Name processModel.identityType
 </code> </code>
  
-— assurez-vous que ce fichier ''license.key'' est bien copié dans le dossier ''publish/'' lors du ''dotnet publish'' (propriété « Copy to Output Directory » sur le fichier dans le projet), puis vérifiez sa présence une fois copié sur le serveur, au même chemin relatif attendu par le code (ex. ''D:\Sites\questionnaire\Content\license.key''). Un oubli de ce fichier au déploiement est une cause fréquente d'erreur de licence en production alors que tout fonctionne en local.+Puis donnez les droits sur le dossier de données (le groupe ``IIS_IUSRS`` couvre le cas ``ApplicationPoolIdentity`` par défaut ; si l'identité est un compte de service spécifique, remplacez ``IIS_IUSRS`` par ce compte) :
  
-==== 6.2 Taille maximale des requêtes ====+<code powershell> 
 +icacls "C:\RemoteApp\Formulaire-Data" /grant "IIS_IUSRS:(OI)(CI)M" 
 +</code>
  
-Les formulaires Stimulsoft peuvent transporter des données volumineuses (pièces jointes, images intégrées, export de rapports). Si vous rencontrez une erreur HTTP 404.13 ou 413 lors de la soumission d'un formulaire un peu chargé, augmentez la limite dans le web.config :+> **Comment on a isolé ce problème en pratique :** l'application plantait sous IIS (503 en boucle) mais fonctionnait parfaitement lancée manuellement avec ``dotnet .\VotreApp.dll`` depuis une session Administrateur — la différence entre les deux étant précisément les droits du compte utilisé. Lancer l'app manuellement est le test le plus rapide pour distinguer un problème de code d'un problème de droits/environnement IIS (voir étape 11).
  
-<code xml> +===== 7. Étape 5 — Publier l'application ===== 
-<system.webServer> + 
-  <security> +<code bash> 
-    <requestFiltering> +dotnet publish -c Release -o ./publish
-      <requestLimits maxAllowedContentLength="52428800" /> +
-      <!-- 52428800 = 50 Mo, à ajuster selon vos besoins --> +
-    </requestFiltering> +
-  </security> +
-</system.webServer>+
 </code> </code>
  
-Si l'application est en ASP.NET Core, vérifier également, côté code, la configuration Kestrel/formOptions correspondante (''MultipartBodyLengthLimit'', ''MaxRequestBodySize'') si elle est explicitement définie dans ''Program.cs''.+Vérifiez que ``appsettings.Production.json`` et le fichier de licence Stimulsoft (si utilisé par fichier) sont bien inclus dans ce dossier.
  
-===== 9. Étape 7 — Configurer HTTPS =====+===== 8. Étape 6 — Copier les fichiers vers le serveur =====
  
-Fortement recommandé dès que le questionnaire transmet des données, même peu sensibles.+<code bash> 
 +robocopy publish C:\RemoteApp\Formulaire /MIR 
 +</code>
  
-  * Obtenir un certificat SSL : certificat interne (PKI de l'entreprise) si le site n'est accessible qu'en interne, ou certificat public (ex. Let's Encrypt, via l'outil win-acme, ou un certificat acheté) si le site est exposé sur Internet. +===== 9. Étape 7 — Créer le site dans IIS Manager ===== 
-  * Dans IIS Manager, sélectionner le site → « Certificats de serveur » → importer le certificat. + 
-  * Ajouter une liaison https sur le port 443 pour le site, en sélectionnant ce certificat. +  - IIS Manager → « Sites » → Ajouter un site web. 
-  * Forcer la redirection HTTP → HTTPS : le plus simple ici est d'activer ''app.UseHttpsRedirection()'' directement dans le code (''Program.cs''), déjà présent par défaut dans la plupart des templates ASP.NET Core. Alternative sans toucher au code : utiliser le module URL Rewrite d'IIS avec une règle de redirection identique à celle des guides précédents.+  - Nom du site : ex. ``Formulaire``. 
 +  - Chemin d'accès physique : ``C:\RemoteApp\Formulaire\``. 
 +  - Liaison : https, port 443, nom d'hôte = ``formulaire.mondomaine.fr`` (liaison par SNI si le certificat est partagé avec d'autres sites du serveur — cas fréquent avec un certificat wildcard). 
 +  - Pool d'applications dédié, en « No Managed Code ». 
 +  - Démarrer le site. 
 + 
 +===== 10. Étape 8 — Configuration de production (appsettings.Production.json) ===== 
 + 
 +Créez ou complétez ``C:\RemoteApp\Formulaire\appsettings.Production.json`` avec, au minimum, les origines CORS et le chemin de la base : 
 + 
 +<code json> 
 +{ 
 +  "Cors": { 
 +    "Origins": [ "https://interface.mondomaine.fr" ] 
 +  }, 
 +  "Storage": { 
 +    "DatabasePath": "C:\\RemoteApp\\Formulaire-Data\\forms.db" 
 +  } 
 +} 
 +</code>
  
-===== 10. Étape 8 — Pare-feu Windows =====+===== 11. Étape 9 — Vérification manuelle AVANT de tester via IIS =====
  
-Autoriser uniquement les ports nécessaires en entrée : 80 et 443. Il n'y a ici aucun port applicatif interne à ouvrir (le module ANCM communique avec l'application via un canal interne, pas un port TCP exposé).+**Ne testez pas directement via IIS en premier.** Lancer l'app manuellement isole immédiatement les problèmes de code/configuration des problèmes d'environnement IIS — c'est le test le plus rapide et le plus fiable de tout ce guide, et celui qui a permis de trancher le plus vite lors du déploiement réel.
  
 <code powershell> <code powershell>
-New-NetFirewallRule -DisplayName "IIS HTTP" -Direction Inbound -Protocol TCP -LocalPort 80 -Action Allow +cd C:\RemoteApp\Formulaire 
-New-NetFirewallRule -DisplayName "IIS HTTPS" -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow+dotnet .\Logeas.Forms.Server.dll
 </code> </code>
  
-===== 11. Étape 9 — Tester le déploiement =====+Vous devez voir les migrations EF Core s'exécuter, puis : 
 +<code> 
 +info: Microsoft.Hosting.Lifetime[0] 
 +      Application started. Press Ctrl+C to shut down. 
 +</code>
  
-  - Depuis un poste client : accéder à ''http://nom-du-site'' puis ''https://nom-du-site'' dans un navigateur : l'application doit répondre. +Si ça plante ici (licence, connexion à la base, exception applicative), l'erreur s'affiche directement dans la console — corrigez avant de continuer. Si ça démarre correctement, **arrêtez le process (Ctrl+C)** avant de passer à l'étape suivante : le laisser tourner peut verrouiller le fichier SQLite ou entrer en conflit avec l'instance qu'IIS va lancer.
-  - Vérifier que le questionnaire fonctionne de bout en bout (soumission, enregistrement des réponses). +
-  - Provoquer volontairement un arrêt du pool d'application (Arrêter puis Démarrer dans IIS Manager) et vérifier que le site répond de nouveau normalement après redémarrage — IIS/ANCM relance automatiquement l'application à la requête suivante. +
-  - Redémarrer le serveur et vérifier que le site est de nouveau accessible sans intervention manuelle (IIS démarre automatiquement au boot).+
  
-===== 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 ''web.config'' généré par ''dotnet publish'', repérer la balise ''aspNetCore'' et ajuster :+**Piège à éviter :** ``curl -H "Host: monapi.fr" https://localhost/`` change l'en-tête HTTP mais **pas le SNI** envoyé pendant la négociation TLS. Si le site est en liaison par nom d'hôte (cas courant avec un certificat partagé entre plusieurs sites), ce test peut échouer de façon incohérente (parfois ça "marche" par repli de schannel sur un certificat par défaut, parfois non, notamment après un ``iisreset``).
  
-<code xml> +**La bonne commande**, qui force le bon SNI tout en testant en local : 
-<aspNetCore processPath="dotnet" + 
-            arguments=".\QuestionnaireApp.dll" +<code powershell> 
-            stdoutLogEnabled="true" +curl.exe -v -k --resolve formulaire.mondomaine.fr:443:127.0.0.1 https://formulaire.mondomaine.fr/
-            stdoutLogFile=".\logs\stdout" +
-            hostingModel="InProcess" />+
 </code> </code>
  
-Créer le sous-dossier ''logs\'' à côté du web.config (IIS ne le crée pas automatiquement) et s'assurer que le pool d'application a le droit d'y écrire. Une fois le diagnostic terminé, repasser ''stdoutLogEnabled'' à ''false'' pour éviter une accumulation de fichiers.+Un ``404`` sur ``/`` est normal et attendu pour une API sans route racine — c'est le signe que l'app tourne. Testez ensuite une vraie route de vos contrôleurs pour confirmer complètement.
  
-Les logs IIS classiques restent aussi disponibles dans ''%SystemDrive%\inetpub\logs\LogFiles\'', et les erreurs de démarrage du module ANCM apparaissent dans l'Observateur d'événements Windows (Journaux Windows → Application).+Puis testez depuis l'extérieur (sans ``--resolve`` ni ``-k``, depuis n'importe quel poste) pour valider le DNS public et le certificat de bout en bout :
  
-===== 13. Étape 11 — Sécuriser l'application =====+<code powershell> 
 +curl.exe -v https://formulaire.mondomaine.fr/ 
 +</code>
  
-  * Activer HSTS (''app.UseHsts()'', déjà présent par défaut dans la plupart des templates hors environnement de développement). +Enfin, testez depuis l'application Angular réelle, dans un navigateur, pour valider l'absence d'erreur CORS.
-  * 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 ''appsettings.json'' déployé — utiliser des variables d'environnement, le gestionnaire de secrets IIS, ou un coffre-fort (ex. Azure Key Vault si applicable). +
-  * Restreindre les droits NTFS du dossier ''D:\Sites\questionnaire\'' pour que seuls les comptes de déploiement/administration puissent y écrire. +
-  * Mettre en place une limite de débit si nécessaire (middleware ''Microsoft.AspNetCore.RateLimiting'', inclus nativement depuis .NET 7).+
  
-===== 14. Étape 12 — Procédure de mise à jour de l'application =====+===== 13. Étape 11 — Diagnostiquer une erreur 500/503 (méthode complète) =====
  
-  - Sur le poste de build : récupérer la nouvelle version du code, puis relancer ''dotnet publish -c Release -o ./publish''. +Si malgré tout l'application ne démarre pas sous IIS, suivez cette séquence dans l'ordre — c'est celle qui a permis de résoudre le déploiement réel, étape par étape, sans deviner :
-  - Avant de copier les nouveaux fichiers, déposer un fichier ''app_offline.htm'' à la racine de ''D:\Sites\questionnaire\'' : IIS/ANCM détecte sa présence et arrête proprement l'application, libérant les DLL verrouillées (sans ça, la copie peut échouer si les fichiers sont en cours d'utilisation). +
-  - Copier les nouveaux fichiers (''robocopy publish D:\Sites\questionnaire /MIR'', en excluant ''app_offline.htm'' s'il est encore présent côté serveur). +
-  - Supprimer ''app_offline.htm'' : l'application redémarre automatiquement à la requête suivante. +
-  - 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 d'utilisation du questionnaire, et garder une copie de la version précédente du dossier ''publish/'' pour pouvoir revenir en arrière rapidement en cas de problème.+  - **Le 500 générique masque le détail par défaut pour toute requête "distante".** Testez en local avec le bon SNI (étape 10) pour obtenir la page d'erreur IIS détaillée plutôt qu'une page générique. 
 +  - **Lisez le code d'erreur précis** dans la page détaillée (500.19, 500.30, 500.31, etc.) — chacun a une cause différente (voir annexe B). 
 +  - **Observateur d'événements Windows → Journaux Windows → Application.** Deux sources à distinguer : 
 +    * ``IIS AspNetCore Module V2`` : erreurs de démarrage du module lui-même (runtime introuvable, échec catastrophique de chargement). 
 +    * ``.NET Runtime`` : exceptions applicatives non gérées, avec pile d'appels — c'est celle-ci qui donne la vraie cause si l'app plante après avoir démarré. 
 +  - **Lancez l'app manuellement** (``dotnet .\VotreApp.dll`` depuis le dossier de déploiement) — voir étape 9. Si ça fonctionne manuellement mais pas sous IIS, la cause est presque toujours l'identité du pool (droits NTFS) ou une variable d'environnement absente sous IIS. 
 +  - **Vérifiez l'état du pool** (``Get-WebAppPoolState -Name "<Pool>"``) — un pool ``Stopped`` peut résulter de la protection contre les échecs rapides (Rapid-Fail Protection) après plusieurs plantages successifs pendant les tests ; ``Start-WebAppPool`` le redémarre, mais ne résout pas la cause sous-jacente. 
 +  - **Activez les logs stdout** (``stdoutLogEnabled="true"`` dans le web.config) si aucune des étapes précédentes n'a suffi — mais vérifiez d'abord que le compte du pool a les droits d'écriture sur le dossier ``logs\`` (``icacls ... /grant "IIS_IUSRS:(OI)(CI)M"``), sinon aucun fichier n'apparaîtra malgré l'activation.
  
-===== 15. Checklist finale =====+===== 14. Étape 12 — Sécuriser l'API ===== 
 + 
 +  * Swagger/OpenAPI désactivé ou protégé en production. 
 +  * CORS restreint à l'origine exacte de l'Angular, jamais ``AllowAnyOrigin`` en production sur un endpoint qui écrit des données. 
 +  * 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 ./publish``. 
 +  - Sauvegarde de la base SQLite avant toute mise à jour de schéma (simple copie du fichier ``.db``, pool arrêté ou via l'outil ``backup`` de SQLite pour une copie à chaud cohérente). 
 +  - Déposer ``app_offline.htm`` à la racine du site pour libérer les fichiers verrouillés. 
 +  - ``robocopy publish C:\RemoteApp\Formulaire /MIR`` (la base, hors de ce dossier, n'est pas affectée). 
 +  - Supprimer ``app_offline.htm``. 
 +  - Revérifier avec la méthode de l'étape 10 (pas juste ``curl https://localhost/`` sans ``--resolve``). 
 + 
 +===== 16. Checklist finale (exhaustive) =====
  
 ^ É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 publish -c Release) | ☐ | +| **Vérifié** : Test-Path aspnetcorev2.dll = True | ☐ | 
-| Fichiers copiés sur le serveur (dossier publish/, avec web.config) | ☐ |+| **Vérifié** : dotnet --list-runtimes affiche Microsoft.AspNetCore.App 8.x | ☐ | 
 +| Visual C++ Redistributable x64 installé/réparé | ☐ | 
 +| Redémarrage complet du serveur effectué après ces installations | ☐ | 
 +| Politique CORS configurée avec l'origine exacte de l'Angular | ☐ | 
 +| 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:Origins, Storage:DatabasePath) | ☐ | 
 +| license.key présent sur le serveur si chargement par fichier | ☐ | 
 +| Application testée manuellement (dotnet .\App.dll) avant IIS | ☐ | 
 +| 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 SNI configuré | ☐ |
-| Limite de taille des requêtes (maxAllowedContentLength) ajustée si besoin | ☐ | +
-| Binding http configuré | ☐ | +
-| Certificat SSL installé, binding https configuré | ☐ | +
-| Redirection HTTP → HTTPS active | ☐ |+
 | Pare-feu : ports 80/443 ouverts | ☐ | | Pare-feu : ports 80/443 ouverts | ☐ |
-| Test de bout en bout du questionnaire réussi | ☐ | +| Test IIS via --resolve (pas -H Host seul) réussi (404 sur / = OK) | ☐ | 
-| Redémarrage serveur testé (reprise automatique) | ☐ | +| Test depuis l'extérieur (DNS public) réussi | ☐ | 
-| Droits NTFS restreints sur le dossier du site | ☐ | +| Appel depuis l'Angular réel sans erreur CORS | ☐ | 
-| Secrets non stockés en clair (appsettings) | ☐ |+| 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, masque tout détail | Requête traitée comme "distante" par IIS | Retester en local avec --resolve (étape 10) | 
 +| 500.19, code 0x8007000d | Module ANCM non reconnu par IIS (Hosting Bundle absent/incomplet) | Test-Path aspnetcorev2.dll | 
 +| 500.31 "Failed to load ASP.NET Core runtime" | Runtime .NET absent ou bitness du pool incohérente | dotnet --list-runtimes + réglage "32 bits" du pool | 
 +| 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 "Application Shutting Down" en boucle, mais l'app tourne manuellement | Droits NTFS insuffisants pour l'identité du pool (souvent sur le dossier de données) | icacls sur le dossier de données, identité du pool | 
 +| 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'écriture manquants sur le dossier logs\ | icacls sur le dossier logs | 
 +| Erreur CORS dans la console navigateur | Origine absente ou incorrecte dans Cors:Origins | Vérifier appsettings.Production.json, protocole/domaine/port exacts | 
 +| 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 B — Dépannage rapide (référence courte) =====
  
 ^ Symptôme ^ Piste de résolution ^ ^ Symptôme ^ Piste de résolution ^
-| Erreur HTTP 500.19 | web.config invalide, ou module ANCM absent — vérifier que le Hosting Bundle est bien installé (pas seulement le runtime standard). | +| Erreur 500.19 | web.config invalide ou Hosting Bundle absent — vérifier Test-Path du module ANCM. | 
-| Erreur HTTP 500.30 (in-process start failure) | L'application plante au démarrage — activer stdoutLogEnabled (étape 9) et consulter l'Observateur d'événements Windows pour l'exception exacte. | +| Erreur 500.31 | Runtime absent ou bitness du pool incohérente — vérifier dotnet --list-runtimes et le réglage 32 bits. | 
-| Erreur HTTP 500.31 / 502.5 | Le module ANCM ne trouve pas l'application ou une version du runtime incompatible — vérifier la version du Hosting Bundle installée vs le TargetFramework du projet. | +| Event ID 1010, code 0x8000ffff | VC++ Redistributable manquant — réinstaller puis redémarrer complètement le serveur. | 
-| Le site ne répond pas après une mise à jour | Vérifier qu'''app_offline.htm'' a bien été retiré après la copie des nouveaux fichiers. | +| 503 en boucle, app OK en manuel | Droits NTFS du compte du pool sur le dossier de données. | 
-| Erreur d'accès refusé lors du démarrage | Droits NTFS insuffisants pour le compte du pool d'application (''ApplicationPoolIdentity'') sur le dossier du site ou le sous-dossier logs. | +| 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 | license.key absent du dossier publié, chemin incorrect. | 
-| Erreur de licence Stimulsoft en production (fonctionne en local) | Le fichier license.key n'a probablement pas été copié dans le dossier publié — vérifier sa présence sur le serveur au chemin exact attendu par le code (étape 6.1). | +| Erreur 404.13 / 413 sur soumission de formulaire | Augmenter maxAllowedContentLength dans le web.config. | 
-| Erreur 404.13 ou 413 lors de la soumission d'un formulaire volumineux | Limite de taille de requête IIS trop basse — augmenter maxAllowedContentLength dans le web.config (étape 6.2). |+| Erreur CORS dans la console navigateur | Origine incorrecte dans Cors:Origins, ou app.UseCors() appelé après app.MapControllers(). |
  
 ---- ----
  
-//Document préparé le 18 août 2026.//+//Document mis à jour le 18 août 2026, à partir d'un déploiement réel et de son dépannage complet.//