Skip to content

Commit 1b20af6

Browse files
Added GCP service base module
1 parent bb25044 commit 1b20af6

12 files changed

Lines changed: 472 additions & 6 deletions

File tree

.terraform.lock.hcl

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

main.tf

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -250,12 +250,73 @@ module "agm_talk" {
250250
source = "./modules/agm-talk"
251251
}
252252

253-
# Simple GCS bucket to validate GCP provider connectivity in CI
254-
resource "google_storage_bucket" "test" {
255-
name = "${var.gcp_project_id}-tf-test"
256-
location = var.gcp_region
257-
force_destroy = true
258-
uniform_bucket_level_access = true
253+
# =============================================================================
254+
# GCP Platform Infrastructure
255+
# Shared VPC and alerting owned by the platform team. Service teams deploy
256+
# into this network via their own wrapper modules below.
257+
# =============================================================================
258+
259+
resource "google_compute_network" "platform" {
260+
name = "platform-services"
261+
auto_create_subnetworks = false
262+
project = var.gcp_project_id
263+
}
264+
265+
resource "google_compute_subnetwork" "platform" {
266+
name = "platform-europe-west2"
267+
ip_cidr_range = "10.10.0.0/24"
268+
region = var.gcp_region
269+
network = google_compute_network.platform.id
270+
project = var.gcp_project_id
271+
}
272+
273+
resource "google_compute_firewall" "platform_ssh" {
274+
name = "platform-allow-ssh-iap"
275+
network = google_compute_network.platform.id
276+
project = var.gcp_project_id
277+
description = "Allow SSH via Identity-Aware Proxy for instance management"
278+
279+
allow {
280+
protocol = "tcp"
281+
ports = ["22"]
282+
}
283+
284+
source_ranges = ["35.235.240.0/20"]
285+
target_tags = ["allow-ssh"]
286+
}
287+
288+
resource "google_pubsub_topic" "platform_alerts" {
289+
name = "platform-alerts"
290+
project = var.gcp_project_id
291+
292+
labels = {
293+
environment = "production"
294+
managed-by = "terraform"
295+
}
296+
}
297+
298+
# =============================================================================
299+
# Service Deployments (each team owns their wrapper module)
300+
# =============================================================================
301+
302+
module "payments_service" {
303+
source = "./modules/gcp-service-payments"
304+
305+
network = google_compute_network.platform.id
306+
subnet = google_compute_subnetwork.platform.id
307+
project_id = var.gcp_project_id
308+
region = var.gcp_region
309+
alert_topic = google_pubsub_topic.platform_alerts.id
310+
}
311+
312+
module "inventory_service" {
313+
source = "./modules/gcp-service-inventory"
314+
315+
network = google_compute_network.platform.id
316+
subnet = google_compute_subnetwork.platform.id
317+
project_id = var.gcp_project_id
318+
region = var.gcp_region
319+
alert_topic = google_pubsub_topic.platform_alerts.id
259320
}
260321

261322
module "api_access" {

modules/gcp-service-base/main.tf

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
data "google_compute_image" "debian" {
2+
family = "debian-12"
3+
project = "debian-cloud"
4+
}
5+
6+
resource "google_compute_instance" "service" {
7+
name = var.service_name
8+
machine_type = "e2-micro"
9+
zone = "${var.region}-b"
10+
project = var.project_id
11+
12+
boot_disk {
13+
initialize_params {
14+
image = data.google_compute_image.debian.self_link
15+
}
16+
}
17+
18+
network_interface {
19+
network = var.network
20+
subnetwork = var.subnet
21+
}
22+
23+
tags = [var.service_name, "allow-ssh"]
24+
25+
metadata_startup_script = <<-EOF
26+
#!/bin/bash
27+
cat > /opt/health_server.py << 'PY'
28+
from http.server import BaseHTTPRequestHandler, HTTPServer
29+
30+
class Handler(BaseHTTPRequestHandler):
31+
def do_GET(self):
32+
if self.path in ("/health", "/healthz", "/"):
33+
self.send_response(200)
34+
self.send_header("Content-Type", "text/plain")
35+
self.end_headers()
36+
self.wfile.write(b"ok\n")
37+
return
38+
self.send_response(404)
39+
self.end_headers()
40+
41+
def log_message(self, format, *args):
42+
return
43+
44+
if __name__ == "__main__":
45+
HTTPServer(("0.0.0.0", 9090), Handler).serve_forever()
46+
PY
47+
nohup python3 /opt/health_server.py &
48+
EOF
49+
50+
labels = {
51+
service = var.service_name
52+
environment = "production"
53+
team = var.team
54+
managed-by = "terraform"
55+
}
56+
}
57+
58+
# Service ingress — allows application and health-check traffic from internal ranges.
59+
# target_tags binds this rule to instances carrying the service_name tag.
60+
resource "google_compute_firewall" "service_ingress" {
61+
name = "${var.service_name}-ingress"
62+
network = var.network
63+
project = var.project_id
64+
description = "Allow ${var.service_name} traffic on service and health-check ports"
65+
66+
allow {
67+
protocol = "tcp"
68+
ports = [tostring(var.service_port), "9090"]
69+
}
70+
71+
source_ranges = var.allowed_source_ranges
72+
target_tags = [var.service_name]
73+
}
74+
75+
# Dedicated rule for Google Cloud health-check probes (source ranges are fixed
76+
# Google-owned prefixes). Also targets by service_name tag.
77+
resource "google_compute_firewall" "health_check" {
78+
name = "${var.service_name}-health-check"
79+
network = var.network
80+
project = var.project_id
81+
description = "Allow Google Cloud health-check probes to ${var.service_name}"
82+
83+
allow {
84+
protocol = "tcp"
85+
ports = ["9090"]
86+
}
87+
88+
source_ranges = ["35.191.0.0/16", "130.211.0.0/22"]
89+
target_tags = [var.service_name]
90+
}
91+
92+
resource "google_compute_health_check" "service" {
93+
name = "${var.service_name}-health"
94+
project = var.project_id
95+
96+
tcp_health_check {
97+
port = 9090
98+
}
99+
100+
check_interval_sec = 10
101+
timeout_sec = 5
102+
healthy_threshold = 2
103+
unhealthy_threshold = 3
104+
}
105+
106+
resource "google_monitoring_notification_channel" "oncall" {
107+
display_name = "${var.service_name} On-Call (${var.team})"
108+
type = "pubsub"
109+
project = var.project_id
110+
111+
labels = {
112+
topic = var.alert_topic
113+
}
114+
}
115+
116+
resource "google_monitoring_alert_policy" "health" {
117+
display_name = "${var.service_name} Health Check Failed"
118+
combiner = "OR"
119+
project = var.project_id
120+
121+
conditions {
122+
display_name = "${var.service_name} Instance Not Running"
123+
124+
condition_threshold {
125+
filter = "resource.type = \"gce_instance\" AND resource.labels.instance_id = \"${google_compute_instance.service.instance_id}\" AND metric.type = \"compute.googleapis.com/instance/uptime\""
126+
comparison = "COMPARISON_LT"
127+
threshold_value = 1
128+
duration = "300s"
129+
130+
aggregations {
131+
alignment_period = "300s"
132+
per_series_aligner = "ALIGN_RATE"
133+
}
134+
}
135+
}
136+
137+
notification_channels = [google_monitoring_notification_channel.oncall.id]
138+
139+
user_labels = {
140+
service = var.service_name
141+
team = var.team
142+
}
143+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
output "instance_name" {
2+
description = "Name of the service instance"
3+
value = google_compute_instance.service.name
4+
}
5+
6+
output "instance_id" {
7+
description = "Server-assigned instance ID"
8+
value = google_compute_instance.service.instance_id
9+
}
10+
11+
output "instance_internal_ip" {
12+
description = "Internal IP address of the service instance"
13+
value = google_compute_instance.service.network_interface[0].network_ip
14+
}
15+
16+
output "firewall_rule_name" {
17+
description = "Name of the service ingress firewall rule"
18+
value = google_compute_firewall.service_ingress.name
19+
}
20+
21+
output "health_check_firewall_name" {
22+
description = "Name of the health-check probe firewall rule"
23+
value = google_compute_firewall.health_check.name
24+
}
25+
26+
output "health_check_name" {
27+
description = "Name of the TCP health check"
28+
value = google_compute_health_check.service.name
29+
}
30+
31+
output "alert_policy_name" {
32+
description = "Display name of the monitoring alert policy"
33+
value = google_monitoring_alert_policy.health.display_name
34+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
variable "service_name" {
2+
description = "Name of the service — used for resource naming and GCE network tags"
3+
type = string
4+
}
5+
6+
variable "service_port" {
7+
description = "Primary port the service listens on"
8+
type = number
9+
}
10+
11+
variable "allowed_source_ranges" {
12+
description = "CIDR ranges allowed to reach the service"
13+
type = list(string)
14+
default = ["10.0.0.0/8"]
15+
}
16+
17+
variable "network" {
18+
description = "VPC network self_link or ID"
19+
type = string
20+
}
21+
22+
variable "subnet" {
23+
description = "Subnetwork self_link or ID"
24+
type = string
25+
}
26+
27+
variable "project_id" {
28+
description = "GCP project ID"
29+
type = string
30+
}
31+
32+
variable "region" {
33+
description = "GCP region for regional resources"
34+
type = string
35+
}
36+
37+
variable "team" {
38+
description = "Team identifier for labels and alert routing"
39+
type = string
40+
}
41+
42+
variable "alert_topic" {
43+
description = "Full Pub/Sub topic ID (projects/{project}/topics/{name}) for alert notifications"
44+
type = string
45+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module "base" {
2+
source = "../gcp-service-base"
3+
4+
service_name = "inventory-api"
5+
service_port = 8080
6+
network = var.network
7+
subnet = var.subnet
8+
project_id = var.project_id
9+
region = var.region
10+
team = "inventory"
11+
alert_topic = var.alert_topic
12+
}
13+
14+
resource "google_pubsub_topic" "stock_events" {
15+
name = "inventory-stock-events"
16+
project = var.project_id
17+
18+
labels = {
19+
service = "inventory-api"
20+
team = "inventory"
21+
}
22+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
output "instance_name" {
2+
description = "Name of the inventory API instance"
3+
value = module.base.instance_name
4+
}
5+
6+
output "instance_internal_ip" {
7+
description = "Internal IP of the inventory API instance"
8+
value = module.base.instance_internal_ip
9+
}
10+
11+
output "firewall_rule_name" {
12+
description = "Name of the inventory ingress firewall rule"
13+
value = module.base.firewall_rule_name
14+
}
15+
16+
output "health_check_name" {
17+
description = "Name of the inventory health check"
18+
value = module.base.health_check_name
19+
}
20+
21+
output "stock_events_topic" {
22+
description = "Pub/Sub topic for inventory stock events"
23+
value = google_pubsub_topic.stock_events.name
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
variable "network" {
2+
description = "VPC network self_link or ID"
3+
type = string
4+
}
5+
6+
variable "subnet" {
7+
description = "Subnetwork self_link or ID"
8+
type = string
9+
}
10+
11+
variable "project_id" {
12+
description = "GCP project ID"
13+
type = string
14+
}
15+
16+
variable "region" {
17+
description = "GCP region"
18+
type = string
19+
}
20+
21+
variable "alert_topic" {
22+
description = "Full Pub/Sub topic ID for alert notifications"
23+
type = string
24+
}

0 commit comments

Comments
 (0)