CRITICAL CONSTRAINTS
- •NEVER apply/destroy/plan → validate/fmt only
- •Init ALWAYS with -upgrade flag
- •Write configs NOT execute
- •Analyze project first → match structure, style, naming, conventions exactly
- •Mirror existing patterns
Repository Organization
Dual Strategy
Service-First (Root):
- •
/jfrog,/github,/coralogix,/hex,/snowflake,/okta,/opsgenie,/open-ai,/sentra,/mapbox,/doit,/arcgis→ cross-environment - •
/aws,/azure→ cloud-specific - •
/modules→ reusable local - •
/org→ GCP org-level (policies, custom roles, SAs) - •
/iam→ global IAM - •
/dns_zones,/domains→ DNS
Environment-First (/us):
- •
/us/{dev|staging|prod}/app-infra/{service}/→ service-specific- •Services:
analysts,bi,dps,feedx,gtm,guru,incubation,ingestion,insights-analyst,marketing,mip,octo,poi,retail-sales
- •Services:
- •
/us/{dev|staging|prod}/infra/{component}/→ shared- •Components:
jenkins,databricks,vault,k8s,artifact-registry,mlflow,grafana,holmesgpt
- •Components:
- •
/us/{dev|staging|prod}/buckets/{service}/→ GCS buckets - •
/us/{dev|staging|prod}/hierarchy/{project}/→ GCP projects - •
/us/network/→ shared VPC (env-agnostic) - •
/us/certificates/→ SSL
Cell Architecture:
- •Each dir = independent root module
- •Isolated state per component
- •
/us/{env}→ env-specific, root → cross-env
State Management
Backend (GCS only):
terraform {
backend "gcs" {
bucket = "placer-terraform-boot-bucket"
prefix = "<path-matching-directory>"
}
}
Examples:
- •
/dns_zones/→"dns_zones" - •
/us/dev/infra/holmesgpt/→"dev/infra/holmesgpt" - •
/us/prod/app-infra/guru/quota-management/postgres/→"prod/app-infra/guru/quota-management/postgres"
Rules:
- •Prefix mirrors directory path
- •Isolated per component
- •No shared state
Naming
Service Accounts: <service>-<env>-<purpose>-sa
bi-dev-scheduledqs-sa@bi-dev-services.iam.gserviceaccount.com guru-composer-sa@guru-dev-services.iam.gserviceaccount.com
Projects: {service}-{env}-services
placer-management-services bi-dev-services guru-prod-services
Resources: {service}_{component}
resource "google_service_account" "doit_cmp_sa" { ... }
Databases: ${app_name}-db
locals { app_name = "quota-management" }
module "pgsql" { name = "${local.app_name}-db" }
Providers
Primary:
- •GCP → main cloud
- •Vault → secrets (
https://vault-prod.placer.team/)
Cloud: AWS, Azure, Snowflake, Databricks
Platform: Okta, GitHub, Artifactory, Airflow, Kafka, Kubernetes, OpenAI, Coralogix, Opsgenie, DNSimple
Version Constraints:
version = "< 7.9" # Upper bound (preferred) version = ">= 0.9.1, < 1.0.0" # Range version = "2.9.0" # Exact
Aliases:
provider "snowflake" { alias = "snowflake_aws" }
provider "snowflake" { alias = "snowflake_gcp" }
provider "aws" { alias = "org" }
Modules
Structure:
/modules/{name}/
main.tf
variables.tf
outputs.tf
providers.tf
locals.tf
Sourcing:
# External versioned source = "git@github.com:placer-engineering/placer-terraform-mod-gke.git?ref=2.15.0" source = "git@github.com:placer-engineering/terraform-google-composer.git//modules/create_environment_v2?ref=v4.0.2" # Local source = "../../../modules/openai-project-key"
Wrapping (local wraps external):
# /modules/google-composer/main.tf
module "composer_environment_v2" {
source = "git@github.com:placer-engineering/terraform-google-composer.git//modules/create_environment_v2?ref=v4.0.2"
}
Database:
module "pgsql" {
source = "git@github.com:placer-engineering/placer-terraform-mod-gcp-sql.git//postgres?ref=3.22.0"
name = "${local.app_name}-db"
team = local.team
env = local.env
vault_path = "kv/${local.env}/apps/${local.app_name}/db"
deletion_protection = true
enable_vault_integration = false
}
Security & Lifecycle
Prevent Destroy (for Artifactory repos, Okta apps, AWS accounts, SAML):
lifecycle { prevent_destroy = true }
Ignore Changes:
lifecycle { ignore_changes = [users_names, groups, member] } # Externally managed
lifecycle { ignore_changes = [key_algorithm] } # Certs
lifecycle { ignore_changes = [url] } # External updates
lifecycle { ignore_changes = [log_config, headers, end_date] } # Auto-managed
Ephemeral Secrets (Terraform 1.10+):
ephemeral "vault_kv_secret_v2" "okta_credentials" {
mount = "kv"
name = "devops/okta/credentials"
}
provider "okta" {
org_name = ephemeral.vault_kv_secret_v2.okta_credentials.data["org_name"]
api_token = ephemeral.vault_kv_secret_v2.okta_credentials.data["api_token"]
}
Vault Token:
data "google_secret_manager_secret_version" "vault_token" {
secret = "vault_prod_root_token"
project = "placer-management-services"
}
provider "vault" {
address = "https://vault-prod.placer.team/"
token = data.google_secret_manager_secret_version.vault_token.secret_data
}
Workload Identity (K8s-GCP):
resource "google_project_iam_member" "vertex_ai" {
member = "principal://iam.googleapis.com/projects/${data.google_project.project.number}/locations/global/workloadIdentityPools/${data.google_project.project.name}.svc.id.goog/subject/ns/${local.namespace}/sa/${local.service_account_name}"
role = "roles/aiplatform.user"
}
Configuration
Locals over tfvars:
locals {
env = "prod"
app_name = "quota-management"
team = "guru"
gcp_project_id = "guru-prod-services"
dataset_access = {
"team-a" = ["dataset1", "dataset2"]
"team-b" = ["dataset3"]
}
}
Tfvars usage: Only /us/network/ for VPC config
Hierarchy (/us/{env}/hierarchy/):
module "project" {
source = "git@github.com:placer-engineering/placer-terraform-mod-project.git?ref=1.18.0"
prefix = local.project_logical_name
environment = local.env
iam = { dataflow_vpc_access = true, metastore_vpc_access = true }
labels = { division = "ingestion", team = "poi" }
}
Network (/us/network/shared-net/):
- •Single shared VPC, 100+ subnets
- •Subnets:
{service}-{env}-{purpose} - •Secondary ranges for K8s
- •IP planning in
terraform.tfvars
CI/CD
GitHub Actions (primary):
- •PR Agent: Claude Sonnet 4 (
vertex_ai/claude-sonnet-4@20250514), fallback Gemini 2.5 Pro - •Workload Identity auth
- •Non-DevOps only
TFLint:
plugin "google" { enabled = true, version = "0.36.0" }
plugin "aws" { enabled = true, version = "0.43.0" }
Disabled: terraform_module_pinned_source, terraform_typed_variables, terraform_documented_variables, terraform_required_providers, terraform_required_version
Jenkins (legacy): Docker builds, /docker/Jenkinsfile, /jobs/*/Jenkinsfile
Core Features
Resources:
- •
resource→ create infra - •
data→ external data, computed values - •
for_each,count→ iteration - •
dynamic→ nested blocks - •Conditionals →
count = var.enabled ? 1 : 0
Variables:
- •
variable→ inputs - •
locals→ computed (prefer over tfvars) - •
output→ expose - •Types:
string,number,bool,list(type),map(type),object({...}) - •Validation:
validation { condition, error_message }
Modules:
- •Hierarchical
- •Dependency injection via variables/outputs
- •Minimal interfaces
- •Git refs for versions
Functions:
- •Template:
templatefile(),jsonencode(),yamlencode() - •File:
file(),filebase64() - •String:
join(),split(),replace(),regex() - •Collection:
concat(),merge(),flatten(),lookup() - •Validation: precondition/postcondition
Multi-Environment
Structure:
- •
/us/{dev|staging|prod}/{app-infra|infra|buckets|hierarchy}/ - •
/us/network/(env-agnostic)
State:
- •Isolated per env
- •Prefix:
{env}/infra/k8s - •No workspaces → separate dirs
Config:
- •
locals.tffor env values - •Minimal tfvars
- •
env = "dev"in locals
Promotion: dev → staging → prod, same module versions, env values in locals
Multi-Cloud
Abstraction:
- •GCP → compute, storage, network
- •AWS → specific services, cross-account
- •Azure → blobs, service principals
- •Snowflake → data warehouse
- •Databricks → analytics
Dependencies: Data sources, outputs between clouds
Cost: Labels (GCP) / tags (AWS): division, team, env via locals
Troubleshooting
State:
- •
terraform state list|show|mv|rm|import - •Never manual edit
Providers:
- •
terraform init -upgrade - •Lock:
terraform.lock.hcl - •Cache:
.terraform/providers/
Drift: Validate regularly, update or revert manual changes, data sources for external
Upgrades:
- •Update Git ref
?ref=new-version - •Test in dev
- •Review CHANGELOG
- •
terraform init -upgrade
Behavioral Traits
Safety: NEVER apply/destroy/plan → validate/fmt only, protect state, prevent_destroy for stateful
Consistency: Analyze first, match patterns, dual org strategy, mirror interfaces
Design: DRY, version constraints, data > hardcoded, locals > tfvars
Simplicity: KISS, flat hierarchies, clear names
Quality: TFLint, AI reviews, Workload Identity, ephemeral secrets, lifecycle rules
Scale: Multi-env consistency, isolated state, scalable modules, separation of concerns
Documentation: Clear interfaces, READMEs, upgrade strategies
Workflow
- •Analyze project → patterns, conventions, structure
- •Identify env → dev/staging/prod or cross-env
- •Determine location →
/us/{env}/vs root - •Design module → local wrapper vs external versioned
- •Apply naming → SAs, projects, resources
- •Configure backend → GCS, prefix matches dir
- •Use locals for env values
- •Security → prevent_destroy, Workload Identity, ephemeral secrets
- •Providers → version constraints, aliases
- •Run
terraform validateandterraform fmt - •Validate with TFLint
- •Document → rationale, dependencies, upgrades