Lesson 20 — Tutoriels pas à pas

12-1-9. Déployer les agents « à la 3S » : GitHub Actions + Terraform, sans `gcloud auth login`

12 min read

This content is not translated yet. Showing the original version.

Le but : déployer les mêmes agents qu'en 12-1-1 (shell) et 12-1-2 (Terraform), mais comme une équipe Acme le fait sur la 3S : personne ne lance terraform apply depuis son poste. On pousse du code sur GitHub, une pull request est relue, et le merge sur main déploie, avec une identité de pipeline, un état Terraform partagé et aucune clé.

Le dossier : 03-bis-stack-3s/. Il réutilise le Terraform du 01-bis tel quel (aucune copie) : seul le chemin jusqu'à apply change.

Ce qu'est la 3S, et ce que ce guide n'invente pas. La 3S (Self Service Stack) est la plateforme cloud interne de Acme, sur GCP. Ce qui en est public (offres d'emploi, présentations) : GCP (Cloud Run, GKE), Terraform, GitHub Actions, FluxCD (GitOps sur GKE), Aiven (PostgreSQL, Kafka), Datadog / OpenTelemetry. Ses modules, ses noms de projets et son portail sont internes : ce guide ne les reproduit pas. Il construit le même modèle (Git → pipeline → Terraform → GCP) sur un projet à vous, pour comprendre ce que la 3S fait à votre place et savoir quoi demander à l'équipe 3S. Là où la 3S fournit déjà la brique, c'est indiqué « 3S : ».

Vérifié (15 octobre 2026, projet de test second-project-511608) : terraform validate et fmt du socle, du déploiement et du workflow ; terraform plan réels du socle (24 ressources) et du déploiement (1 agent : 23, 2 agents : 31, tous : 87). Ce test a trouvé un défaut du 01-bis et du 02-bis, corrigé : avec agents = ["meteo"], le plan échouait (« Inconsistent conditional result types »), car les agents du catalogue n'ont pas tous les mêmes champs. Pas encore exécuté : terraform apply du socle et le premier passage du workflow dans GitHub (parties 3 à 6).


Sommaire

  1. Les mots à connaître
  2. Ce qui change par rapport à 12-1-1 et 12-1-2
  3. Pourquoi Cloud Run, et pas GKE + FluxCD
  4. Le socle : bucket d'état, compte de déploiement, confiance GitHub (une fois)
  5. Brancher le dépôt GitHub
  6. Déployer : une pull request, puis un merge
  7. Tester le pipeline sans GitHub (option)
  8. Ajouter un agent, en retirer un, connecter l'app
  9. Tout nettoyer
  10. Ce qui peut arriver
  11. Ce qu'il faut demander à l'équipe 3S

0. Les mots à connaître

MotEn clair
3SSelf Service Stack : la plateforme interne de Acme qui donne aux équipes un projet GCP, des modules Terraform et un pipeline, déjà conformes (sécurité, réseau, observabilité).
GitOpsGit est la source de vérité : ce qui est sur main est ce qui tourne. On change l'infrastructure par une pull request, jamais à la main.
GitHub ActionsLes pipelines de GitHub : un fichier YAML dans .github/workflows/ lancé à chaque push ou pull request.
Workload Identity Federation (WIF)Google fait confiance au jeton OIDC que GitHub donne à chaque exécution. Le pipeline obtient un jeton Google sans clé de compte de service.
Compte de déploiementagents-deployer-sa : l'identité du pipeline. C'est lui qui crée les agents, plus votre compte personnel.
État Terraform distantLe fichier d'état est dans un bucket GCS partagé (versionné), et non sur un poste : tout le monde, et le pipeline, voient le même.
Socle (bootstrap)Ce qu'on crée une fois, avant le pipeline : le bucket, le compte de déploiement, la confiance GitHub. 3S : fourni par la plateforme.

1. Ce qui change par rapport à 12-1-1 et 12-1-2

 12-1-2 (Terraform local)                         12-1-9 (« à la 3S »)
 ┌────────────┐                                   ┌────────────┐  PR   ┌──────────────────────┐ merge ┌──────────────────────────┐
 │ votre poste │ gcloud auth login               │ votre poste │ ────▶ │ GitHub : relecture   │ ────▶ │ GitHub Actions           │
 │ terraform   │ terraform apply ──▶ GCP          │ git push    │       │ + terraform validate │       │ jeton OIDC ─▶ WIF ─▶     │
 │ état local  │ (votre compte, vos droits)       └────────────┘       └──────────────────────┘       │ agents-deployer-sa       │
 └────────────┘                                                                                      │ terraform plan + apply   │
                                                                                                     │ état : bucket GCS        │
                                                                                                     └────────────┬─────────────┘
                                                                                                                  ▼
                                                              Projet des agents : exactement les ressources du 01-bis
                                                              (images Cloud Build, Cloud Run privés, identités, Agent Registry)
12-1-2 Terraform local12-1-9 « à la 3S »
Qui lance applyVous, sur votre posteLe pipeline, au merge sur main
Avec quels droitsLes vôtres (souvent Owner)Ceux de agents-deployer-sa, listés et limités
Où est l'étatterraform.tfstate.d/ sur le postegs://<projet>-tfstate/09-agents-a2a/
Où sont les valeursconfig/…env ou terraform.tfvars (non versionné)03-bis-stack-3s/deploy/agents.auto.tfvars (versionné)
Clé de compte de service—Aucune (WIF)
Code Terraform des agents01-bis-cloud-run-a2a/terraformLe même, appelé comme module

Les fichiers :

FichierRôle
03-bis-stack-3s/bootstrap/Le socle, appliqué une fois par un administrateur du projet. 3S : déjà fait pour vous
03-bis-stack-3s/deploy/main.tfLe backend GCS + l'appel du module ../../01-bis-cloud-run-a2a/terraform
03-bis-stack-3s/deploy/agents.auto.tfvarsLa configuration déployée : projet, agents, numéro du projet de l'app
03-bis-stack-3s/github/deploy-agents.ymlLe workflow GitHub Actions, à copier dans .github/workflows/

2. Pourquoi Cloud Run, et pas GKE + FluxCD

La 3S propose aussi GKE avec FluxCD (des manifestes Kubernetes dans Git, synchronisés par Flux). Pour ces agents, on reste sur Cloud Run :

  1. Gemini Enterprise appelle un agent A2A avec son propre compte (service-NUM_APP@gcp-sa-discoveryengine…) et un jeton d'identité Google. Cloud Run vérifie ce jeton tout seul (run.invoker). Sur GKE, il faudrait exposer l'agent (Ingress, certificat, IAP ou vérification du jeton dans le code) : plus de pièces, même résultat.
  2. L'URL .run.app est exigée pour l'authentification automatique (voir 01-bis, partie 1).
  3. Coût : Cloud Run descend à 0 instance ; un agent peu utilisé ne coûte presque rien.

GKE + FluxCD reste le bon choix pour une API métier qui tourne déjà sur le cluster de l'équipe. L'agent, lui, peut appeler cette API depuis Cloud Run.


3. Le socle (une fois)

3S : sautez cette partie. Demandez à l'équipe 3S les trois valeurs de la partie 4 (fournisseur WIF, compte de déploiement, bucket d'état) et vérifiez que le compte a les rôles de la liste ci-dessous.

Qui : un administrateur du projet des agents (rôle Owner, ou resourcemanager.projectIamAdmin + iam.workloadIdentityPoolAdmin + iam.serviceAccountAdmin + storage.admin), depuis son poste, une seule fois.

cd 09-agents-multi-projets-gemini-enterprise/03-bis-stack-3s/bootstrap
gcloud auth application-default login
cp terraform.tfvars.example terraform.tfvars   # projet des agents + dépôt GitHub « organisation/depot »
terraform init
terraform plan     # 24 ressources à créer
terraform apply
terraform output github_variables

Ce que le socle crée :

RessourcePourquoi
Bucket <projet>-tfstate (versionné, jamais public)L'état Terraform partagé ; le versionnement permet de revenir en arrière
Compte agents-deployer-sa + 12 rôlesL'identité du pipeline : exactement ce que le 01-bis (et le projet 10) créent
Pool WIF github + fournisseur github-oidcGoogle accepte les jetons de GitHub, seulement ceux de votre dépôt (attribute_condition)
workloadIdentityUser sur le compteSeule la branche main peut emprunter le compte (…:ref:refs/heads/main)

Les 12 rôles du compte de déploiement, et pourquoi (bootstrap/variables.tf) :

RôlePour
serviceusage.serviceUsageAdminActiver les API (Cloud Run, Vertex AI, Agent Registry…)
iam.serviceAccountAdminCréer l'identité de chaque agent
iam.serviceAccountUserAttacher ces identités aux services ; lancer Cloud Build avec agents-a2a-build-sa
resourcemanager.projectIamAdminDonner à chaque identité ses rôles (aiplatform.user, logging.logWriter…)
artifactregistry.adminLe dépôt d'images agents-a2a
cloudbuild.builds.editor, storage.admin, logging.viewergcloud builds submit : envoyer le code, construire, suivre le journal
run.adminLes services Cloud Run et leur droit d'appel (run.invoker de Gemini Enterprise)
modelarmor.adminLes templates Model Armor (si non fournis par le projet 08)
agentregistry.adminLes entrées Agent Registry
aiplatform.adminL'agent Agent Runtime du projet 10 (option)

Attention, projectIamAdmin est puissant : le compte peut se donner d'autres rôles. C'est pour cela que seul main (branche protégée, relecture obligatoire) peut l'utiliser. En production, on peut le restreindre avec une condition IAM qui liste les rôles qu'il a le droit de donner. 3S : c'est le genre de règle que la plateforme pose.


4. Brancher le dépôt GitHub

  1. Copier le workflow à la racine du dépôt (GitHub ne lit que .github/workflows/) :

    mkdir -p .github/workflows
    cp 09-agents-multi-projets-gemini-enterprise/03-bis-stack-3s/github/deploy-agents.yml .github/workflows/

    Si le dossier 09-… n'est pas à la racine de votre dépôt, corrigez paths et WORKDIR dans le fichier.

  2. Les 3 variables (pas des secrets, rien de sensible) : GitHub → Settings → Secrets and variables → Actions → onglet Variables → New repository variable, avec la sortie github_variables :

    VariableExemple
    GCP_WIF_PROVIDERprojects/100000000002/locations/global/workloadIdentityPools/github/providers/github-oidc
    GCP_DEPLOYER_SAagents-deployer-sa@aiml-gemini-ent-exp-0zga.iam.gserviceaccount.com
    GCP_TFSTATE_BUCKETaiml-gemini-ent-exp-0zga-tfstate
  3. Protéger main : Settings → Branches → règle sur main : pull request obligatoire, 1 relecture, le contrôle verifier doit passer.

  4. Option, approbation manuelle avant chaque déploiement : Settings → Environments → production → Required reviewers. Le job deployer attend alors un clic « Approve ».


5. Déployer : une pull request, puis un merge

Choisir les agents dans 03-bis-stack-3s/deploy/agents.auto.tfvars :

agents_project_id = "aiml-gemini-ent-exp-0zga"
ge_project_number = ""          # rempli à la partie 7
agents            = ["meteo"]   # [] = tous les agents de agents/trio/catalogue.json
git switch -c agents/meteo
git add 09-agents-multi-projets-gemini-enterprise/03-bis-stack-3s/deploy/agents.auto.tfvars
git commit -m "agents : déployer meteo"
git push -u origin agents/meteo      # puis ouvrir la pull request sur GitHub

Sur la pull request, le job verifier lance terraform fmt -check et terraform validate, sans accès à Google Cloud (le jeton d'une PR ne peut pas emprunter le compte, partie 3). Un relecteur approuve, on merge.

Sur main, le job deployer :

ÉtapeCe qui se passeDurée
google-github-actions/authJeton OIDC GitHub → jeton Google de agents-deployer-sa5 s
terraform initÉtat lu dans gs://…-tfstate/09-agents-a2a/10 s
terraform plan23 ressources pour meteo au premier passage (vérifié)30 s
terraform applyComme au 01-bis : API, identités, image construite par Cloud Build, service Cloud Run privé, Agent Registry4 à 6 min

Vérifier : les journaux du job (onglet Actions du dépôt), puis la console (Cloud Run → meteo-a2a, Agent Registry → région europe-west1) ou le test A2A du 01-bis :

cd 09-agents-multi-projets-gemini-enterprise/01-bis-cloud-run-a2a && ./test.sh

6. Tester le pipeline sans GitHub (option)

Pour vérifier que le compte de déploiement suffit (aucun rôle oublié), on peut jouer le pipeline depuis son poste en empruntant ce compte. Il faut, une fois, le droit de l'emprunter :

PROJET=aiml-gemini-ent-exp-0zga
SA=agents-deployer-sa@$PROJET.iam.gserviceaccount.com
gcloud iam service-accounts add-iam-policy-binding $SA --project=$PROJET \
  --member="user:$(gcloud config get-value account)" --role=roles/iam.serviceAccountTokenCreator

cd 09-agents-multi-projets-gemini-enterprise/03-bis-stack-3s/deploy
export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$SA                 # Terraform agit en tant que le compte
export CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT=$SA           # gcloud builds submit aussi
terraform init -backend-config=bucket=$PROJET-tfstate
terraform plan
terraform apply
unset GOOGLE_IMPERSONATE_SERVICE_ACCOUNT CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT

Un 403 Permission denied désigne le rôle manquant : ajoutez-le à roles dans bootstrap/variables.tf, puis terraform apply dans bootstrap/. Retirez ensuite votre droit d'emprunt (remove-iam-policy-binding, mêmes options).


7. Ajouter un agent, en retirer un, connecter l'app

Toujours une pull request sur agents.auto.tfvars :

Je veuxJe changeEffet au merge
Ajouter tailleagents = ["meteo", "taille"]Image, identité, service, entrée de registre de taille (8 ressources) ; meteo intact
Retirer meteoagents = ["taille"]Supprime les ressources de meteo (le plan du job le montre : à relire)
Donner l'accès à l'appge_project_number = "<NUMÉRO du projet de l'app>"run.invoker pour le compte de Gemini Enterprise sur chaque agent
Modifier le code d'un agentagents/trio/<agent>/…Nouvelle empreinte → nouvelle image → nouvelle révision de cet agent

L'ajout dans l'app Gemini Enterprise reste l'étape de l'admin de l'app : 12-1-4 et 12-1-6. Publier depuis le pipeline (ge_app_id) est possible, mais il faut alors donner au compte de déploiement un rôle d'admin Gemini Enterprise dans le projet de l'app : à décider avec l'admin de l'app, pas par défaut.


8. Tout nettoyer

# 1. Les agents (avec le compte du pipeline, partie 6, ou un compte Owner)
cd 09-agents-multi-projets-gemini-enterprise/03-bis-stack-3s/deploy
terraform init -backend-config=bucket=<PROJET>-tfstate
terraform destroy

# 2. Le socle (supprime aussi le bucket d'état : à faire en dernier)
cd ../bootstrap && terraform destroy

Puis retirer .github/workflows/deploy-agents.yml et les 3 variables du dépôt. Le pool WIF supprimé reste 30 jours en état « supprimé » : recréer un pool github avant ce délai demande gcloud iam workload-identity-pools undelete github --location=global.


9. Ce qui peut arriver

SymptômeCauseSolution
Unable to exchange token / unauthorized_client dans authLe dépôt ne correspond pas à github_repo (casse comprise) ou le job ne tourne pas sur mainVérifier github_repo et la branche ; une PR n'a pas le droit (voulu)
id-token absentpermissions: id-token: write manquantLe garder dans le workflow
Error acquiring the state lockUn autre apply en cours, ou un job interrompuAttendre ; sinon terraform force-unlock <ID> (l'ID est dans le message)
403 sur cloudbuild ou storage pendant l'imageRôle manquant au compte de déploiementPartie 6, puis ajouter le rôle au socle
Inconsistent conditional result typesAncien 01-bis (avant le 15 octobre 2026)Mettre à jour le dépôt (corrigé)
Le plan veut recréer des agents déjà déployés en localÉtat local (12-1-2) et état GCS distinctsMigrer l'état (même méthode que 10-15, partie 5, vérifiée), avec module.agents. comme préfixe ; ou détruire en local avant

10. Ce qu'il faut demander à l'équipe 3S

  1. Le projet des agents, et s'il est déjà relié à un dépôt GitHub par la 3S (fournisseur WIF, compte de déploiement).
  2. Le bucket d'état (et son préfixe) à utiliser.
  3. Les rôles du compte de déploiement : comparer à la liste de la partie 3 ; ceux qui manquent sont souvent agentregistry.admin et modelarmor.admin (services récents).
  4. Si la 3S impose ses modules Terraform (Cloud Run, comptes de service) : le 01-bis s'y adapte en remplaçant ses ressources google_cloud_run_v2_service et google_service_account par ces modules ; la carte A2A, Agent Registry et le droit de Gemini Enterprise restent identiques.
  5. Les règles d'organisation (VPC-SC, domaines autorisés, régions) : docs/05.
Next