Lesson 10 — Le code Terraform, couche par couche

09. `60_advanced.tf` : Agent Gateway et VPC Service Controls (options)

9 min read

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

Deux protections avancées, désactivées par défaut, qu'on active par une simple variable sans réécrire le socle. Bases Terraform : 00-bases-terraform.md.


1. À quoi sert ce fichier

Il contient deux options de sécurité que tout le monde n'utilise pas dès le premier jour :

  • l'Agent Gateway : un point de passage contrôlé pour les appels de Gemini Enterprise vers des agents, des outils MCP ou des API ;
  • un périmètre VPC Service Controls : une « frontière » autour des API du projet, déployée en mode observation (dry-run) pour voir ce qui serait bloqué sans rien bloquer.

Chaque ressource n'existe que si sa variable est renseignée (technique du count à 0 ou 1). Avec les valeurs par défaut, ce fichier ne crée rien.


2. Vue d'ensemble

Bloc (adresse)Type de blocRôle en une phrase
google_network_services_agent_gateway.egressresource (0 ou 1)Passerelle de sortie qui gouverne les appels de Gemini Enterprise vers agents / MCP / API.
google_access_context_manager_service_perimeter.geresource (0 ou 1)Périmètre VPC-SC autour du projet, en dry-run.

3. Le code expliqué, bloc par bloc

3.1 L'Agent Gateway : google_network_services_agent_gateway.egress

Vocabulaire.

  • Un agent est un programme IA qui agit (interroge une base, appelle une API…). Un outil MCP (Model Context Protocol) est un service qu'un agent peut appeler selon un protocole standard.
  • Agent Registry est le catalogue Google des agents, outils MCP et API autorisés.
  • L'Agent Gateway est une sorte de pare-feu applicatif pour agents : refus par défaut, autorisation par règle IAM, politiques par outil, filtrage Model Armor possible, traçabilité de chaque appel (README §11.1).
resource "google_network_services_agent_gateway" "egress" {
  count       = var.enable_agent_gateway ? 1 : 0
  name        = "${var.app_id}-egress"
  location    = var.region
  description = "Sortie gouvernée de Gemini Enterprise vers agents / MCP / API."
  • count = var.enable_agent_gateway ? 1 : 0 : la ressource existe en 1 exemplaire si la variable vaut true, en 0 (= pas du tout) sinon. C'est la façon standard de rendre une ressource optionnelle (00-bases §8.1). Conséquence : ailleurs, on y fait référence avec [0] (google_network_services_agent_gateway.egress[0]), comme dans 50_app.tf et outputs.tf. Avec le terraform.tfvars.example, enable_agent_gateway n'est pas renseigné : défaut false, donc 0.
  • name = "${var.app_id}-egress" : interpolation. Avec app_id = "assistant-ia" → "assistant-ia-egress". « egress » = « sortie ».
  • location = var.region : défaut "europe-west1". Une app en multi-région eu utilise une passerelle dans une région européenne (commentaire du fichier).
  • description : texte libre pour les humains.
  google_managed {
    governed_access_path = "AGENT_TO_ANYWHERE"
  }
  registries = ["//agentregistry.googleapis.com/projects/${var.project_id}/locations/global"]

  depends_on = [google_project_service.apis]
}
  • google_managed { … } : la passerelle est gérée par Google (pas de machine à administrer).
    • governed_access_path = "AGENT_TO_ANYWHERE" : le sens du trafic gouverné. Ici : depuis les agents (Gemini Enterprise) vers n'importe quelle cible enregistrée. Pour Gemini Enterprise, seule la sortie a du sens (README §11.1).
  • registries : la liste des registres d'agents dont la passerelle connaît les cibles. Ici, le registre global du projet. Avec project_id = "acme-gemini-enterprise-prd" : "//agentregistry.googleapis.com/projects/acme-gemini-enterprise-prd/locations/global". Le // initial est le format Google des « noms complets de ressource ».
  • depends_on = [google_project_service.apis] : attendre l'activation des API. Dans 00_foundation.tf, les API réseau et Agent Registry (networkservices, agentregistry…) ne sont ajoutées à la liste que si enable_agent_gateway = true.

Le rattachement à l'app ne se fait pas ici : il passe par la clé agentGatewaySetting de local.engine_settings, envoyée par curl dans 50_app.tf (voir 08-app.md).

3.2 Le périmètre VPC-SC : google_access_context_manager_service_perimeter.ge

Vocabulaire.

  • VPC Service Controls (VPC-SC) : un périmètre autour d'API Google. Même avec des identifiants volés, on ne peut pas appeler ces API depuis l'extérieur du périmètre, ni copier les données vers un projet hors périmètre.
  • Access Context Manager : le service, au niveau de l'organisation Google Cloud, qui gère ces périmètres.
  • Access policy (politique d'accès) : le conteneur de tous les périmètres de l'organisation. Elle a un ID numérique.
  • Dry-run : mode « à blanc ». Rien n'est bloqué ; les violations sont seulement journalisées (journaux « VPC SC dry run »).
resource "google_access_context_manager_service_perimeter" "ge" {
  count                     = var.vpc_sc_access_policy == "" ? 0 : 1
  parent                    = "accessPolicies/${var.vpc_sc_access_policy}"
  name                      = "accessPolicies/${var.vpc_sc_access_policy}/servicePerimeters/gemini_enterprise_${replace(var.project_id, "-", "_")}"
  title                     = "gemini-enterprise-${var.project_id}"
  perimeter_type            = "PERIMETER_TYPE_REGULAR"
  use_explicit_dry_run_spec = true
  • count = var.vpc_sc_access_policy == "" ? 0 : 1 : ici la condition est « la variable est-elle vide ? ». Vide (défaut) → 0 périmètre. Renseignée (par exemple "123456789") → 1 périmètre. Une variable texte vide sert donc d'interrupteur, et sa valeur sert en même temps de paramètre.

  • parent = "accessPolicies/${var.vpc_sc_access_policy}" : la politique d'accès de l'organisation qui contiendra le périmètre. Par exemple "accessPolicies/123456789".

  • name : le nom complet du périmètre. Google impose que la dernière partie ne contienne que des lettres, chiffres et tirets bas (_), pas de tiret (-). Or un ID de projet contient des tirets. D'où replace(texte, "-", "_"), qui remplace chaque - par _ (00-bases §12.4).

    Exemple concret avec project_id = "acme-gemini-enterprise-prd" et vpc_sc_access_policy = "123456789" :

    • replace("acme-gemini-enterprise-prd", "-", "_") → "acme_gemini_enterprise_prd" ;
    • name → "accessPolicies/123456789/servicePerimeters/gemini_enterprise_acme_gemini_enterprise_prd".
  • title = "gemini-enterprise-${var.project_id}" : le titre lisible, affiché dans la console. Les tirets y sont autorisés → "gemini-enterprise-acme-gemini-enterprise-prd".

  • perimeter_type = "PERIMETER_TYPE_REGULAR" : un périmètre normal. (L'autre type, « bridge », sert seulement à relier deux périmètres.)

  • use_explicit_dry_run_spec = true : la configuration écrite dans le bloc spec ci-dessous est la configuration dry-run. La configuration appliquée (qui bloque) irait dans un bloc status, absent ici : donc rien n'est bloqué.

  spec {
    resources = ["projects/${local.project_number}"]
    restricted_services = [
      "discoveryengine.googleapis.com",
      "modelarmor.googleapis.com",
      "dlp.googleapis.com",
      "cloudkms.googleapis.com",
      "secretmanager.googleapis.com",
    ]
  }
}
  • spec : le contenu du périmètre en mode dry-run.
    • resources : les projets à l'intérieur du périmètre. VPC-SC exige le numéro de projet (pas son ID), d'où local.project_number (lu dans 00_foundation.tf). Exemple : ["projects/123456789012"].
    • restricted_services : les API protégées par le périmètre. Ce sont exactement les services sensibles du socle : Gemini Enterprise (discoveryengine), Model Armor, DLP, Cloud KMS (clés) et Secret Manager (secrets).

Pourquoi le dry-run (README §11.2) : un périmètre mal réglé peut couper l'accès à l'app. On observe donc 2 à 4 semaines les journaux « VPC SC dry run », on ajuste, puis on passe en mode actif.

Passer en mode actif (plus tard) : copier le contenu de spec dans un bloc status (même syntaxe). Les détails sont dans la page de la ressource (lien §6). À faire seulement après analyse des journaux et ajout des règles d'entrée.


4. Ce que Terraform crée concrètement

Avec le terraform.tfvars.example : rien (0 ressource). enable_agent_gateway vaut false et vpc_sc_access_policy vaut "" par défaut.

Si on active les deux :

  • enable_agent_gateway = true → 1 passerelle assistant-ia-egress en europe-west1 + les API réseau activées (dans 00_foundation.tf) + le rattachement à l'app (dans 50_app.tf) ;
  • vpc_sc_access_policy = "<id>" → 1 périmètre en dry-run autour du projet, qui protège 5 API, sans rien bloquer.

5. Dépendances avec les autres fichiers

LienFichierFiche
Utilise var.enable_agent_gateway, var.region, var.app_id, var.vpc_sc_access_policy, var.project_idvariables.tf02-variables.md
Utilise google_project_service.apis et local.project_number00_foundation.tf03-foundation.md
La passerelle est rattachée à l'app par local.engine_settings50_app.tf08-app.md
L'ID de la passerelle est affiché par l'output agent_gatewayoutputs.tf11-outputs.md

6. Pour reproduire / adapter

Besoin type : ajouter une ressource optionnelle, activable par une variable.

# variables.tf : l'interrupteur
variable "enable_mon_option" {
  description = "Active mon option (désactivée par défaut)."
  type        = bool
  default     = false
}

# 60_advanced.tf : la ressource, présente 0 ou 1 fois
resource "google_xxx" "mon_option" {
  count = var.enable_mon_option ? 1 : 0          # ou : var.mon_id == "" ? 0 : 1
  name  = "${var.app_id}-mon-option"
  # ...
  depends_on = [google_project_service.apis]     # si une API doit être activée avant
}

# Partout ailleurs : toujours avec [0], et toujours sous la même condition
# valeur = var.enable_mon_option ? google_xxx.mon_option[0].id : null

Variante : un périmètre VPC-SC en dry-run pour un autre projet.

resource "google_access_context_manager_service_perimeter" "autre" {
  parent                    = "accessPolicies/${var.vpc_sc_access_policy}"
  name                      = "accessPolicies/${var.vpc_sc_access_policy}/servicePerimeters/${replace(var.autre_projet, "-", "_")}" # pas de "-"
  title                     = "perimetre-${var.autre_projet}"
  perimeter_type            = "PERIMETER_TYPE_REGULAR"
  use_explicit_dry_run_spec = true      # observer d'abord
  spec {
    resources           = ["projects/${var.autre_projet_numero}"]   # NUMÉRO de projet
    restricted_services = ["storage.googleapis.com"]                # API à protéger
  }
}
  1. Lire la doc de la ressource : https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/network_services_agent_gateway et https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter
  2. Créer la variable interrupteur avec un défaut « désactivé ».
  3. Ajouter count = condition ? 1 : 0 et des références en [0] partout, protégées par la même condition.
  4. Activer les API nécessaires seulement si l'option est active (comme dans 00_foundation.tf).
  5. terraform plan avec l'option à false (rien ne doit changer), puis à true.

7. Pièges et bonnes pratiques

  • Agent Gateway sans agent personnalisé = inutile. S'il n'y a ni agent maison ni outil MCP, il n'y a rien à gouverner. Elle devient indispensable dès qu'on ajoute un agent (par exemple Databricks, README §7.4) ou un serveur MCP : ce sont justement les flux que le Model Armor de l'assistant ne filtre pas.
  • Après activation de la passerelle, il faut encore importer dans Agent Registry les agents, MCP et API autorisés (refus par défaut), et attendre jusqu'à 15 minutes que les politiques s'appliquent.
  • VPC-SC bloque l'interface web en mode actif tant qu'on n'a pas créé de règles d'entrée (access levels : plages IP de l'entreprise, appareils gérés…). Ne jamais passer en actif sans elles.
  • VPC-SC doit précéder les connecteurs : appliquer un périmètre sur un projet qui a déjà des data stores n'est pas supporté (README §11.2). En production, déployez le périmètre avant de créer les connecteurs.
  • Droits au niveau organisation : créer un périmètre exige des droits Access Context Manager sur l'organisation (pas seulement sur le projet). Sans eux, apply échoue avec une erreur 403.
  • API Access Context Manager : comme le provider utilise user_project_override = true (versions.tf), les appels VPC-SC sont facturés au projet, qui doit donc avoir l'API accesscontextmanager.googleapis.com activée. C'est fait automatiquement dans 00_foundation.tf dès que vpc_sc_access_policy est renseigné.
  • Le nom du périmètre est figé : changer project_id change name, donc recrée le périmètre.
  • Activez ces options en recette d'abord, puis en production, comme tout changement de sécurité.
Next