.NETC#AI AgentsMicrosoft FoundryDevOpsGitHub ActionsCI/CD

Passez d’un agent C# local à un agent Microsoft Foundry hébergé, versionné et contrôlé par GitHub Actions, azd et une identité OIDC sans secret durable.

Yva Hajatiana
3 septembre 2026
11 min de lecture
Partager :X / TwitterLinkedIn
Composition éditoriale abstraite sur le déploiement fiable d’un agent IA

Déployer un agent Foundry .NET avec CI/CD sécurisé

Mettre un agent .NET en production ne se résume pas à publier une API qui appelle un modèle. Il faut aussi versionner le code qui expose l’agent, tester son protocole avant et après le déploiement, rattacher chaque version à un commit et donner à la CI uniquement les droits dont elle a besoin.

Les agents hébergés Microsoft Foundry répondent à ce besoin : votre code C# est exécuté dans une infrastructure gérée, tandis que Foundry prend en charge le cycle de vie de l’agent, sa session et son identité. Depuis l’été 2026, azd apporte une configuration unifiée dans azure.yaml, un mode de déploiement de code adapté à .NET et des commandes pour exécuter puis invoquer l’agent. Microsoft fournit aussi un modèle GitHub Actions qui déploie et teste l’agent avec OpenID Connect (OIDC), sans conserver de secret client Azure.

Ce tutoriel crée un agent C# minimal qui expose le protocole Responses, le teste localement, le décrit avec azd, puis ajoute une pipeline de déploiement vers un environnement staging. Le contrôle CI vérifie qu’une version est réellement invocable : ce n’est pas une évaluation de la qualité des réponses, à traiter séparément.

État des composants. Le service d’agents hébergés est disponible généralement, mais les packages .NET de l’intégration Foundry restent publiés en préversion. Épinglez leurs versions dans un projet réel, relisez les notes de version et validez toute montée de version dans staging avant la production.

Ce que l’hébergement géré change

Un agent hébergé est une application conteneurisée qui exécute votre code sur l’infrastructure Microsoft Foundry. Ce n’est donc pas simplement un agent créé par configuration dans un portail : le code, ses dépendances et ses outils C# font partie du livrable.

ResponsabilitéApplication auto-hébergéeAgent Foundry hébergé
Démarrage HTTP et mise à l’échelleÉquipe plateformeService Foundry
Logique, prompts, outils et testsÉquipe applicativeÉquipe applicative
Identité de l’agent déployéÀ créer et gérerIdentité dédiée fournie par la plateforme
Historique de conversationÀ persister et isolerGéré par le protocole Responses
Livraison du codeImage ou runtime à opérerazd deploy crée une version d’agent

Cette séparation est particulièrement utile quand un agent doit appeler des outils C# ou des services internes tout en restant déployable comme n’importe quel autre composant. Elle ne dispense pas de concevoir les autorisations : l’identité de l’agent et celle de la CI ont des rôles différents.

commit Git
   |
   v
GitHub Actions -- OIDC --> Azure / Foundry
   |                         |
   | azd deploy              v
   +------------------> nouvelle version de l’agent
                                  |
                                  v
                         azd ai agent invoke
                                  |
                                  v
                         résultat du test de fumée

Pour un workflow long, avec reprise après panne et approbation humaine, complétez cette base avec les patterns de workflow d’agents .NET durables. Ici, l’objectif est plus étroit : rendre la livraison de l’agent elle-même répétable et vérifiable.

Prérequis et frontières de sécurité

Prévoyez :

  • le SDK .NET 10 ou ultérieur ;
  • Azure CLI connecté au bon tenant avec az login ;
  • Azure Developer CLI (azd) 1.27.1 ou ultérieur ;
  • un projet Microsoft Foundry avec un déploiement de modèle, ou les droits nécessaires pour en provisionner un ;
  • un dépôt GitHub si vous souhaitez exécuter la CI.

Installez les extensions azd requises et vérifiez-les avant de créer un projet :

azd ext install azure.ai.agents
azd ext install azure.ai.projects
azd ext list

Ne copiez ni clé de modèle ni mot de passe dans azure.yaml, le code C# ou les variables GitHub. L’exemple utilise DefaultAzureCredential : localement, il peut employer votre session Azure CLI ; dans Foundry, le runtime reçoit l’identité gérée de l’agent. La CI, elle, s’authentifie avec un jeton OIDC éphémère émis par GitHub.

Créez ensuite le projet :

dotnet new web --framework net10.0 --output src/SupportAgent
Set-Location src/SupportAgent

dotnet add package Microsoft.Agents.AI.Foundry.Hosting --prerelease
dotnet add package Azure.AI.Projects --prerelease
dotnet add package Azure.Identity

Dans une application livrée, remplacez --prerelease par les versions précises déjà validées et commitez le fichier packages.lock.json :

dotnet restore --use-lock-file

1. Exposer un agent C# par le protocole Responses

Le protocole Responses fournit un endpoint HTTP compatible OpenAI à l’adresse /responses. C’est le meilleur point de départ si votre client attend une conversation standard, du streaming et une gestion de session par la plateforme.

Remplacez Program.cs par ce code :

using Azure.AI.AgentServer.Core;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Foundry.Hosting;

var projectEndpoint = new Uri(
    Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")
    ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT est requis."));

var deployment = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME")
    ?? throw new InvalidOperationException("AZURE_AI_MODEL_DEPLOYMENT_NAME est requis.");

AIAgent agent = new AIProjectClient(projectEndpoint, new DefaultAzureCredential())
    .AsAIAgent(
        model: deployment,
        name: "support-agent",
        instructions: """
            Tu aides l’équipe de support. Réponds en français, brièvement.
            N’invente ni procédure ni statut de service. Si le contexte ne suffit pas,
            indique clairement ce qu’il faut vérifier.
            """);

var builder = AgentHost.CreateBuilder(args);
builder.Services.AddFoundryResponses(agent);
builder.RegisterProtocol("responses", endpoints => endpoints.MapFoundryResponses());

var app = builder.Build();
app.Run();

AgentHost.CreateBuilder prépare l’hôte attendu par Foundry. AddFoundryResponses enregistre l’agent, puis MapFoundryResponses publie le contrat HTTP. Ne créez pas une seconde base d’historique de conversation dans ce code : pour le protocole Responses, la plateforme gère déjà le cycle de vie et l’historique de session.

Le contrat de l’agent est volontairement simple. Ajoutez ensuite vos fonctions C#, votre validation d’entrées et vos appels à des services internes, mais conservez une frontière nette : les outils ayant un effet de bord doivent vérifier l’autorisation côté code, et non obéir uniquement à une instruction du modèle.

2. Décrire le déploiement dans azure.yaml

Initialisez le projet pour azd. Si vous partez d’un projet Foundry existant, sélectionnez-le pendant l’assistant ; sinon, azd provision créera les ressources déclarées.

azd ai agent init --protocol responses --deploy-mode code

Le mode code est le choix naturel pour un agent .NET : azd envoie le code source et réalise la construction distante. Le fichier azure.yaml devient la source de vérité des ressources et du déploiement. Voici une base à adapter, notamment au nom et à la version d’un modèle réellement disponible dans votre région :

# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json
name: support-agent

requiredVersions:
  azd: ">=1.27.1"
  extensions:
    azure.ai.agents: ">=1.0.0-beta.8"
    azure.ai.projects: ">=1.0.0-beta.4"

services:
  ai-project:
    host: azure.ai.project
    deployments:
      - name: ${AZURE_AI_MODEL_DEPLOYMENT_NAME}
        model:
          format: OpenAI
          name: gpt-5.4-mini
          version: "2026-03-17"
        sku:
          name: GlobalStandard
          capacity: 10

  support-agent:
    host: azure.ai.agent
    kind: hosted
    name: support-agent
    project: .
    language: docker
    uses:
      - ai-project
    protocols:
      - protocol: responses
        version: 2.0.0
    env:
      AZURE_AI_MODEL_DEPLOYMENT_NAME: ${AZURE_AI_MODEL_DEPLOYMENT_NAME}
    container:
      resources:
        cpu: "0.5"
        memory: 1Gi

Le bloc uses est important : il déclare la dépendance de l’agent envers le projet Foundry, ce qui permet à azd d’ordonner les opérations. Les substitutions ${NOM} sont résolues par azd depuis l’environnement actif ; elles ne doivent pas recevoir un secret committé.

Créez un environnement de développement et fournissez seulement la configuration non secrète :

azd env new dev
azd env set AZURE_AI_MODEL_DEPLOYMENT_NAME "<nom-du-deploiement>"
azd provision

azd down peut supprimer le groupe de ressources créé par cet environnement. N’exécutez cette commande que si vous avez vérifié l’environnement actif et l’impact sur les ressources partagées.

3. Tester localement avant le cloud

Le test local raccourcit la boucle de développement : le code tourne sur votre poste et se connecte au projet Foundry avec votre identité Azure. azd injecte les variables de l’environnement actif lors de l’exécution.

Dans un premier terminal :

azd ai agent run

Dans un second terminal, lancez un test de fumée :

azd ai agent invoke --local "Bonjour. Réponds par une confirmation courte."

Pour vérifier le contrat HTTP sans dépendre de la CLI, vous pouvez aussi appeler l’endpoint local :

$body = @{ input = "Bonjour. Réponds par une confirmation courte." } | ConvertTo-Json
(Invoke-WebRequest `
  -Uri "http://localhost:8088/responses" `
  -Method POST `
  -ContentType "application/json" `
  -Body $body).Content

Un test de fumée répond à une seule question : l’agent déployable démarre-t-il et répond-il au protocole ? Il ne prouve pas que la réponse est utile, sûre ou correcte. Pour cela, versionnez des scénarios et des seuils d’évaluation comme expliqué dans Évaluer une application IA .NET dans une pipeline CI.

4. Configurer l’identité OIDC de GitHub

Créez une application Microsoft Entra et une identité fédérée limitée à votre dépôt et à l’environnement GitHub staging. Le sujet de cette fédération doit empêcher une branche arbitraire ou un fork d’obtenir un jeton de déploiement. Attribuez ensuite à cette identité les rôles exigés par votre mode de déploiement : pour un déploiement de code, Microsoft indique Foundry User et Contributor sur le projet Foundry cible.

Dans les variables GitHub Actions de l’environnement staging, ajoutez les valeurs de configuration suivantes :

VariableRôle
AZURE_CLIENT_IDID de l’application fédérée OIDC
AZURE_TENANT_IDTenant Microsoft Entra
AZURE_SUBSCRIPTION_IDAbonnement cible
AZURE_LOCATIONRégion de l’environnement
FOUNDRY_PROJECT_ENDPOINTEndpoint du projet Foundry
AZURE_AI_PROJECT_IDIdentifiant du projet Foundry
FOUNDRY_MODEL_NAMEDéploiement de modèle attendu

Ce ne sont pas des clés d’accès, mais évitez malgré tout de les afficher inutilement dans les journaux. Les mots de passe, clés d’API et chaînes de connexion restent dans un coffre de secrets ou dans une connexion Foundry résolue côté plateforme, jamais dans une variable de dépôt en clair.

5. Déployer et vérifier dans GitHub Actions

Ajoutez .github/workflows/hosted-agent-staging.yml. Le workflow s’exécute sur une branche protégée, obtient un jeton OIDC à durée courte, déploie puis invoque l’agent distant. L’étape show laisse une trace diagnostique de la version activée.

name: Deploy hosted agent to staging

on:
  push:
    branches: [main]
    paths:
      - "src/SupportAgent/**"
      - "azure.yaml"
      - ".github/workflows/hosted-agent-staging.yml"
  workflow_dispatch:

permissions:
  contents: read
  id-token: write

concurrency:
  group: foundry-agent-staging
  cancel-in-progress: false

env:
  AGENT_PROJECT_DIR: ./src/SupportAgent
  AZD_ENV_NAME: staging
  AGENT_TEST_PROMPT: "Bonjour depuis la CI. Confirme brièvement que tu réponds."

jobs:
  deploy-and-smoke-test:
    runs-on: ubuntu-latest
    environment: staging

    steps:
      - uses: actions/checkout@v4

      - name: Install Azure Developer CLI
        uses: Azure/setup-azd@v2

      - name: Install Foundry extensions
        run: |
          set -euo pipefail
          azd ext install azure.ai.agents
          azd ext install azure.ai.projects
          azd ai agent --help > /dev/null

      - name: Sign in to Azure with OIDC
        uses: azure/login@v2
        with:
          client-id: ${{ vars.AZURE_CLIENT_ID }}
          tenant-id: ${{ vars.AZURE_TENANT_ID }}
          subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

      - name: Configure azd environment
        working-directory: ${{ env.AGENT_PROJECT_DIR }}
        run: |
          set -euo pipefail
          azd config set auth.useAzCliAuth true
          azd config set defaults.subscription "${{ vars.AZURE_SUBSCRIPTION_ID }}"
          azd env select "$AZD_ENV_NAME" || azd env new "$AZD_ENV_NAME" --no-prompt
          azd env set AZURE_SUBSCRIPTION_ID "${{ vars.AZURE_SUBSCRIPTION_ID }}"
          azd env set AZURE_TENANT_ID "${{ vars.AZURE_TENANT_ID }}"
          azd env set AZURE_LOCATION "${{ vars.AZURE_LOCATION }}"
          azd env set FOUNDRY_PROJECT_ENDPOINT "${{ vars.FOUNDRY_PROJECT_ENDPOINT }}"
          azd env set AZURE_AI_PROJECT_ID "${{ vars.AZURE_AI_PROJECT_ID }}"
          azd env set FOUNDRY_MODEL_NAME "${{ vars.FOUNDRY_MODEL_NAME }}"

      - name: Deploy agent
        working-directory: ${{ env.AGENT_PROJECT_DIR }}
        run: azd deploy --no-prompt

      - name: Show deployed agent status
        working-directory: ${{ env.AGENT_PROJECT_DIR }}
        run: azd ai agent show --no-prompt

      - name: Smoke-test the deployed agent
        working-directory: ${{ env.AGENT_PROJECT_DIR }}
        run: |
          set -euo pipefail
          azd ai agent invoke "$AGENT_TEST_PROMPT" > /tmp/agent-response.txt 2>&1
          test -s /tmp/agent-response.txt
          sed -n '1,80p' /tmp/agent-response.txt

Adaptez AGENT_PROJECT_DIR et le filtre paths à votre dépôt. Ne lancez pas un déploiement ayant accès à Azure depuis une pull request issue d’un fork. L’environnement GitHub staging doit aussi porter des règles de branche et, pour la production, une approbation humaine distincte.

La réponse non vide constitue un seuil volontairement bas : elle détecte un hôte qui ne démarre pas, une mauvaise configuration du protocole ou une identité sans droit d’invocation. Ajoutez un test de contrat déterministe pour les propriétés réellement stables de votre agent, puis une campagne d’évaluation séparée et protégée pour les critères LLM. Mélanger ces trois niveaux dans un unique test rend les incidents difficiles à diagnostiquer.

Points de vigilance avant la production

  • Ne provisionnez pas à chaque push. Dans une CI quotidienne, utilisez azd deploy sur un environnement déjà existant. Réservez azd provision à une étape contrôlée d’infrastructure.
  • Séparez CI et agent. La CI peut créer une version ; l’identité de l’agent doit recevoir séparément le seul accès nécessaire à ses outils et données d’exécution.
  • Rendez les effets de bord idempotents. Un retry de plateforme, un redéploiement ou une reprise ne doit pas créer deux tickets, deux commandes ou deux changements de configuration.
  • Épinglez et mettez à jour consciemment. Les extensions azd et packages de préversion évoluent vite. Déclarez les versions minimales, utilisez un lockfile NuGet et testez leur compatibilité dans un environnement isolé.
  • Surveillez après le déploiement. Le test de fumée ne remplace ni les traces, ni les métriques, ni les alertes. Corrélez la version déployée avec les appels, erreurs et latences ; le guide sur OpenTelemetry et Aspire détaille cette couche.

Sources

Mots-clés :.NETC#AI AgentsMicrosoft FoundryDevOpsGitHub ActionsCI/CD
Y

Yva Hajatiana

Articles techniques sur l'ingénierie logicielle, .NET, le cloud et l'intelligence artificielle appliquée aux applications.