Déployer vers Azure App Service
Un portail Power Portals Pro est une application Core à ASP.NET unique, quelle que soit la pile choisie, donc son déploiement est un standard dotnet publish pour un service d’application ordinaire. Cette page couvre les ressources à créer, les paramètres que le portail lit à l’écran, ce que nous suggérons comme valeurs par défaut, ainsi que les quelques étapes spécifiques à Power Portals Pro plutôt qu’à ASP.NET Core.
Ce que vous déployez
Les deux modèles produisent une application web et une sortie de publication. Il n’y a pas d’hôte front-end séparé à installer, pas de ressource statique du site, et pas de second service d’application :
- Blazor — le projet serveur héberge l’application. Sous
ServerouAutointeractivité, elle sert également le circuit Blazor SignalR ; sousWebAssemblyelle sert le package client et les/api/*points d’accès que le client appelle. - React —
dotnet publishexécute la compilation Vite du projet client et ladist/met en place dans celle dewwwrootl’hôte, donc un ASP.NET hôte Core sert le SPA et l’API depuis une seule origine. Rien de plus à câbler.
Note
Parce que les versions de publication de React sont déployées vers npm, Node doit être installé sur la machine qui tourne
dotnet publish— votre ordinateur portable ou l’agent de compilation. Le client du modèle demande Node^20.19.0 || >=22.12.0. App Service lui-même n’a jamais besoin de Node ; il ne voit que la sortie intégrée.
Paramètres par défaut suggérés
Des points de départ, pas des règles — mais ce sont ce que nous choisirions pour un nouveau portail de production.
- Système d’exploitation : Linux. Moins cher par niveau, plus rapide à démarrer, et cela maintient IIS complètement
web.confighors de l’équation. Windows fonctionne bien si votre organisation le standardise. - Pile d’exécution : .NET 10. Les modèles ciblent
net10.0. Publier en fonction du framework et laisser App Service fournir l’exécution. - Plan : B1 — pour chaque environnement, production incluse. B1 est le niveau le moins cher qui peut rester chaud, et augmenter la vitesse plus tard est une commande sans redéploiement ni interruption significative. Provisionner quelque chose de plus grand « parce que c’est la production » déclenche une facture que personne ne revisite ; commencez ici et montez quand une mesure, ou un besoin de slots de déploiement, l’exige. Ce que vous abandonnez jusque-là : pas de slots de déploiement et pas d’autoscale (les deux commencent au Standard — et comparez S1 à P0v3, qui est généralement moins cher sous Linux et a plus de mémoire).
- Toujours activé : activé. App Service décharge une application inactive, et un démarrage à froid signifie se reconnecter à Dataverse et reconstruire les caches de métadonnées et de localisation. Le premier visiteur après un moment de calme paie tout cela.
- HTTPS uniquement : activé, TLS 1.2 minimum, HTTP/2 activé. Le portail émet des cookies d’authentification ; il n’y a aucune raison d’accepter l’HTTP simple.
- Sockets web : activés pour Blazor Server ou Auto. Désactivé est la valeur par défaut du service d’application, et sans cela, le circuit retombe discrètement dans un long polling.
- Affinité de session : activée pour Blazor Server ou Auto — un circuit appartient à une seule instance. Désactivez-le pour les portails React et Blazor WebAssembly : ils sont sans état sur HTTP, et l’affinité ne déséquilibre que vos instances.
- Secrets dans Key Vault, référencés depuis les paramètres de l’application. Donnez une identité gérée au service d’application et pointez le paramètre vers le vault, afin que le secret ne reste jamais dans la configuration ou dans un script de déploiement.
- Les clés de protection des données dans Blob Storage dès que vous utilisez des emplacements de déploiement ou que vous dépassez une instance. Le porte-clés par défaut est par emplacement, et un échange désigne tout le monde.
WEBSITE_RUN_FROM_PACKAGE=1: optionnel, et ça en vaut la peine. Les déploiements deviennent atomiques et le répertoire de contenu en lecture seule. Le portail ne lit que depuis sa racine de contenu, donc rien ne casse — il met un tampon dans le dossier temporaire système, qui reste écrivable.- Analyses d’application : sur. Le framework enregistre les rejets de licences et les échecs Dataverse via
ILogger. Sans pubbli, vous lisez le flux de journal à la main exactement au moment où vous préféreriez ne pas l’être.
1. Créer le service d’application
Trois commandes. Substituez vos propres noms — myportal est utilisée tout au long de cette page.
az group create --name myportal-rg --location eastus
az appservice plan create --name myportal-plan --resource-group myportal-rg --sku B1 --is-linux
az webapp create --name myportal --resource-group myportal-rg --plan myportal-plan --runtime "DOTNETCORE:10.0"
Le nom de l’application web devient https://myportal.azurewebsites.net et doit être globalement unique. Choisissez-le délibérément : à moins de mettre un domaine personnalisé devant, c’est l’URL à laquelle votre clé de licence est liée.
2. Définir les options de plateforme
Ce sont ceux dont les défauts sont mauvais pour un portail.
az webapp config set --name myportal --resource-group myportal-rg --always-on true --min-tls-version 1.2 --http20-enabled true --web-sockets-enabled true
az webapp update --name myportal --resource-group myportal-rg --https-only true --client-affinity-enabled true
--web-sockets-enabled true— nécessaire pour Blazor Server et Auto. Inoffensif sur les portails React et WebAssembly, qui n’ouvrent jamais de circuit.--client-affinity-enabled— laisser pourtrueBlazor Server et Auto ; passerfalsepour React et Blazor WebAssembly afin que les requêtes soient réparties uniformément entre les instances.--always-on true— Niveau de base ou plus. Il n’est pas disponible sur les forfaits gratuits ou partagés.
3. Configuration
Les paramètres de l’application du service d’applications arrivent sous forme de variables d’environnement et supplantent appsettings.json, ce qui est exactement ce que vous voulez : laissez le fichier tel quel et définissez ici les valeurs par environnement.
Important
Les secrets utilisateur ne voyagent pas. Tout ce que vous stockiez
dotnet user-secretspendant le développement reste uniquement sur votre machine. Chacune de ces clés doit être recréée sous forme de paramètre d’application — ou de référence Key Vault — sinon l’application déployée ne démarrera pas.
Utilisez un double soulignement pour le séparateur de section : D365:ClientId devient D365__ClientId. Le formulaire deux-points fonctionne sur Windows App Service mais pas sous Linux, donc __ le formulaire à utiliser partout est le cas.
Toujours obligatoire
D365__Url— l’URL de l’environnement Dataverse, parhttps://yourorg.crm.dynamics.comexemple . Ce n’est pas un secret.D365__ClientId— l’identifiant client de l’enregistrement de l’application Entra que le portail connecte à Dataverse sous le nom. Ce n’est pas un secret.D365__ClientSecret— ce secret d’enregistrement est client. Secret : utilise une référence Key Vault.D365__EmailSenderEmailAddress— l’adresse de départ que le portail envoie via Dataverse. La plus facile à oublier, car elle n’est pas disponibleappsettings.jsonet l’application ne démarre pas sans elle.ASPNETCORE_ENVIRONMENT— le définir àProduction. Cela fait plus que modifier la page d’erreur : sur un portail React, le plan de secours SPA n’est enregistré qu’en dehors de Développement, donc un site laissé surDevelopmentredirige vers des liens profonds vershttp://localhost:5173au lieu de servir l’application.
Obligatoire si vous avez activé ce fournisseur de connexion
Authentication__Microsoft__ClientIdetAuthentication__Microsoft__ClientSecret— l’enregistrement de connexion, qui est une inscription d’application différente de celle de Dataverse. Voir Entrée ID Connexion (Connexion Entra).Authentication__Google__ClientIdetAuthentication__Google__ClientSecret.Authentication__Facebook__AppIdetAuthentication__Facebook__AppSecret.
Optionnel
Azure__Translation__Key,DeepL__Translation__KeyouGoogle__Translation__Key— quel que soit le fournisseur de traduction automatique que vous avez enregistré. Laissez-le non configuré et la page d’administration de la localisation cache simplement son panneau de traduction ; le reste de la page fonctionne toujours.PortalIdentity__SystemAdminRoleName— le rôle de sécurité Dataverse qui accorde l’accès aux administrateurs de portail. Il est livréappsettings.json; il est remplacé ici lorsque le nom du rôle diffère selon l’environnement.
az webapp config appsettings set --name myportal --resource-group myportal-rg --settings ASPNETCORE_ENVIRONMENT=Production D365__Url="https://yourorg.crm.dynamics.com" D365__ClientId="00000000-0000-0000-0000-000000000000" D365__EmailSenderEmailAddress="portal@contoso.com"
Note
Les clés requises sont lues avec
GetRequiredValue, donc une clé manquante est lancée au démarrage plutôt que de casser plus tard lors du premier appel Dataverse. Sur un service d’applications, cela apparaît comme un site qui n’apparaît jamais — lisez le flux de journal, et l’exception nomme la clé.
4. Donner au portail une adresse à partir de laquelle envoyer
D365__EmailSenderEmailAddress c’est d’où provient l’email de confirmation de compte et de réinitialisation de mot de passe, et le framework le résout en cherchant un utilisateur Dataverse avec cette adresse, puis une file d’attente, puis une équipe. Utilisez une file d’attente. C’est l’option qui ne nécessite aucun travail dans Exchange : la boîte aux lettres créée par Dataverse pour une file d’attente est orientée vers le profil Exchange Online existant de l’organisation, donc il n’y a pas de boîte aux lettres à provisionner ni de licence pour acheter l’adresse. Pointer le paramètre vers une personne réelle vous coûte plutôt trois choses :
- Leur nom apparaît sur chaque message automatisé. Les destinataires voient un collègue comme l’expéditeur d’une réinitialisation de mot de passe qu’ils n’ont pas envoyée.
- Les réponses arrivent dans leur boîte de réception. Les gens répondent aux messages sans réponse, et ces réponses vont là où personne ne les surveille.
- Le portail se brise quand ils partent. Désactiver le compte emporte l’expéditeur, et la défaillance apparaît alors que la confirmation du compte ne fonctionne pas discrètement.
Trois étapes, toutes à l’intérieur de Dataverse. Le serveur MCP effectue les deux premiers appels en un seul appel avec create_email_queue:
- Créez une file d’attente privée avec l’adresse (par
noreply@contoso.comexemple ), la livraison entrante None et la synchronisation côté serveur de la livraison sortante. Le nom de la file d’attente correspond à ce que les destinataires perçoivent comme l’expéditeur, donc nomme-le pour le portail plutôt que pour une personne. - Approuvez l’adresse. Deux drapeaux doivent s’aligner et ils résident sur des enregistrements différents : l’approbation de l’adresse e-mail de la file d’attente, et la boîte mail approuvée par l’administrateur O365. Une file d’attente est créée en attente sur les deux.
- Testez et activez la boîte aux lettres dans le centre d’administration Power Platform → Paramètres → Configuration des e-mails → Mailboxs. C’est l’étape à faire manuellement, et c’est un seul bouton.
Important
Une boîte aux lettres non approuvée ou non testée échoue silencieusement. Dataverse accepte l’email, l’enregistre et ne le livre jamais — ce qui, du côté du portail, est indiscernable d’un envoi réussi. Rien ne se déclenche, rien n’est enregistré comme une erreur, et le premier signe de problème est un utilisateur qui dit n’avoir jamais reçu son lien de confirmation. Si les emails d’inscription n’arrivent pas, vérifiez le statut sortant de la boîte mail avant toute autre chose : Not Run signifie que cette étape a été manquée.
5. Garder les secrets dans le coffre à clés
Donnez à l’application une identité gérée, accordez-lui un accès de lecture à un coffre-fort, et placez l’URI de chaque secret dans les paramètres de l’application au lieu du secret lui-même. App Service résout la référence pour vous, et la valeur n’apparaît jamais dans la page de configuration, dans un script ou dans un journal de déploiement.
az webapp identity assign --name myportal --resource-group myportal-rg --query principalId --output tsv
az keyvault create --name myportal-kv --resource-group myportal-rg --enable-rbac-authorization true
az role assignment create --assignee PRINCIPAL_ID --role "Key Vault Secrets User" --scope VAULT_RESOURCE_ID
az keyvault secret set --vault-name myportal-kv --name D365-ClientSecret --value THE_SECRET
az webapp config appsettings set --name myportal --resource-group myportal-rg --settings D365__ClientSecret="@Microsoft.KeyVault(SecretUri=https://myportal-kv.vault.azure.net/secrets/D365-ClientSecret/)"
Quand un secret client expire — Entra est par défaut à un an — ajoutez une nouvelle version au coffre-fort et redémarrez l’application. Le paramètre de l’application lui-même ne change pas.
Le serveur MCP fait toute cette séquence : create_managed_identity crée une identité attribuée par l’utilisateur, enable_managed_identity l’attache à l’application, create_key_vault crée le coffre et accorde à cette identité un accès à la lecture, puis les outils secrets acceptent destination: "key-vault" — ce qui envoie un client secret fraîchement créé d’Entra directement dans le coffre-fort et ne laisse que la référence dans les paramètres de l’application. La valeur ne devient jamais un réglage littéral et ne passe jamais entre les mains de quiconque.
Important
Un nom de Key Vault est réservé 90 jours après la suppression. La suppression progressive est activée par défaut et ne peut pas être désactivée, donc supprimer un vault ne libère pas son nom — recréer un nom portant le même nom dans cette fenêtre nécessite une purge explicite, qui nécessite elle-même une autorisation et est irréversible. C’est la seule ressource ici qui n’est pas facilement irréalisable, alors choisissez le nom aussi délibérément que celui du service d’applications.
Note
Mieux encore, supprimez complètement le secret client Dataverse. Réglez
D365__ManagedIdentityIdl’identifiant client d’une identité gérée et le portail authentifie Dataverse sous cette identité, sans secret et donc rien à expirer. Il n’y a pas de vérification d’environnement ni de commutateur de compilation : le paramètre appartient à la configuration du service d’application plutôt qu’àappsettings.json, donc il est absent localement et présent lors du déploiement, et une compilation se comporte correctement dans les deux. Utilisez une identité assignée par l’utilisateur — une identité assignée par le système est créée et détruite avec l’application et diffère selon le slot de déploiement, donc reconstruire l’application crée une nouvelle identité et orpheline que l’utilisateur de l’application Dataverse a construite sur l’ancienne. L’identité nécessite toujours cet utilisateur de l’application, créé à partir de son identifiant d’application plutôt que de l’id principal affiché par le portail Azure en premier.
6. Publier
Publiez selon le framework, zipez la sortie, poussez le zip. Sur un portail React, la même dotnet publish chose construit aussi le SPA et le met en scène dans wwwroot.
dotnet publish ./MyPortal/MyPortal.csproj -c Release -o ./publish
Compress-Archive -Path ./publish/* -DestinationPath ./publish.zip -Force
az webapp deploy --name myportal --resource-group myportal-rg --src-path ./publish.zip --type zip
Zipez le contenu du dossier publié, pas le dossier lui-même — App Service décompresse l’archive directement dans la racine du site, donc un répertoire imbriqué de premier niveau produit un site qui ne sert à rien.
La boîte de dialogue Publish de Visual Studio fait la même chose de manière interactive, et un workflow GitHub Actions ou Azure Pipelines le fait sur push. La seule étape supplémentaire pour un portail React est une étape de configuration Node en avance sur la compilation, puisque dotnet publish le CP est fourni à npm.
7. Pointer la licence vers l’URL déployée
La licence du site web est liée à une URL. Dans l’application modèle Power Portals Pro, ouvrez (ou créez) l’enregistrement Portal Web contenant votre clé de licence et réglez son URL sur l’URL de production du portail que vous venez de déployer — https://myportal.azurewebsites.net, ou votre domaine personnalisé si un domaine est mis devant.
Enregistrez uniquement l’URL de production . La validation accepte -devégalement , -test, -uat et -stage les hôtes suffixés, et les mêmes quatre comme sous-domaines principaux, donc les environnements inférieurs n’ont pas besoin d’enregistrement séparé. La page Licence contient la liste complète.
Important
Faites cela avant d’envoyer le trafic. Chaque point de terminaison de service de données vérifie la licence, donc tant que l’enregistrement n’existe pas et correspond à l’hôte, le portail affiche mais chaque appel de données revient
402.
8. Ajouter l’URI de redirection de production
Si le portail propose la connexion Microsoft, l’enregistrement de l’application de connexion ne connaît toujours que l’URI de redirection localhost. Ajoutez celui déployé à côté — Entra accepte plusieurs, donc le local continue de fonctionner :
https://myportal.azurewebsites.net/signin-microsoft
Même schéma pour les autres fournisseurs sur leurs propres chemins de rappel (/signin-google, /signin-facebook). Ajouter un par environnement, et un par domaine personnalisé.
Emplacements de déploiement
Un emplacement de staging et un swap vous offrent une application échauffée et un retour instantané. Ce sont les seuls éléments qui valent vraiment la peine de quitter B1 — et la raison de le quitter quand on en a besoin, plutôt qu’à l’avance. Deux choses nécessitent des soins.
Les clés de protection des données sont par emplacement. ASP.NET Core conserve le trépositeur sous %HOME%, dont chaque emplacement a sa propre copie, donc un échange le remplace — et chaque cookie d’authentification émis par le slot précédent devient indéchiffrable. Tout le monde est déconnecté. Déplacez le porte-clés quelque part que les deux emplacements partagent avant de compter sur l’échange :
builder.Services.AddDataProtection()
.PersistKeysToAzureBlobStorage(new Uri(blobUri), new DefaultAzureCredential())
.ProtectKeysWithAzureKeyVault(new Uri(keyUri), new DefaultAzureCredential());
Marquez les réglages spécifiques à l’environnement comme réglages de slot pour qu’ils restent en arrière pendant un échange. L’URL de l’environnement Dataverse est celle qui prend le dessus : sans elle, échanger un emplacement de staging pointant vers un bac à sable favorise cette connexion bac à sable en production.
Mise en échelle,
L’échellement est une décision à prendre quand on mesure le besoin, pas lors de la provision. Une instance du niveau par défaut transporte une quantité surprenante de trafic de portail.
- Blazor Server / Auto : gardez l’affinité de session activée, et prévoyez environ 250 Ko de mémoire serveur par circuit concurrent comme point de départ avant de mesurer le vôtre. Azure SignalR Service n’est pas requis sur App Service — c’est un outil pour des connexions très élevées ou des utilisateurs distribués globalement.
- React / Blazor WebAssembly : désactivez l’affinité de session et remettez à l’échelle horizontale. Les requêtes sont sans état, donc les instances sont interchangeables.
- Les modèles s’enregistrent
AddDistributedMemoryCache(), ce qui correspond à chaque instance. Sur plusieurs instances, échangez-les contre un véritable cache distribué afin que les métadonnées et les caches de localisation soient partagés plutôt que reconstruits sur chacun.
Faire tout cela depuis un agent IA
Le serveur Power Portals Pro MCP peut effectuer toutes les étapes de cette page. Liez le dossier à un abonnement une fois, sur un terminal — le lien appartient à ce dossier, donc plusieurs projets peuvent cibler différents clients en même temps, et rien ne change à l’échelle de la machine :
ppp-mcp azure login
check_deployment— celui à atteindre en premier. Il compare ce dont le projet a besoin avec ce que possède le service d’applications et nomme ce qui ne va pas : un réglage obligatoire qui n’a existé que dans vos secrets utilisateur, l’environnement laissé en mode Développement, des WebSockets désactivés sur un portail Blazor, un enregistrement Portal Website qui ne couvre pas l’hôte. Lecture seule, donc il est sûr de l’exécuter en production.get_deployment_plan— les étapes, et chaque niveau de plan avec son prix réel actuel issu de la liste publique de prix d’Azure.create_app_service— groupe de ressources, plan et application web, avec la paire WebSockets et affinité déjà configurée pour votre pile.set_app_service_settings— fusionne les paramètres au lieu de les remplacer, afin de ne pas effacer ceux qui contiennent vos secrets.deploy_to_app_service— construit avecdotnet publishet pousse le résultat.create_dataverse_app_registration,create_signin_app_registrationetadd_app_registration_redirect_uri— le côté Entra des étapes 6 et 7, en utilisant le rôle d’annuaire de votre compte connecté. Un secret client créé va directement dans les secrets utilisateur ou dans les paramètres du service App et n’est jamais affiché.create_managed_identity,enable_managed_identityetcreate_key_vault— l’identité et le coffre ci-dessus, avec les attributions de rôle faites pour vous et réessayées pendant qu’elles se propagent.create_email_queue— la file d’attente sans réponse de l’étape 4, créée et approuvée en un seul appel.
Note
Chaque outil qui modifie quoi que ce soit dans Azure vous demande d’abord de confirmer, et vous indique combien cela coûtera avant que vous ne répondiez — le chiffre mensuel réel pour votre région, avec une note indiquant que la liste de vente ignore tout accord d’entreprise ou réservation que vous détenez. Les outils qui ne lisent que ne demandent jamais. Le serveur n’utilise pas la ligne de ligne de ligne Azure et ne lit ni ne change jamais son état de connexion.
Dépannage
- Le site n’apparaît jamais. En général, un paramètre d’application manquant est requis. L’exception de démarrage nomme la clé — lisez-la dans Log stream ou Diagnostique et résolvez les problèmes.
- Le portail affiche mais chaque appel de données retourne 402. La vérification de licence rejette l’hôte. Confirmez que l’URL de l’enregistrement du site web du portail correspond à l’hôte utilisé réellement par le navigateur, et que la solution gérée est installée dans l’environnement auquel le portail est connecté. Le rejet est enregistré avec l’hôte exact, l’hôte transféré et le schéma qui ont été validés.
- Boucles de redirection, ou une
http://URL là où vous vous attendiezhttps://à . L’application ne respecte pas les en-têtes transférés depuis la interface du service applicatif. RéglezASPNETCORE_FORWARDEDHEADERS_ENABLED=truele schéma de requête et l’IP du client reflètent la requête initiale. - « Échec de se connecter via WebSockets, en utilisant le plan de secours Long Polling » dans la console du navigateur sur un portail Blazor — Web Sockets est toujours désactivé sur le service d’application.
- Tout le monde est déconnecté après un déploiement ou un échange. Le porte-clés de protection des données a changé ; voir les emplacements de déploiement ci-dessus.
- Un lien profond React 404s ou redirige vers localhost.
ASPNETCORE_ENVIRONMENTn’est pasProduction— le plan de secours SPA n’est enregistré qu’en dehors du développement. - Rien d’utile dans les journaux. La journalisation des applications des services d’applications est désactivée par défaut : activez la journalisation du système de fichiers au niveau des informations, puis la suivez avec
az webapp log tail.
