Menu
Comment simplifier mes projets cloud et DevOps avec .NET Aspire ?

Comment simplifier mes projets cloud et DevOps avec .NET Aspire ?

Par Anaïs ALEX

17.11.2025

.NET Aspire révolutionne le développement d’applications distribuées en offrant une orchestration simplifiée directement intégrée dans le cycle de développement local. Il intègre des outils puissants pour DevOps, en fournissant un cadre pour automatiser les tests, le déploiement et la surveillance.
Cette technologie change ma façon d'aborder le DevOps dans l'écosystème .NET.

Mon objectif ici est de présenter ce qu’est .NET Aspire, de montrer comment l’intégrer facilement dans un projet, d’explorer ses usages concrets, et de démontrer pourquoi il constitue une avancée majeure pour les équipes techniques en termes d’orchestration, de productivité et d’automatisation DevOps.

 

Qu’est-ce que .NET Aspire ?

.NET Aspire est une évolution récente de l’écosystème Microsoft :  Vue d’ensemble de Aspire - Aspire | Microsoft Learn.
Encore peu adoptée, elle mérite clairement plus d’attention.

C’est une stack complète pour créer et orchestrer des applications, qu’elles soient cloud ou locales.
Elle s’intègre naturellement à Azure (Service Bus, Storage, Event Hub, ...) mais sans obligation.
Aspire fonctionne aussi très bien hors Azure, sur d’autres environnements.

Ce qui m’intéresse surtout, c’est la philosophie DevOps derrière :

  • Aspire ne se limite pas au code.
  • Elle lie le développement et les opérations.
  • Elle permet à un Dev de pouvoir intégrer facilement de l'Ops : de la conteneurisation jusqu'au déploiement dans un cluster Kubernetes.
  • C’est exactement ce qui manquait à l’écosystème .NET.

 

Une orchestration intégrée entre Dev et Ops

Aujourd’hui, Aspire propose un vrai lien entre le développement applicatif et la gestion opérationnelle (Aspire orchestration overview - Aspire | Microsoft Learn).

Avec les modèles de projets Visual Studio, il est facile de créer un projet .net Aspire. Des librairies sont disponibles afin de faciliter la déclaration et la connexion aux différentes ressources du projet.
Le fait de pouvoir définir toutes ces ressources dans le code permet de les orchestrer très facilement. En effet, si un service nécessite une base de données pour fonctionner, il est préférable que celle-ci soit démarré en premier, de vérifier qu'elle soit healthy and ready avant de démarrer le service. C'est très facilement faisable depuis le code.

Net Aspire est disponible à partir de .Net 8. Les différents projets peuvent être lancés soit en mode conteneur soit en mode processus. Pour la conteneurisation, .Net Aspire repose sur Docker Desktop et Podman.. Pour la conteneurisation, il n'est plus nécessaire d'écrire un docker-compose. Ce mode est supporté mais il est possible de le décrire par le code.
Résultat : des dépendances gérées proprement, des ressources prêtes à déployer.
Et surtout, une transition fluide entre local et déploiement sur l’environnement de production.

 

Le projet AppHost : une surcouche légère mais puissante

L’un des éléments centraux d’Aspire, c’est le projet AppHost (Aspire Configuration d’AppHost - Aspire | Microsoft Learn).

C’est un nouveau projet que l’on créé ou l’on ajoute à une solution existante, sans modifier le code métier.

Concrètement, si je possède déjà une API .NET, un projet Blazor, ou un service worker, je peux conserver l’intégralité de mon code.

Il me suffit d’ajouter un projet AppHost,  de référencer mes différents projets, et Aspire s’occupe du reste.

Dans mon projet AppHost, je vais définir mes différents services, leur ordre de démarrage et leur dépendance.

Pas de configuration complexe.
Pas de risque de casser le code.
C’est propre, simple, et efficace.

Exemple de définition du Program.cs d’un AppHost pour deux bases de données, un back-end et un front-end avec leur interdépendance.

 

    var builder = DistributedApplication.CreateBuilder(args);
 
var postgres = builder.AddPostgres(name: "postgres")
    .WithImage("pgvector/pgvector")
    .WithImageTag("0.8.0-pg17")
    .WithEndpoint(name: "postgresendpoint", scheme: "tcp", port: 5432, targetPort: 5432, isProxied: false)
    .WithLifetime(ContainerLifetime.Persistent)
    .WithPgAdmin()
    .PublishAsConnectionString();
 
var qdrant = builder.AddQdrant("qdrant")
                    .WithLifetime(ContainerLifetime.Persistent)
                    .WithDataVolume()
                    .PublishAsConnectionString();
 
var apiService = builder.AddProject<Projects.WhatShouldIRemember_ApiService>("apiservice")
    .WithReference(postgres)
    .WaitFor(postgres)
    .WithReference(qdrant)
    .WaitFor(qdrant);
 
builder.AddProject<Projects.WhatShouldIRemember_Web>("webfrontend")
    .WithExternalHttpEndpoints()
    .WithReference(apiService)
    .WaitFor(apiService);
 
builder.Build().Run();


 

Containerisation et connexions simplifiées

Aspire facilite la containerisation et la configuration des services.
Je peux facilement debugger mon application si mes services sont lancés en mode processus (.AddProject) ou directement dans un conteneur Docker (.AddContainer) comme elle tournerait en production.

Dans le projet de type AppHost, je déclare mes services : API, base, front, composants. Aspire relie tout automatiquement. Si la déclaration des composants “externes” comme les bases de données est défini via les librairies .Net Aspire : il n’y a plus besoin de chaine de connexion. Le lien se fait directement.

De nombreux types de définitions de ressources sont disponibles :

  • AddAzureStorage : stockage type blob storage
  • AddContainer : démarrage en mode conteneur
  • AddProject : démarrage en mode processus
  • AddDockerfile : créer une ressource à partir d’un Dockerfile existant
  • AddPostgres : ajout d’une base de données PosgreSQL
  • AddResource : ajout d’une ressource externe


Dans le cas de l’ajout d’une ressource externe, si la version de l’image n’est pas précisée, ce sera généralement la plus à jour de Docker Hub qui sera prise. De plus, si les ports ne sont pas précisés, les ports par défaut seront exposés. Tout cela en écrivant du code C# et non des fichiers Dockerfile et docker-compose. Un réseau virtuel “Aspire” est créé et regroupe toutes les ressources. Concernant les bases de données, il est possible de les définir en mode persistante ou en mode session en fonction de si on veut la détruire avec chaque fois ou non.

En résumé : Aspire automatise toute la chaîne de containerisation et de déploiement Kubernetes, de manière transparente pour le développeur. La partie déploiement Kubernetes sera expliquée plus loin.

 

Le Dashboard Aspire : supervision claire et directe

L’un des points forts de .NET Aspire, c’est son Dashboard intégré, qui se lance automatiquement avec le projet AppHost.

C’est une interface de supervision complète, permettant de suivre :

  • l’état de chaque ressource ;
  • les logs et métriques au format OpenTelemetry par défaut ;
  • le statut des dépendances entre services ;
  • et même le redémarrage manuel de certains composants ou ressources.
  • d'accéder aux variables d’environnement des différents services

C’est un outil précieux, que ce soit pour le développement local ou pour les environnements de production. Il est aussi possible de désactiver le dashboard si nécessaire.

Il offre une visibilité immédiate sur les composants actifs et facilite le monitoring.

Dashboard supervision Aspire
Dashboard intégré de supervision Aspire

Cas concret : une API, une base et un front

apphost project

J’ai testé Aspire sur un environnement complet :
PostgreSQL + backend .NET + frontend web.

Tout est défini dans l’AppHost : d’abord la base de données, ensuite le backend (l’API) qui a une dépendance sur la base de ,donnée, puis le frontend qui a une dépendance sur le backend ainsi que les paramètres de configuration des différents services.

Depuis le Dashboard Aspire, je visualise chaque service, les ports exposés, leurs statuts, et les journaux d’exécution.

Concernant la base PostgreSQL, il est possible de démarrer l’interface PostgreSQL (.WithPgAdmin) pour explorer la base. Le dashboard me permet d’accéder à mon API REST via le Swagger. En effet, le lien de l’api est directement accessible depuis le dashboard. Je peux ainsi tester et valider que tout fonctionne, sans configuration supplémentaire.
C’est un véritable environnement orchestré, qui se comporte déjà comme un déploiement en production tout en restant exécutable en local.

 

Intégrer les tests dans Aspire

Au-delà du développement, Aspire facilite la mise en place de tests automatisés.
Il s’intègre avec MSTest, xUnit et NUnit, et permet de monter des environnements de test complets et isolés.
Je peux créer un projet de test qui référence le projet AppHost via les librairies .Net Aspire.
    
        var appHost = await DistributedApplicationTestingBuilder
            .CreateAsync<Projects.WhatShouldIRemember_AppHost>();
        appHost.Services.AddRouting();
        appHost.Services.ConfigureHttpClientDefaults(clientBuilder =>
        {
            clientBuilder.AddStandardResilienceHandler();
        });
        appHost.Services.AddLogging(logging =>
        {
            logging.SetMinimumLevel(LogLevel.Debug);
            logging.AddConsole();
            logging.AddDebug();
        });
 
        App = await appHost.BuildAsync();
 
        var resourceNotificationService = App.Services.GetRequiredService<ResourceNotificationService>();
 
        await App.StartAsync();
 
        await resourceNotificationService.WaitForResourceAsync("postgres", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));
        await resourceNotificationService.WaitForResourceAsync("qdrant", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));
        await resourceNotificationService.WaitForResourceAsync("apiservice", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));


Les services démarrent, les dépendances sont prêtes, les tests s’exécutent.
Par exemple :

  • je crée un client HTTP vers mon API démarré par mon AppHost,
  • j’envoie un POST,
  • je vérifie que la donnée est bien en base de données.
    [ClassInitialize]
public static void ClassInit(TestContext context)
{
    _client = App.CreateHttpClient("apiservice");
}
 
[TestMethod]
public async Task PostNote_ShouldReturnOk()
{
    var noteContent = $"this is a test note {Guid.NewGuid()}";
    var noteRequest = new NoteRequestDto(noteContent);
 
    // Act  
    var response = await _client.PostAsJsonAsync($"/notes", noteRequest);
 
    // Assert  
    response.EnsureSuccessStatusCode();
 
    var dbHelper = new DbHelper(App);
    var result = await dbHelper.RequestNotesAsync(noteContent);
    result.Should().NotBeNull();
    result.Should().Be(noteContent);
}
Les conteneurs sont interconnectés, ce qui rend possible des tests d’intégration réalistes, proches du comportement réel en production.

Si mes différents services sont lancés en mode processus, je peux également déboguer mes tests sans avoir besoin de lancer un autre IDE, poser des points d’arrêt dans mes services, et analyser le code en exécution. C’est une intégration fluide, sans rupture dans le cycle de développement.

Simuler des ressources Azure

L’un des cas que j’ai rencontrés concernait la gestion des Azure Blob Storage.
Pour stocker les fichiers de configuration et les logs des devices, mon service utilise normalement Azure Storage.
Mais pour les tests, utiliser le vrai service Azure aurait été trop coûteux et complexe à isoler.
J’ai donc utilisé Azurite, l’émulateur local d’Azure Storage, directement intégré à .NET Aspire.

Grâce à la commande AddAzureStorage et RunAsEmulator, Aspire monte un conteneur local simulant le Blob Storage basé sur l’émulateur Azurite.
Je peux même le configurer pour qu’il fonctionne en HTTPS, afin de respecter les contraintes de sécurité sans modifier le code.
Ainsi, mes tests utilisent une version émulée, sécurisée, et cohérente avec l’environnement réel.

C’est aussi le cas pour d’autres ressources Azure comme :

  • Azurite pour Azure storage
  • Cosmos DB emulator
  • Service Bus emulator
  • ...

    var storage = builder.AddAzureStorage("storage")
.RunAsEmulator(azurite =>
  {
      azurite
              .WithEndpoint(port: 10000, targetPort: 10000, name: "httpsblob", isProxied: false)
              .WithEndpoint(port: 10001, targetPort: 10001, name: "httpsqueue", isProxied: false)
              .WithEndpoint(port: 10002, targetPort: 10002, name: "httpstable", isProxied: false)
              .WithContainerName("azurite")
              .WithBindMount(source: @"./Cert/cert2.key", target: "/azurite/cert2.key", isReadOnly: true)
              .WithBindMount(source: @"./Cert/cert2.crt", target: "/azurite/cert2.crt", isReadOnly: true)
              .WithArgs("--cert", "/azurite/cert2.crt", "--key", "/azurite/cert2.key", "--oauth", "basic", "--pwd", "password", "--tablePort", "0")
              .WithExternalHttpEndpoints()
              .WithBlobPort(10000)
              .WithDataVolume();
  });

 

Déploiement Kubernetes automatisé

Le déploiement, souvent la partie la plus complexe, devient plus simple.
Il existe un outil complémentaire, Aspir8 ou Aspirate, qui génère automatiquement des manifestes Kubernetes et qui permet leur déploiement.

Avec la CLI Aspire8, en seulement trois commandes il est possible de déployer une application dans son cluster kubernetes :

  • aspire init : initialise le projet (aspirate.json)
  • aspire generate : crée les fichiers YAML : manifestes kubernetes. En fonction des différentes extensions, il s’occupe de faire le découpage entre ConfigMap et Secret)
  • Aspire apply : déploie les manifestes sur le cluster Kubernetes choisi. Ils sont protégés via un mot de passe défini lors de l’init.


En quelques secondes, Aspirate crée les images Docker, génère les manifestes avec les ConfigMaps, les Secrets, et déploie les pods sur Kubernetes.

Il est même possible de supprimer proprement les ressources avec aspire destroy.

Cependant, tout dépend de la définition faite dans le code : elle doit être complète et propre afin de définir au mieux les manifestes. En effet, si ce n’est pas le cas et que des modifications sont effectuées à la main dans Kubernetes, elles seront perdues avec un aspire destroy. Les étapes de génération et déploiement via Aspirate peuvent être intéressante pour un premier déploiement et des tests. Il faut cependant prendre des précautions quant à l’utilisation en production.

Tout cela est compatible avec les outils DevOps, notamment Azure DevOps et les pipelines CI/CD modernes.Aspire s’intègre parfaitement dans les pipelines Azure DevOps ou tout autre CI/CD.

C’est propre, reproductible et cloud-native.


Retour d’expérience : un projet client

J’ai utilisé Aspire sur un projet concret.
L’objectif : gérer des API REST pour des devices connectés.
Nous avion deux modes de déploiement :

  • une version cloud,
  • une version on premise.


Avant, les tests d’intégration étaient complexes à maintenir.
Il fallait tout instancier et démarrer manuellement, gérer les ports, surveiller les dépendances.
Avec Aspire, tout s’est automatisé et les tests sont facilement debuggable sans modification du code métier.
J’ai ajouté un AppHost, défini les dépendances, et intégrer cette nouvelle version de test dans mon pipeline de CI Azure DevOps.


Résultat :

  • des tests fiables,
  • une intégration simple,
  • une maintenance simplifiée.

C’est un vrai levier d’efficacité technique.


Pourquoi adopter .NET Aspire ?

Aspire apporte une vraie cohérence technique.
Il unifie développement, containerisation, tests et déploiement, sans complexité supplémentaire.

Je garde mon code et j’obtiens un environnement complet, automatisé et reproductible.
C’est une approche moderne, alignée avec la culture DevOps.


Pour une équipe .NET, c’est une solution claire pour accélérer les projets qu’il soit cloud-native ou autre.

 

Les bénéfices concrets

Adopter Aspire, c’est :

  • Gain de temps : création automatique des environnements et des dépendances.
  • Cohérence : environnement identique en dev, test et production.
  • Fiabilité : orchestration maîtrisée.
  • Tests intégrés : environnements isolés, compatibles avec MSTest, xUnit et NUnit.
    Simplicité du delivery : génération automatique des images Docker et déploiement Kubernetes en quelques commandes. Pour la partie déploiement, il faut toutefois faire attention pour un déploiement en production.
  • Interopérabilité : fonctionne aussi bien sur Azure que sur d’autres infrastructures.

 

En conclusion

.NET Aspire n’est pas une révolution, c’est une évolution intelligente et efficace.

  • Elle simplifie la mise en place d’environnements cloud ou non et DevOps.
  • Elle reste fidèle à l’écosystème .NET.
  • Elle fait gagner du temps, réduit les erreurs, et améliore la maintenabilité.


C’est un outil pensé pour les équipes techniques et une façon moderne et pragmatique de développer, tester et déployer.
Pour moi, Aspire est la suite logique du développement .NET.

Un pas naturel vers plus de DevOps et de cohérence.


Retour aux articles

C'est à lire...