Authentification GCP quand on gère 10 clients et un agent IA

Je gère une douzaine de projets GCP chaque semaine. Différents clients, différents comptes de facturation, différents datasets. Un matin typique, je fais un cd dans le projet dbt d’un client, je lance un build, je passe à un autre terminal pour requêter BigQuery pour un deuxième client, et Claude Code travaille sur un troisième en arrière-plan. Ça fonctionne très bien, jusqu’au moment où ça ne fonctionne plus. Et quand ça casse, on se retrouve avec des erreurs d’authentification cryptiques, des tokens expirés, ou un agent qui s’arrête en pleine tâche parce qu’il a perdu l’accès aux identifiants du bon projet.

Si vous travaillez dans un cabinet de conseil ou en freelance sur plusieurs projets GCP, ça va vous arriver.

Le défaut de conception au cœur du problème

Le CLI gcloud stocke tout dans un seul répertoire : ~/.config/gcloud/. Un seul jeu d’identifiants, une seule configuration active, un seul fichier Application Default Credentials, partagés entre toutes les fenêtres de terminal et tous les processus de la machine. Quand vous lancez gcloud config set project client-a, ce changement affecte toutes les autres sessions qui lisent le même répertoire. AWS gère ça différemment, avec des profils nommés et sans contexte actif partagé. Google a choisi un état global mutable.

Le problème s’aggrave parce que gcloud a en réalité deux systèmes d’authentification indépendants que les gens confondent en permanence.

gcloud auth login authentifie les outils CLI (gcloud, bq, gsutil). Les tokens OAuth sont stockés dans credentials.db à l’intérieur de ce répertoire global.

gcloud auth application-default login crée le fichier application_default_credentials.json utilisé par les bibliothèques clientes : Python, Node.js, Terraform, dbt, tout ce qui appelle google.auth.default().

Lancer l’un n’affecte pas l’autre. On peut très bien avoir gcloud qui pointe vers le Projet A pendant qu’un script Python s’authentifie sur le Projet B. Le résultat est généralement une erreur de permission incompréhensible, jusqu’à ce qu’on réalise que les identifiants ne correspondent pas au projet qu’on croit cibler.

Le scénario classique : vous lancez un build dbt pour le Client A, puis vous changez votre contexte gcloud vers le Client B dans un autre terminal. Votre fichier ADC est écrasé, ou la configuration active change, et soudain le build du Client A échoue avec des erreurs d’auth à mi-parcours. Ou vous lancez Claude Code dans un nouveau terminal et il récupère des identifiants périmés d’une session d’un autre client. Avec un humain qui fait une chose à la fois, c’est gérable. Avec des agents IA qui exécutent des commandes gcloud en parallèle, les conflits d’authentification deviennent une interruption permanente.

Les agents IA aggravent le problème

Le problème multi-projets existe depuis la création de gcloud. Ce qui a changé, c’est que les agents IA ont introduit une nouvelle catégorie de contraintes qui transforment un désagrément occasionnel en risque quotidien.

Les agents IA ne peuvent pas compléter les flux OAuth interactifs. gcloud auth login ouvre une fenêtre de navigateur pour le consentement, ce qu’un agent en terminal ne peut pas gérer. Ils ne peuvent pas se ré-authentifier quand les tokens expirent en cours de session. Et quand plusieurs agents tournent simultanément, ils écrasent mutuellement leurs fichiers de configuration, ce qui provoque des erreurs d’auth pénibles à diagnostiquer.

Claude Code

Claude Code s’appuie entièrement sur les identifiants d’environnement pour se connecter à Vertex AI. Il utilise l’ADC standard, ce qui signifie qu’il faut une pré-authentification via gcloud auth login et gcloud auth application-default login avant de démarrer une session.

Un problème documenté (GitHub #726) affecte les organisations qui imposent des restrictions de durée de session GCP. Quand les identifiants expirent, Claude Code ne récupère pas les tokens rafraîchis. Il continue d’utiliser les identifiants en cache et échoue avec des erreurs invalid_grant. La seule solution est de redémarrer entièrement Claude Code. L’authentification en environnement headless est encore une demande ouverte (GitHub #7100), sans support du device code flow pour l’instant.

Il y a aussi une dimension sécurité. Des recherches ont montré que des dépôts malveillants peuvent utiliser des intégrations MCP ou des hooks shell pour exfiltrer les identifiants gcloud actifs avant même que vous n’ayez accordé votre confiance au projet. C’est pourquoi l’isolation via CLOUDSDK_CONFIG compte au-delà du problème multi-clients : elle limite le rayon d’impact d’un vol d’identifiants au répertoire de configuration temporaire d’un seul projet.

OpenAI Codex

Codex adopte l’approche inverse. Il tourne dans des conteneurs isolés sans accès internet par défaut. Les secrets peuvent être fournis via des variables d’environnement lors de la configuration, mais ils sont supprimés avant le démarrage de la phase d’exécution de l’agent. L’accès persistant aux identifiants GCP est donc architecturalement impossible sans activer l’accès réseau et câbler explicitement les identifiants soi-même.

Codex a ajouté l’authentification par device code (codex login --device-auth) pour sa propre authentification, mais la gestion des identifiants GCP reste entièrement manuelle. Le modèle d’isolation est solide par conception. Ça signifie simplement que tout le câblage des identifiants se fait en amont.

Autres agents

Le terminal agent de Cursor tourne dans un sous-shell sandboxé et non interactif qui n’hérite pas des variables d’environnement utilisateur et ne source pas les fichiers de configuration shell. Les commandes CLI cloud interactives sont totalement non fonctionnelles. Le contournement utilisé par la communauté consiste à passer par des serveurs MCP pour les interactions GCP plutôt que des commandes CLI directes.

Pour tous les agents, les fichiers de clés JSON de comptes de service restent la méthode d’authentification la plus fiable pour les sessions d’agents IA, malgré les mises en garde appuyées de Google. Il n’existe pas d’alternative keyless viable pour le développement local avec des agents aujourd’hui.

La solution : variables d’environnement et direnv

La chose la plus utile que vous puissiez faire est de définir CLOUDSDK_CONFIG vers un répertoire différent par projet. Cette seule variable isole complètement tout l’état gcloud : identifiants, configurations, fichiers ADC, tokens d’accès et logs. Chaque répertoire a son propre credentials.db, son propre application_default_credentials.json, son propre tout. Deux sessions de terminal utilisant des chemins CLOUDSDK_CONFIG différents ne peuvent pas interférer entre elles.

Mais CLOUDSDK_CONFIG seul ne suffit pas. Il faut quatre variables qui fonctionnent ensemble pour obtenir une isolation complète entre les outils CLI et les bibliothèques clientes :

VariableCe qu’elle contrôleAffecte
CLOUDSDK_CONFIGRépertoire complet de l’état gcloudgcloud, bq, gsutil
CLOUDSDK_CORE_PROJECTProjet par défaut pour les commandes CLIgcloud, bq, gsutil
GOOGLE_CLOUD_PROJECTProjet par défaut pour les bibliothèques clientesPython, dbt, Terraform
GOOGLE_APPLICATION_CREDENTIALSChemin vers le fichier d’identifiants pour l’ADCPython, dbt, Terraform

Pourquoi deux variables de projet distinctes ? Parce que CLOUDSDK_CORE_PROJECT n’affecte que le CLI gcloud et ses compagnons. GOOGLE_CLOUD_PROJECT n’affecte que les bibliothèques clientes. Ce sont des systèmes indépendants. Si vous n’en définissez qu’une, gcloud ciblera le bon projet tandis que dbt ciblera le mauvais, ou inversement.

Automatiser avec direnv

Définir quatre variables d’environnement à chaque changement de client, ça devient vite pénible. direnv résout le problème en chargeant automatiquement les variables d’environnement quand vous entrez dans un répertoire et en les déchargeant quand vous en sortez.

Voici à quoi ressemble un .envrc pour un de mes projets clients :

~/projects/acme-analytics/.envrc
export CLOUDSDK_CONFIG="$HOME/.config/gcloud-acme"
export CLOUDSDK_CORE_PROJECT=acme-analytics-prod
export GOOGLE_CLOUD_PROJECT=acme-analytics-prod
export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.gcp-keys/acme-dbt-runner.json"
# Spécifique à dbt
export DBT_TARGET=prod
export DBT_PROFILES_DIR="$HOME/.dbt"

Quand je fais un cd dans ce répertoire, direnv charge tout. Quand j’en sors, il décharge tout. Chaque répertoire client a son propre .envrc avec son propre chemin de configuration isolé, son propre ID de projet et ses propres identifiants. Claude Code hérite de ces variables depuis mon shell, donc quand je démarre une session dans un répertoire client, l’agent a automatiquement le bon contexte.

Pour un nouveau client, la mise en place prend environ cinq minutes :

Terminal window
# Créer le répertoire de configuration gcloud isolé
mkdir -p ~/.config/gcloud-newclient
# Activer le compte de service dans ce répertoire isolé
CLOUDSDK_CONFIG=~/.config/gcloud-newclient \
gcloud auth activate-service-account \
--key-file=~/.gcp-keys/newclient-sa.json
# Créer le .envrc
cat > ~/projects/newclient-dbt/.envrc << 'EOF'
export CLOUDSDK_CONFIG="$HOME/.config/gcloud-newclient"
export CLOUDSDK_CORE_PROJECT=newclient-warehouse
export GOOGLE_CLOUD_PROJECT=newclient-warehouse
export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.gcp-keys/newclient-sa.json"
EOF
# Approuver le .envrc
direnv allow ~/projects/newclient-dbt

Un point à connaître : le gke-gcloud-auth-plugin ne respecte pas CLOUDSDK_CONFIG (Kubernetes Issue #554). Si vous faites du GKE, ce pattern d’isolation ne fonctionne pas. Pour les workflows d’analytics engineering qui ne touchent pas à Kubernetes, ce n’est pas un problème.

Ce pattern est étonnamment méconnu en dehors des contextes CI/CD. La documentation de Google mentionne CLOUDSDK_CONFIG mais ne le met pas en avant comme la solution multi-projets qu’il est en réalité. Pour toute équipe de conseil, ça devrait être une infrastructure standard, pas un outillage optionnel.

Comptes de service : ce que Google recommande vs ce qui marche vraiment

La position officielle de Google est claire : n’utilisez pas de fichiers de clés de comptes de service. Utilisez Workload Identity Federation à la place. (J’ai écrit sur l’approche IAM du moindre privilège pour les équipes data, qui couvre la théorie. Cette section porte sur la pratique.)

WIF fonctionne en laissant un fournisseur d’identité externe (GitHub Actions OIDC, AWS IAM, Azure AD) se porter garant de votre workload, pour que Google puisse émettre des identifiants à durée de vie courte sans fichier de clé statique. C’est véritablement plus sécurisé. Le problème, c’est qu’un laptop de développeur n’est pas GitHub Actions. Il n’y a pas de fournisseur d’identité externe avec lequel fédérer. WIF résout bien le problème CI/CD. Il ne résout pas du tout le problème « je suis à mon bureau en train de lancer dbt pour trois clients ».

Le compromis, c’est l’impersonation de compte de service. Vous vous authentifiez en tant que vous-même via gcloud auth login, puis vous impersonnez un compte de service pour obtenir un token d’accès à durée de vie courte :

Terminal window
gcloud auth print-access-token \
--impersonate-service-account=dbt-runner@acme-prod.iam.gserviceaccount.com

Cela génère un token valable 60 minutes. Pas de fichier de clé sur le disque. La trace d’audit montre à la fois votre identité et celle du compte de service. Les équipes sécurité apprécient cette approche, et à juste titre. La contrainte, c’est que les tokens expirent et doivent être rafraîchis, ce qui ajoute de la complexité aux sessions d’agents longues où la ré-authentification n’est pas simple.

Une configuration pragmatique pour la plupart des missions clients :

  • Un compte de service par projet client, créé à l’intérieur du projet du client (pour qu’une clé compromise ne puisse pas atteindre les autres clients)
  • Rôles minimaux : BigQuery User + BigQuery Data Editor pour le travail dbt, Storage Object Viewer si l’accès GCS est nécessaire
  • Convention de nommage : dbt-runner@client-project.iam.gserviceaccount.com
  • Rotation des clés tous les 90 jours, permissions filesystem (chmod 600) sur les fichiers de clés
  • Clés stockées dans ~/.gcp-keys/, exclu du .gitignore et de la synchronisation cloud

Est-ce ce que Google recommande ? Non. Est-ce que ça fonctionne de façon fiable avec tous les outils de la stack, y compris les agents IA incapables de faire de l’auth interactive ? Oui. Je préférerais utiliser des tokens d’impersonation, et c’est ce que je fais pour le travail de production sensible. Mais pour la réalité quotidienne de lancer des builds dbt sur 10 clients, les clés de comptes de service avec une bonne hygiène sont le choix pragmatique.

Un point d’honnêteté : les agents IA enregistrent leurs entrées et sorties. Un log de conversation mal nettoyé pourrait exposer un chemin de clé, voire le contenu d’une clé. C’est pourquoi les tokens d’impersonation à durée de vie courte sont véritablement préférables si vous pouvez absorber la friction opérationnelle. Ayez conscience du compromis que vous faites.

Intégration avec dbt

Si vous utilisez direnv avec le pattern à quatre variables, la configuration dbt est simple. Le profiles.yml lit les variables d’environnement définies par direnv, donc les bons identifiants se chargent automatiquement selon le répertoire dans lequel vous vous trouvez :

~/.dbt/profiles.yml
acme_analytics:
target: "{{ env_var('DBT_TARGET', 'dev') }}"
outputs:
dev:
type: bigquery
method: service-account
project: "{{ env_var('GOOGLE_CLOUD_PROJECT') }}"
dataset: dbt_dev
keyfile: "{{ env_var('GOOGLE_APPLICATION_CREDENTIALS') }}"
threads: 4
location: EU
prod:
type: bigquery
method: service-account
project: "{{ env_var('GOOGLE_CLOUD_PROJECT') }}"
dataset: analytics
keyfile: "{{ env_var('GOOGLE_APPLICATION_CREDENTIALS') }}"
threads: 8
location: EU
# Même pattern pour chaque client
globex_warehouse:
target: "{{ env_var('DBT_TARGET', 'dev') }}"
outputs:
dev:
type: bigquery
method: service-account
project: "{{ env_var('GOOGLE_CLOUD_PROJECT') }}"
dataset: dbt_dev
keyfile: "{{ env_var('GOOGLE_APPLICATION_CREDENTIALS') }}"
threads: 4
location: US

Chaque client a son propre bloc de profil, mais ils lisent tous les mêmes variables d’environnement. direnv permute les valeurs quand vous changez de répertoire. dbt run récupère automatiquement le bon projet et les bons identifiants.

La méthode oauth existe et fonctionne bien pour le développement mono-projet, mais elle casse dans les environnements non interactifs (y compris les sessions d’agents IA). La méthode service-account avec l’interpolation env_var() est le choix adapté au conseil.

Quand il faut une isolation plus poussée

direnv couvre la plupart des scénarios de conseil. Mais il existe des cas où les variables d’environnement ne suffisent pas.

Docker et DevContainers

Si vous faites tourner plusieurs agents IA simultanément (Claude Code pour un client, Codex pour un autre), ou du travail de production où vous voulez une séparation absolue, les conteneurs offrent une frontière d’isolation stricte :

docker-compose.yml
services:
agent-acme:
image: analytics-agent:latest
volumes:
- ~/.gcp-keys/acme-sa.json:/tmp/creds.json:ro
- ~/projects/acme-analytics:/workspace
environment:
- GOOGLE_APPLICATION_CREDENTIALS=/tmp/creds.json
- GOOGLE_CLOUD_PROJECT=acme-analytics-prod
agent-globex:
image: analytics-agent:latest
volumes:
- ~/.gcp-keys/globex-sa.json:/tmp/creds.json:ro
- ~/projects/globex-warehouse:/workspace
environment:
- GOOGLE_APPLICATION_CREDENTIALS=/tmp/creds.json
- GOOGLE_CLOUD_PROJECT=globex-warehouse-prod

Chaque conteneur a son propre système de fichiers, son propre environnement, et aucun moyen d’accéder aux identifiants de l’autre. Pour Claude Code spécifiquement, claudebox offre une isolation par projet avec des images Docker séparées et un état d’authentification distinct.

La plupart des équipes de conseil n’ont pas besoin de ça. direnv gère 90% des cas. Les conteneurs ajoutent une charge opérationnelle qui ne se justifie que pour le travail multi-agents simultané ou les environnements de production critiques.

Terraform pour standardiser la création des SA

Quand on gère des comptes de service sur plus de 10 projets clients, la création manuelle devient vite le bazar. Le module terraform-google-modules/service-accounts standardise le processus :

module "dbt_service_account" {
source = "terraform-google-modules/service-accounts/google"
version = "~> 4.0"
project_id = var.client_project_id
prefix = "dbt"
names = ["runner"]
project_roles = [
"${var.client_project_id}=>roles/bigquery.user",
"${var.client_project_id}=>roles/bigquery.dataEditor"
]
}

Un seul module, appliqué par client, donne un nommage cohérent, des rôles cohérents, et un seul endroit pour mettre à jour les permissions. La rotation des clés peut être automatisée avec la ressource time_rotating de Terraform combinée à Cloud Scheduler.

Une note rapide sur les workspaces Terraform : ce n’est pas le bon outil pour les configurations multi-clients. La propre documentation de HashiCorp indique que les workspaces CLI partagent le même backend et ne conviennent pas quand les déploiements nécessitent des contrôles d’accès séparés. Pour les cabinets de conseil, Terragrunt avec des structures de répertoires par client est le meilleur choix.

GKE Agent Sandbox

Pour les équipes qui développent des fonctionnalités IA destinées aux clients et qui exécutent du code non fiable, l’Agent Sandbox de GKE utilise gVisor (un kernel en espace utilisateur) pour fournir une couche d’isolation supplémentaire au-delà des conteneurs standards. Un agent compromis ne peut pas atteindre le kernel hôte ni les autres workloads. C’est probablement disproportionné pour le travail d’analytics en conseil, mais ça existe si vous en avez besoin.

Serveurs MCP : la direction que ça prend

Google a lancé des serveurs MCP officiels pour BigQuery, Cloud SQL, Spanner et d’autres services fin 2025 (j’ai couvert la mise en place du serveur MCP BigQuery séparément). Le package @google-cloud/gcloud-mcp encapsule le CLI gcloud pour une interaction structurée, ce qui répond directement au problème « les agents ne peuvent pas faire d’auth interactive ».

Le changement est architectural. Au lieu qu’un agent exécute des commandes gcloud dans votre shell (héritant de votre état global, en conflit avec vos autres sessions), l’agent communique avec un serveur MCP qui gère ses propres identifiants. Si le protocole ne vous est pas familier, mon aperçu des fondamentaux de MCP couvre les bases. L’agent ne touche jamais directement ~/.config/gcloud/. L’authentification passe de la couche terminal à une couche protocole.

Pour le travail multi-projets, c’est prometteur. Des implémentations communautaires comme LokiMCPUniverse/gcp-mcp-server supportent déjà une configuration explicite par projet où chaque projet a son propre fichier d’identifiants, et l’agent spécifie quel contexte projet utiliser à chaque requête.

Les agents intégrés aux IDE vont aussi dans cette direction. Cursor et Windsurf supportent les configurations MCP au niveau du projet, donc le serveur MCP redémarre avec les bons identifiants quand vous changez de projet. Le mode d’échec à surveiller est la dérive de contexte : votre IDE ouvert sur le projet du Client A pendant que le processus MCP en arrière-plan est encore authentifié sur le Client B. Des fichiers mcp_config.json au niveau du projet empêchent ce problème.

Mon avis honnête : MCP va dans la bonne direction. L’authentification gérée au niveau du protocole plutôt qu’au niveau du shell élimine toute une catégorie de problèmes. Mais l’écosystème est jeune. Les serveurs sont encore en cours de maturation, la documentation est limitée, et pour le travail de conseil en production, je ne suis pas prête à m’y fier comme mécanisme d’authentification principal. À suivre, mais ne jetez pas votre configuration direnv pour l’instant.

L’approche par couches

Aujourd’hui (30 minutes) : Installez direnv. Créez un .envrc par client avec les quatre variables (CLOUDSDK_CONFIG, CLOUDSDK_CORE_PROJECT, GOOGLE_CLOUD_PROJECT, GOOGLE_APPLICATION_CREDENTIALS). Un compte de service par projet client avec les rôles minimaux nécessaires.

Ce mois-ci : Mettez en place un module Terraform pour la création standardisée des comptes de service. Automatisez la rotation des clés. Alignez toute l’équipe sur le même pattern direnv.

Ce trimestre : Évaluez les serveurs MCP de Google pour vos workflows d’agents. S’ils sont assez stables, ils rendront tout le problème d’isolation au niveau du shell obsolète.

Google a conçu gcloud pour des développeurs mono-projet. Les cabinets de conseil sont l’exact opposé. Chaque solution de cet article est un contournement d’un choix de conception fait par Google il y a des années. La bonne nouvelle, c’est que CLOUDSDK_CONFIG existe et fonctionne. La meilleure nouvelle, c’est que MCP pourrait finir par faire disparaître le problème. En attendant, quatre variables d’environnement et un fichier .envrc font la différence entre une journée de travail normale et passer la moitié de sa matinée à débugger des erreurs d’auth sur trois projets clients.