Le modèle du fichier où l'on donne ses valeurs (projet, groupes, connecteurs). C'est le seul fichier à modifier pour un déploiement standard. Bases Terraform : 00-bases-terraform.md.
1. À quoi sert ce fichier
Les fichiers .tf décrivent comment construire le socle ; ils utilisent des variables
(00-bases §6.3) déclarées dans variables.tf.
Un fichier .tfvars donne les valeurs de ces variables pour un déploiement précis. terraform.tfvars.example
est un modèle : on le copie en terraform.tfvars, on remplace les valeurs, et Terraform le lit tout seul à chaque
plan et apply. Il ne contient aucun secret : seulement des noms.
2. Vue d'ensemble
Le fichier suit l'ordre de ses commentaires :
| Partie du fichier | Variables | Rôle en une phrase |
|---|---|---|
| En-tête | (aucune) | Comment créer terraform.tfvars et ne jamais le committer. |
| Projet et localisation | project_id, ge_location (en commentaire) | Le projet Google Cloud cible et la zone des données. |
| Groupes et alertes | user_groups, admin_groups, security_groups, viewer_groups, alert_email | Qui a accès à quoi, et qui reçoit les alertes. |
| Phrases interdites | banned_phrases (en commentaire) | Expressions que l'assistant ne doit jamais produire. |
| Connecteurs | connectors | Les sources de données : une entrée par source. |
Toutes les variables absentes du fichier prennent leur valeur par défaut de variables.tf (voir §3.6).
3. Le contenu expliqué, partie par partie
Rappel : qu'est-ce qu'un .tfvars ?
- Une simple liste
nom_de_variable = valeur, en syntaxe HCL. Pas de blocsresource, pas devar.: uniquement des valeurs. - Terraform charge automatiquement
terraform.tfvars(et les fichiers*.auto.tfvars). Un autre nom (par exemplerecette.tfvars) se passe à la main :terraform plan -var-file=recette.tfvars. - Une variable sans
defaultdansvariables.tfest obligatoire : si elle manque,plans'arrête. Ici :project_id,user_groups,admin_groups,security_groups.
3.1 L'en-tête
# cp terraform.tfvars.example terraform.tfvars (ne jamais committer terraform.tfvars)- La commande à lancer dans le dossier
terraform/pour créer votre fichier. - Pourquoi ne jamais committer
terraform.tfvars: il décrit un environnement réel (projet de production, adresses, groupes, identifiants de tenant). Ce n'est pas secret au sens « mot de passe », mais ce sont des informations internes qui n'ont rien à faire dans un dépôt partagé. Le.example, lui, est committé : c'est la documentation du format. - Le projet contient un fichier
.gitignorequi exclut déjàterraform.tfvars,*.tfstate*,*.tfplanet.terraform/. Vérifiez qu'il est bien présent si vous copiez le projet ailleurs.
3.2 Projet et localisation
project_id = "acme-gemini-enterprise-prd"
# ge_location = "eu" # IMMUABLEproject_id: l'ID du projet Google Cloud (le texte, pas le numéro). Obligatoire. Il doit déjà exister, dans l'organisation de l'entreprise, avec la facturation activée (guide du socle (docs/00) §12.1).ge_location: en commentaire, donc le défaut"eu"s'applique (données en Union européenne). Une validation dansvariables.tfn'accepte que"eu"ou"us"(CMEK impossible englobal). IMMUABLE : la changer après coup recrée l'app et ses données. Décidez-la une fois pour toutes.
3.3 Groupes et alertes
user_groups = ["group:gemini-utilisateurs@acme.example"]
admin_groups = ["group:gemini-admins@acme.example"]
security_groups = ["group:securite-ia@acme.example"]
viewer_groups = ["group:gemini-pilotage@acme.example"]
alert_email = "securite-ia@acme.example"- Ce sont des listes (crochets), même avec un seul groupe : on peut en mettre plusieurs, séparés par des virgules.
- Le préfixe
group:est le format IAM de Google pour désigner un groupe (les autres formats sontuser:etserviceAccount:). Le socle n'attribue des droits qu'à des groupes, jamais à des personnes (guide du socle (docs/00) §5.2) : on ajoute ou retire quelqu'un dans l'annuaire, sans toucher à Terraform.
| Variable | Qui | Ce que ça donne |
|---|---|---|
user_groups | Les utilisateurs de l'app | Rôle utilisateur Gemini Enterprise. |
admin_groups | Les administrateurs de l'app (petit groupe) | Rôle administrateur Gemini Enterprise. |
security_groups | L'équipe sécurité | Seule à gérer Model Armor, les clés KMS et à lire les journaux privés. |
viewer_groups | Pilotage, DPO | Lecture seule et statistiques. Facultatif (défaut []). |
alert_email: la boîte qui reçoit les alertes de70_detection.tf. Vide ("", défaut) = pas de canal e-mail, donc des alertes que personne ne reçoit. Mettez une liste de diffusion d'équipe.
Le détail des rôles est dans 04-identity-access.md.
3.4 Phrases interdites
# banned_phrases = ["nom de code projet confidentiel"]- En commentaire, donc défaut
[]: aucune phrase interdite. - Pour l'activer : retirer le
#et lister les expressions (par exemple des noms de code de projets confidentiels). Chaque élément devient un blocbanned_phrasesde l'assistant (voir 08-app.md, blocdynamic).
3.5 Les connecteurs
L'en-tête de la section :
# -----------------------------------------------------------------------------
# CONNECTEURS - une entrée par source. La clé (ex. "gdrive") devient l'ID de collection.
# Les secrets sont créés AVANT par l'équipe sécurité dans Secret Manager ;
# ici on ne met que le NOM du secret.
# gcloud secrets create sharepoint-client-secret --replication-policy=user-managed --locations=europe-west1
# printf '%s' 'VALEUR' | gcloud secrets versions add sharepoint-client-secret --data-file=-
# Les noms exacts de paramètres d'une source sont à confirmer dans la page du connecteur
# (docs.cloud.google.com/gemini/enterprise/docs/connectors/...) : marqués [à confirmer].
# -----------------------------------------------------------------------------Le principe. connectors est une map : chaque clé (gdrive, sharepoint…) est un connecteur, et sa
valeur est un objet qui le décrit. 40_connectors.tf crée un connecteur par entrée
grâce à for_each (voir 07-connectors.md). La clé devient l'ID de collection chez Google :
choisissez-la courte, en minuscules, et ne la changez plus (la changer = supprimer et recréer le connecteur).
Les champs possibles (déclarés dans variables.tf) :
| Champ | Obligatoire ? | Défaut | Rôle |
|---|---|---|---|
data_source | oui | Le type de source Google (google_drive, sharepoint, github…). | |
display_name | oui | Le nom affiché. | |
modes | non | ["FEDERATED"] | FEDERATED = interrogation en direct, sans copie ; DATA_INGESTION = copie indexée chez Google. |
params | non | {} | Paramètres non secrets (URL, ID de tenant…). |
secret_params | non | {} | Paramètres secrets : « nom du paramètre → nom du secret dans Secret Manager ». |
entities | non | [] | Les types d'objets à interroger (fichiers, tickets…), chacun avec un name. |
refresh_interval | non | "86400s" | Fréquence de synchronisation (1 jour). Utile surtout en ingestion. |
static_ip | non | false | Appeler la source depuis des IP fixes. Immuable. |
Les secrets, par leur NOM. Le principe central de la sécurité des connecteurs (guide du socle (docs/00) §6.2) :
- Avant Terraform, l'équipe sécurité crée chaque secret et sa valeur avec
gcloud:gcloud secrets create sharepoint-client-secret --replication-policy=user-managed --locations=europe-west1: crée le secret (une « boîte » vide), stocké uniquement eneurope-west1(UE) ;printf '%s' 'VALEUR' | gcloud secrets versions add sharepoint-client-secret --data-file=-: met la valeur dedans.printf '%s'évite d'ajouter un retour à la ligne parasite ;--data-file=-lit la valeur depuis l'entrée standard, ce qui évite de la voir apparaître dans les arguments de la commande.
- Dans
terraform.tfvars, on écrit seulement le nom :client_secret = "sharepoint-client-secret". - Terraform lit le secret par son nom, donne au compte de Gemini Enterprise le droit de le lire
(
20_data_protection.tf), puis passe au connecteur une référence de la formeprojects/<numéro>/secrets/sharepoint-client-secret/versions/latest. Gemini Enterprise lit la valeur au moment de l'appel. La valeur ne passe jamais par le code, ni par l'état Terraform.
Le marqueur [à confirmer] : un nom de paramètre ou de source pris dans la documentation Google, mais pas encore
vérifié sur un vrai déploiement (guide du socle (docs/00) §16). Au premier apply, si Google refuse le paramètre, lire la page du
connecteur (lien dans le commentaire) ou créer le connecteur une fois dans la console et comparer.
Voyons maintenant chaque entrée.
gdrive et gcalendar : Google Workspace
connectors = {
# Google Workspace : application OAuth gérée par Google, aucun paramètre.
gdrive = {
data_source = "google_drive"
display_name = "Google Drive"
}
gcalendar = {
data_source = "google_calendar"
display_name = "Google Agenda"
}- Seuls les deux champs obligatoires. Tout le reste prend son défaut : mode fédéré, pas de secret (l'application OAuth est gérée par Google), pas d'IP fixe.
- Fédéré = chacun ne voit que les fichiers et événements auxquels il a déjà accès dans Drive / Agenda.
gmail : désactivé
# gmail = { data_source = "google_mail", display_name = "Gmail" } # sur décision explicite (minimisation)- En commentaire volontairement : la messagerie contient beaucoup de données personnelles. À activer seulement sur décision explicite (principe de minimisation, guide du socle (docs/00) §7.3).
- Exemple d'écriture sur une seule ligne : entre accolades, les champs sont alors séparés par des virgules. Sur plusieurs lignes, pas de virgule.
sharepoint : Microsoft 365
# Microsoft 365 : application Entra ID (mono-tenant, consentement admin).
# Avec l'identité Google, les sources Microsoft sont en FEDERATED (l'ingestion exigerait Entra ID).
sharepoint = {
data_source = "sharepoint" # [à confirmer] la doc montre aussi "sharepoint_federated_search"
display_name = "SharePoint"
params = {
instance_uri = "https://acme.sharepoint.com"
tenant_id = "00000000-0000-0000-0000-000000000000"
}
secret_params = {
client_id = "sharepoint-client-id"
client_secret = "sharepoint-client-secret"
}
entities = [{ name = "file" }]
static_ip = true
}- Entra ID (ex-Azure AD) est l'annuaire de Microsoft. On y crée une application (mono-tenant, avec consentement administrateur) que Gemini Enterprise utilise pour interroger SharePoint.
data_source = "sharepoint": [à confirmer], la doc mentionne aussi"sharepoint_federated_search". Repli : essayer l'autre valeur (guide du socle (docs/00) §16).params(non secrets) :instance_uri: l'adresse du SharePoint de l'entreprise ;tenant_id: l'identifiant du tenant (de l'« instance ») Microsoft 365. Les zéros sont à remplacer par le vrai ID (visible dans le portail Entra).
secret_params: l'ID et le secret de l'application Entra. On range aussiclient_iddans Secret Manager, par prudence et pour tout gérer au même endroit. Les valeurs à droite sont des noms de secrets.entities = [{ name = "file" }]: interroger les fichiers. Une liste d'objets : on peut en ajouter d'autres.static_ip = true: IP fixes, à autoriser côté Microsoft (terraform output connector_static_ips, 11-outputs.md).- Pas de
modes: défaut["FEDERATED"]. Le commentaire explique pourquoi : avec l'identité Google, l'ingestion des sources Microsoft exigerait Entra ID comme fournisseur d'identité.
teams
teams = {
data_source = "teams" # recherche fédérée uniquement
display_name = "Microsoft Teams"
params = { tenant_id = "00000000-0000-0000-0000-000000000000" } # [à confirmer]
secret_params = {
client_id = "teams-client-id"
client_secret = "teams-client-secret"
}
static_ip = true
}- Teams n'existe qu'en mode fédéré chez Google.
params: seulement letenant_id, [à confirmer] (nom exact du paramètre à vérifier).- Ses propres secrets (
teams-client-id,teams-client-secret) : on peut utiliser une application Entra différente de SharePoint, avec ses propres droits minimaux. - Pas d'
entities: défaut[].
github
# GitHub : une GitHub App (pas de jeton personnel), installée sur l'organisation.
github = {
data_source = "github"
display_name = "GitHub"
params = { organization = "acme" } # [à confirmer]
secret_params = { client_id = "github-client-id", client_secret = "github-client-secret" }
static_ip = true
}- Une GitHub App (pas un jeton personnel, qui serait lié à une personne et souvent trop puissant), installée sur l'organisation GitHub, avec les permissions minimales.
params = { organization = "acme" }: l'organisation GitHub. Nom du paramètre [à confirmer].secret_paramsécrit sur une ligne : deux noms de secrets, séparés par une virgule.
jira : un exemple d'ingestion (en commentaire)
# Jira : exemple d'ingestion (copie indexée, chiffrée CMEK, droits Jira synchronisés).
# jira = {
# data_source = "jira"
# display_name = "Jira"
# modes = ["DATA_INGESTION"]
# params = { instance_uri = "https://acme.atlassian.net", instance_id = "...", auth_type = "OAUTH" }
# secret_params = { client_id = "jira-client-id", client_secret = "jira-client-secret", refresh_token = "jira-refresh-token" }
# entities = [{ name = "project" }, { name = "issue" }, { name = "comment" }]
# static_ip = true
# }- Désactivé : c'est un modèle pour le cas où l'on choisit l'ingestion.
modes = ["DATA_INGESTION"]: les données sont copiées chez Google. Elles sont alors chiffrées avec la clé CMEK de l'entreprise (automatique dans40_connectors.tfdès que le mode contientDATA_INGESTION).params: l'adresse Atlassian, l'ID de l'instance (à remplir) et le type d'authentification.secret_params: trois secrets, dont unrefresh_token(jeton qui permet d'obtenir de nouveaux accès).entities: trois types d'objets (projets, tickets, commentaires) : en ingestion, il faut dire quoi copier.- Pourquoi l'ingestion n'est pas le défaut : voir guide du socle (docs/00) §7.2.
Databricks
# Databricks : pas de connecteur natif. Voir le guide, section "Databricks".
}- Il n'existe pas de
data_sourceDatabricks : les options (serveur MCP, agent dédié, publication vers BigQuery) sont décrites dans le guide du socle (docs/00) §7.4. L'accolade}ferme la mapconnectors.
3.5 bis Journalisation
# -----------------------------------------------------------------------------
# JOURNALISATION (voir guide §10.4)
# -----------------------------------------------------------------------------
# log_prompts_responses = false # texte complet des questions/réponses : décision DPO/CSE
# security_log_retention_days = 365
# usage_log_retention_days = 90
# lock_security_logs = false # true en production : rétention non modifiable (irréversible)
# link_logs_to_bigquery = false # true pour des tableaux de bord Looker StudioTout est commenté : les valeurs par défaut s'appliquent (le plus sobre). Pour activer un réglage, retirez le #
et changez la valeur. Chaque réglage est une décision à tracer (DPO pour les deux premiers, sécurité pour le
verrouillage). Explications : 13-logging.md et 02-variables.md.
3.6 Les variables absentes : leurs valeurs par défaut
Tout ce qui n'est pas dans le fichier prend la valeur de variables.tf. Les principales :
| Variable | Défaut | Effet |
|---|---|---|
region | "europe-west1" | Région des ressources régionales (Agent Gateway). |
app_id / app_display_name / company_name | "assistant-ia" / "Assistant IA" / "Acme" | Identité de l'app. |
enable_cmek | true | Chiffrement avec les clés de l'entreprise. |
session_ttl_days | 30 | Conservation des conversations (à valider avec le DPO). |
armor_confidence | "MEDIUM_AND_ABOVE" | Sévérité des filtres Model Armor. |
sdp_mode | "advanced" | Détecteurs de données sensibles français / UE. |
log_screened_content | false | Pas de contenu filtré dans les journaux. |
web_grounding | "WEB_GROUNDING_TYPE_DISABLED" | Pas de recherche web. |
features | 4 réglages (partages désactivés, feedback activé) | Voir 08-app.md. |
enable_observability | true | Traces sans contenu. |
enable_agent_gateway / vpc_sc_access_policy | false / "" | Options avancées désactivées (09-advanced.md). |
Pour en changer une, ajoutez simplement la ligne dans terraform.tfvars, par exemple session_ttl_days = 15.
4. Ce que Terraform crée concrètement
Ce fichier ne crée rien lui-même : il paramètre les autres. Avec ces valeurs, les points marquants :
- 5 connecteurs (
gdrive,gcalendar,sharepoint,teams,github), tous en mode fédéré, dont 3 avec IP fixes ; - 6 secrets référencés (2 par source tierce) ; ils doivent déjà exister dans Secret Manager ;
- des droits pour 4 groupes ;
- un canal d'alerte vers
securite-ia@acme.example; - tout le reste selon les défauts (CMEK activé, conservation 30 jours, pas de web, options avancées désactivées).
5. Dépendances avec les autres fichiers
| Valeur | Déclarée dans | Utilisée dans | Fiche |
|---|---|---|---|
| Toutes | variables.tf (types, défauts, validations) | 02-variables.md | |
| Groupes | 10_identity_access.tf | 04-identity-access.md | |
| Noms des secrets | 20_data_protection.tf, 40_connectors.tf | 05-data-protection.md | |
connectors | 40_connectors.tf, outputs.tf | 07-connectors.md, 11-outputs.md | |
banned_phrases | 50_app.tf | 08-app.md | |
alert_email | 70_detection.tf | 10-detection.md |
6. Pour reproduire / adapter
Besoin type : ajouter un nouveau connecteur.
connectors = {
# ... connecteurs existants ...
confluence = { # clé = ID de collection : courte, stable, minuscules
data_source = "confluence" # type de source (page du connecteur dans la doc Google)
display_name = "Confluence"
# modes = ["FEDERATED"] # défaut ; "DATA_INGESTION" seulement sur décision justifiée
params = { instance_uri = "https://monentreprise.atlassian.net" } # NON secret [à confirmer]
secret_params = { # paramètre => NOM du secret (jamais la valeur)
client_id = "confluence-client-id"
client_secret = "confluence-client-secret"
}
# entities = [{ name = "page" }] # si la source en exige [à confirmer]
static_ip = true # IMMUABLE : décider dès la création
}
}- Côté source : créer l'application OAuth (Entra ID, GitHub App, Atlassian…) avec les permissions minimales
en lecture et l'URL de redirection
https://vertexaisearch.cloud.google.com/oauth-redirect(guide du socle (docs/00) §7.6). - Équipe sécurité : créer les secrets en UE, avant Terraform :
gcloud secrets create confluence-client-secret --replication-policy=user-managed --locations=europe-west1 printf '%s' 'VALEUR' | gcloud secrets versions add confluence-client-secret --data-file=- - Code : ajouter l'entrée dans
terraform.tfvars; reporter le format (sans valeurs réelles) dansterraform.tfvars.examplepour documenter. Noms des paramètres : page du connecteur (docs.cloud.google.com/gemini/enterprise/docs/connectors/…) et doc Terraform https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/discovery_engine_data_connector terraform plan: vérifier qu'il n'y a qu'un connecteur ajouté (+), et lire si l'app est recréée (voir §7).- Après
apply: autoriser les IP (terraform output connector_static_ips) côté source, faire l'autorisation OAuth initiale dans la console, vérifierterraform output connector_states. - Recette : un utilisateur sans droit sur un document de test ne doit pas le voir dans l'assistant.
7. Pièges et bonnes pratiques
- Ne jamais committer
terraform.tfvars(niterraform.tfstate). Ajoutez-les à un.gitignore. - Jamais de valeur secrète dans ce fichier : seulement des noms de secrets. Une valeur ici finirait dans Git, dans l'état et dans les journaux de CI.
- Les secrets doivent exister avant
plan: Terraform les lit par leur nom ; un secret absent fait échouer leplan(« secret not found »). Vérifiez l'orthographe exacte. - Clé de connecteur = immuable en pratique : la renommer supprime et recrée le connecteur (et ses data stores).
static_ipetge_locationsont immuables : les décider dès la création.- Ajouter un connecteur modifie
data_store_idsde l'app, ce qui peut recréer l'app (bug connu du provider, guide du socle (docs/00) §16). Lisez leplan; idéalement, déclarez tous les connecteurs avant l'ouverture aux utilisateurs. [à confirmer]= à vérifier au premier déploiement. Retirez le marqueur une fois le nom validé, pour que la prochaine personne sache que c'est sûr.- Les ID de tenant
00000000-…sont des exemples : les laisser ferait échouer le connecteur. - Préfixe
group:obligatoire dans les listes de groupes : sans lui, Google refuse le membre IAM. - Plusieurs environnements (recette, production) : un fichier par environnement (
recette.tfvars,prod.tfvars) passé avec-var-file=…, et un état séparé pour chacun.