Compare commits
2
Commits
839d3978d3
...
0ec23ca6c4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ec23ca6c4 | ||
|
|
e56eb80dfe |
@@ -9,3 +9,6 @@ tfplan*
|
|||||||
|
|
||||||
# VS Code
|
# VS Code
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Gemini Enterprise Agent Platform — Terraform Scaffold
|
# Gemini Enterprise Agent Platform - Terraform Scaffold
|
||||||
|
|
||||||
Provisions the infrastructure needed to use the Gemini Enterprise Agent Platform (Vertex AI Agent Engine) on an existing GCP project: required APIs, service accounts, and IAM bindings.
|
Provisions the infrastructure needed to use the Gemini Enterprise Agent Platform (Vertex AI Agent Engine) on an existing GCP project: required APIs, service accounts, and IAM bindings.
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ Provisions the infrastructure needed to use the Gemini Enterprise Agent Platform
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp terraform.tfvars.example terraform.tfvars
|
cp terraform.tfvars.example terraform.tfvars
|
||||||
# edit terraform.tfvars — set your project_id
|
# edit terraform.tfvars - set your project_id
|
||||||
|
|
||||||
terraform init
|
terraform init
|
||||||
terraform plan
|
terraform plan
|
||||||
@@ -25,7 +25,7 @@ terraform apply
|
|||||||
|
|
||||||
| Name | Required | Default | Description |
|
| Name | Required | Default | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `project_id` | yes | — | Existing GCP project ID |
|
| `project_id` | yes | - | Existing GCP project ID |
|
||||||
| `prefix` | no | `gemini` | Short prefix applied to all resource names |
|
| `prefix` | no | `gemini` | Short prefix applied to all resource names |
|
||||||
|
|
||||||
## Outputs
|
## Outputs
|
||||||
@@ -39,7 +39,7 @@ terraform apply
|
|||||||
## What gets created
|
## What gets created
|
||||||
|
|
||||||
- **7 GCP APIs** enabled (`aiplatform`, `cloudaicompanion`, `discoveryengine`, `dialogflow`, `secretmanager`, `iam`, `cloudresourcemanager`)
|
- **7 GCP APIs** enabled (`aiplatform`, `cloudaicompanion`, `discoveryengine`, `dialogflow`, `secretmanager`, `iam`, `cloudresourcemanager`)
|
||||||
- **2 service accounts** — one for app runtime, one for IDE enterprise config
|
- **2 service accounts** - one for app runtime, one for IDE enterprise config
|
||||||
- **4 project IAM bindings**
|
- **4 project IAM bindings**
|
||||||
|
|
||||||
## Setting up credentials
|
## Setting up credentials
|
||||||
@@ -51,7 +51,7 @@ gcloud auth application-default login
|
|||||||
gcloud config set project <your-project-id>
|
gcloud config set project <your-project-id>
|
||||||
```
|
```
|
||||||
|
|
||||||
For workloads running on GCP (Cloud Run, GKE, Compute Engine), attach the service account to the resource — no credentials file needed.
|
For workloads running on GCP (Cloud Run, GKE, Compute Engine), attach the service account to the resource - no credentials file needed.
|
||||||
|
|
||||||
## Granting developer access to Gemini Code Assist
|
## Granting developer access to Gemini Code Assist
|
||||||
|
|
||||||
@@ -113,5 +113,5 @@ gcloud billing accounts list
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[MIT](LICENSE) © 2026 Slawomir Koszewski
|
[MIT](LICENSE) (c) 2026 Slawomir Koszewski
|
||||||
|
|
||||||
|
|||||||
@@ -196,16 +196,16 @@ def generate_report(
|
|||||||
Guidelines:
|
Guidelines:
|
||||||
- The list of changes above must be reproduced verbatim in the report under a "Planned Changes" section, preserving the grouping.
|
- The list of changes above must be reproduced verbatim in the report under a "Planned Changes" section, preserving the grouping.
|
||||||
- Focus exclusively on what is changing and whether it is expected.
|
- Focus exclusively on what is changing and whether it is expected.
|
||||||
- Deletions and replacements are highest priority — analyse each one individually: why might it be deleted or replaced, and is that intentional?
|
- Deletions and replacements are highest priority - analyse each one individually: why might it be deleted or replaced, and is that intentional?
|
||||||
- For updates and creates, note anything surprising in scope or naming.
|
- For updates and creates, note anything surprising in scope or naming.
|
||||||
- Do not include implementation advice — the plan is already the product of implementation.
|
- Do not include implementation advice - the plan is already the product of implementation.
|
||||||
- Do not pad with generic statements.
|
- Do not pad with generic statements.
|
||||||
|
|
||||||
Structure:
|
Structure:
|
||||||
1. **Change Summary** — one-paragraph overview of the overall scope
|
1. **Change Summary** - one-paragraph overview of the overall scope
|
||||||
2. **Planned Changes** — verbatim list from above
|
2. **Planned Changes** - verbatim list from above
|
||||||
3. **Deletions & Replacements Analysis** — only if any exist; individual analysis per resource
|
3. **Deletions & Replacements Analysis** - only if any exist; individual analysis per resource
|
||||||
4. **Scope Review** — does the set of changes look coherent and complete? flag anomalies"""
|
4. **Scope Review** - does the set of changes look coherent and complete? flag anomalies"""
|
||||||
|
|
||||||
system_instruction = "You are a senior infrastructure engineer reviewing a Terraform plan before it is applied. Your job is to identify whether the changes match the deployment intent and surface anything worth scrutinising."
|
system_instruction = "You are a senior infrastructure engineer reviewing a Terraform plan before it is applied. Your job is to identify whether the changes match the deployment intent and surface anything worth scrutinising."
|
||||||
|
|
||||||
@@ -214,11 +214,11 @@ Structure:
|
|||||||
from google import genai
|
from google import genai
|
||||||
from google.genai import types
|
from google.genai import types
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("❌ Error: google-genai package is not installed. Please install it to use Gemini.", file=sys.stderr)
|
print("[ERROR] google-genai package is not installed. Please install it to use Gemini.", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if not gcp_project:
|
if not gcp_project:
|
||||||
print("❌ Error: GCP project must be specified via --gcp-project or GOOGLE_CLOUD_PROJECT environment variable for Gemini.", file=sys.stderr)
|
print("[ERROR] GCP project must be specified via --gcp-project or GOOGLE_CLOUD_PROJECT environment variable for Gemini.", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
client = genai.Client(vertexai=True, project=gcp_project, location=gcp_location)
|
client = genai.Client(vertexai=True, project=gcp_project, location=gcp_location)
|
||||||
@@ -339,36 +339,36 @@ def main():
|
|||||||
plan_file = Path.cwd() / plan_file
|
plan_file = Path.cwd() / plan_file
|
||||||
|
|
||||||
if not plan_file.exists():
|
if not plan_file.exists():
|
||||||
print(f"❌ Error: {plan_file} not found", file=sys.stderr)
|
print(f"[ERROR] {plan_file} not found", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print(f"📋 Loading plan from {plan_file}...", file=sys.stderr)
|
print(f"[INFO] Loading plan from {plan_file}...", file=sys.stderr)
|
||||||
plan = load_plan(plan_file)
|
plan = load_plan(plan_file)
|
||||||
|
|
||||||
print("🔍 Extracting resource changes...", file=sys.stderr)
|
print("[INFO] Extracting resource changes...", file=sys.stderr)
|
||||||
categorised = extract_resource_changes(plan)
|
categorised = extract_resource_changes(plan)
|
||||||
total = sum(len(v) for v in categorised.values())
|
total = sum(len(v) for v in categorised.values())
|
||||||
|
|
||||||
if not categorised:
|
if not categorised:
|
||||||
print("⚠️ No resource changes found in the plan", file=sys.stderr)
|
print("[WARN] No resource changes found in the plan", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
for action in ACTION_ORDER:
|
for action in ACTION_ORDER:
|
||||||
count = len(categorised.get(action, []))
|
count = len(categorised.get(action, []))
|
||||||
if count:
|
if count:
|
||||||
print(f" {ACTION_LABELS[action]}: {count}", file=sys.stderr)
|
print(f" {ACTION_LABELS[action]}: {count}", file=sys.stderr)
|
||||||
print(f"✅ Total changes: {total}", file=sys.stderr)
|
print(f"[OK] Total changes: {total}", file=sys.stderr)
|
||||||
|
|
||||||
# Generate report
|
# Generate report
|
||||||
if args.ai:
|
if args.ai:
|
||||||
if args.gemini:
|
if args.gemini:
|
||||||
if args.model == MODEL_DEFAULT:
|
if args.model == MODEL_DEFAULT:
|
||||||
args.model = "gemini-2.5-flash"
|
args.model = "gemini-2.5-flash"
|
||||||
print(f"🚀 Generating AI report with Gemini ({args.model})...", file=sys.stderr)
|
print(f"[INFO] Generating AI report with Gemini ({args.model})...", file=sys.stderr)
|
||||||
elif args.azure_endpoint:
|
elif args.azure_endpoint:
|
||||||
print(f"🚀 Generating AI report with Azure OpenAI ({args.model})...", file=sys.stderr)
|
print(f"[INFO] Generating AI report with Azure OpenAI ({args.model})...", file=sys.stderr)
|
||||||
else:
|
else:
|
||||||
print(f"🚀 Generating AI report with OpenAI endpoint ({args.model})...", file=sys.stderr)
|
print(f"[INFO] Generating AI report with OpenAI endpoint ({args.model})...", file=sys.stderr)
|
||||||
|
|
||||||
report_start = time.time()
|
report_start = time.time()
|
||||||
report = generate_report(
|
report = generate_report(
|
||||||
@@ -384,9 +384,9 @@ def main():
|
|||||||
args.gcp_location
|
args.gcp_location
|
||||||
)
|
)
|
||||||
report_elapsed = time.time() - report_start
|
report_elapsed = time.time() - report_start
|
||||||
print(f" ✓ Completed in {report_elapsed:.2f}s", file=sys.stderr)
|
print(f" [OK] Completed in {report_elapsed:.2f}s", file=sys.stderr)
|
||||||
else:
|
else:
|
||||||
print("📄 Generating plain report...", file=sys.stderr)
|
print("[INFO] Generating plain report...", file=sys.stderr)
|
||||||
report = generate_plain_report(categorised, plan)
|
report = generate_plain_report(categorised, plan)
|
||||||
|
|
||||||
# Determine output filename
|
# Determine output filename
|
||||||
@@ -402,12 +402,12 @@ def main():
|
|||||||
with open(output_file, "w") as f:
|
with open(output_file, "w") as f:
|
||||||
f.write(report)
|
f.write(report)
|
||||||
|
|
||||||
print(f"📄 Report written to {output_file}", file=sys.stderr)
|
print(f"[INFO] Report written to {output_file}", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
main()
|
main()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\n⚠️ Interrupted by user", file=sys.stderr)
|
print("\n[WARN] Interrupted by user", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ─────────────────────────────────────────────
|
# ---------------------------------------------
|
||||||
# 1. API Enablement
|
# 1. API Enablement
|
||||||
# ─────────────────────────────────────────────
|
# ---------------------------------------------
|
||||||
|
|
||||||
locals {
|
locals {
|
||||||
apis = toset([
|
apis = toset([
|
||||||
@@ -24,9 +24,9 @@ resource "google_project_service" "apis" {
|
|||||||
disable_dependent_services = false
|
disable_dependent_services = false
|
||||||
}
|
}
|
||||||
|
|
||||||
# ─────────────────────────────────────────────
|
# ---------------------------------------------
|
||||||
# 2. Service Accounts
|
# 2. Service Accounts
|
||||||
# ─────────────────────────────────────────────
|
# ---------------------------------------------
|
||||||
|
|
||||||
resource "google_service_account" "agent_sa" {
|
resource "google_service_account" "agent_sa" {
|
||||||
project = var.project_id
|
project = var.project_id
|
||||||
@@ -46,9 +46,9 @@ resource "google_service_account" "code_assist_sa" {
|
|||||||
depends_on = [google_project_service.apis]
|
depends_on = [google_project_service.apis]
|
||||||
}
|
}
|
||||||
|
|
||||||
# ─────────────────────────────────────────────
|
# ---------------------------------------------
|
||||||
# 3. Project-level IAM Bindings (additive)
|
# 3. Project-level IAM Bindings (additive)
|
||||||
# ─────────────────────────────────────────────
|
# ---------------------------------------------
|
||||||
|
|
||||||
resource "google_project_iam_member" "agent_sa_aiplatform_user" {
|
resource "google_project_iam_member" "agent_sa_aiplatform_user" {
|
||||||
project = var.project_id
|
project = var.project_id
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Copy this file to terraform.tfvars and fill in your values.
|
# Copy this file to terraform.tfvars and fill in your values.
|
||||||
# terraform.tfvars is excluded from git (see .gitignore).
|
# terraform.tfvars is excluded from git (see .gitignore).
|
||||||
|
|
||||||
project_id = "your-gcp-project-id" # required — your existing GCP project
|
project_id = "your-gcp-project-id" # required - your existing GCP project
|
||||||
prefix = "gemini" # optional, default: gemini
|
prefix = "gemini" # optional, default: gemini
|
||||||
|
|||||||
Reference in New Issue
Block a user