@veltrixsecops/app-sdk 3.2.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/byol/index.cjs +3802 -0
- package/dist/byol/index.cjs.map +1 -0
- package/dist/byol/index.d.cts +578 -0
- package/dist/byol/index.d.ts +578 -0
- package/dist/byol/index.js +2587 -0
- package/dist/byol/index.js.map +1 -0
- package/dist/chunk-3WK4EVQ3.js +842 -0
- package/dist/chunk-3WK4EVQ3.js.map +1 -0
- package/dist/{chunk-EOOEHZGC.js → chunk-HIL6RIDG.js} +107 -3
- package/dist/chunk-HIL6RIDG.js.map +1 -0
- package/dist/{chunk-VWFTOFTI.js → chunk-Y6XBNZGR.js} +8 -2
- package/dist/chunk-Y6XBNZGR.js.map +1 -0
- package/dist/client/index.cjs +112 -2
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +118 -6
- package/dist/client/index.d.ts +118 -6
- package/dist/client/index.js +13 -1
- package/dist/connections/index.cjs +1787 -0
- package/dist/connections/index.cjs.map +1 -0
- package/dist/connections/index.d.cts +78 -0
- package/dist/connections/index.d.ts +78 -0
- package/dist/connections/index.js +754 -0
- package/dist/connections/index.js.map +1 -0
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +2 -2
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +1 -1
- package/dist/index.cjs +7 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/opentofu/index.cjs +138 -0
- package/dist/opentofu/index.cjs.map +1 -0
- package/dist/opentofu/index.d.cts +131 -0
- package/dist/opentofu/index.d.ts +131 -0
- package/dist/opentofu/index.js +109 -0
- package/dist/opentofu/index.js.map +1 -0
- package/dist/pipeline/index.cjs +7 -0
- package/dist/pipeline/index.cjs.map +1 -1
- package/dist/pipeline/index.d.cts +9 -2
- package/dist/pipeline/index.d.ts +9 -2
- package/dist/pipeline/index.js +3 -1
- package/dist/pipeline-beeT8dje.d.cts +408 -0
- package/dist/pipeline-beeT8dje.d.ts +408 -0
- package/dist/ui/index.cjs +136 -0
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +109 -1
- package/dist/ui/index.d.ts +109 -1
- package/dist/ui/index.js +35 -677
- package/dist/ui/index.js.map +1 -1
- package/dist/{use-app-context-OQlcmq7t.d.cts → use-app-context-Byv_fam0.d.cts} +206 -4
- package/dist/{use-app-context-OQlcmq7t.d.ts → use-app-context-Byv_fam0.d.ts} +206 -4
- package/opentofu/README.md +114 -0
- package/opentofu/modules/aws/main.tf +1008 -0
- package/opentofu/modules/aws/outputs.tf +90 -0
- package/opentofu/modules/aws/variables.tf +437 -0
- package/opentofu/modules/aws/versions.tf +23 -0
- package/opentofu/modules/azure/main.tf +920 -0
- package/opentofu/modules/azure/outputs.tf +72 -0
- package/opentofu/modules/azure/variables.tf +498 -0
- package/opentofu/modules/azure/versions.tf +27 -0
- package/opentofu/modules/gcp/main.tf +645 -0
- package/opentofu/modules/gcp/outputs.tf +73 -0
- package/opentofu/modules/gcp/variables.tf +428 -0
- package/opentofu/modules/gcp/versions.tf +27 -0
- package/opentofu/modules/hetzner/main.tf +432 -0
- package/opentofu/modules/hetzner/outputs.tf +72 -0
- package/opentofu/modules/hetzner/variables.tf +423 -0
- package/opentofu/modules/hetzner/versions.tf +26 -0
- package/package.json +33 -1
- package/dist/chunk-EOOEHZGC.js.map +0 -1
- package/dist/chunk-VWFTOFTI.js.map +0 -1
- package/dist/pipeline-A-gSsPRR.d.cts +0 -189
- package/dist/pipeline-A-gSsPRR.d.ts +0 -189
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# GCP environment module — outputs.
|
|
3
|
+
#
|
|
4
|
+
# `resource_refs` is THE contract with the status-back path: a map of
|
|
5
|
+
# plan_key -> external cloud ref (GCP self-link / id). The CI apply reads this
|
|
6
|
+
# (via `tofu output -json`) and/or the parsed apply stream and emits
|
|
7
|
+
# `resource.status {planKey, status, externalRef}` per key, which the app's
|
|
8
|
+
# onEvent hook maps onto the matching BYOL-resource row. Output NAMES are
|
|
9
|
+
# identical to sdk/opentofu/modules/aws.
|
|
10
|
+
# =============================================================================
|
|
11
|
+
|
|
12
|
+
locals {
|
|
13
|
+
# Foundation-tier refs, included only when the tier is in the plan. Same
|
|
14
|
+
# plan_key contract + `foundation/<x>` keys as AWS; values are GCP self-links /
|
|
15
|
+
# ids (GCP LB/IP for the load-balancer + hec refs, in lieu of an ARN/DNS name).
|
|
16
|
+
infra_refs = merge(
|
|
17
|
+
{ "foundation/network" = local.network_self_link },
|
|
18
|
+
local.has_storage ? { "foundation/storage" = google_storage_bucket.objstore[0].url } : {},
|
|
19
|
+
local.has_secrets ? { "foundation/secrets" = google_secret_manager_secret.env[0].id } : {},
|
|
20
|
+
local.has_license_file ? { "foundation/license-file" = google_secret_manager_secret.license[0].id } : {},
|
|
21
|
+
(local.dns_managed && local.has_tls && var.dns_domain != "") ? { "foundation/tls" = google_compute_managed_ssl_certificate.env[0].self_link } : {},
|
|
22
|
+
local.has_lb ? { "foundation/load-balancer" = google_compute_global_address.lb[0].self_link } : {},
|
|
23
|
+
local.dns_managed && local.has_dns && local.has_lb ? { "foundation/dns" = google_dns_record_set.env[0].name } : {},
|
|
24
|
+
# HEC is realized as an endpoint on the LB (external) or on the standalone
|
|
25
|
+
# instance; it maps to the LB's public IP when present. When there is no LB it
|
|
26
|
+
# is a post-config step (reported via deployment.step, not a discrete resource).
|
|
27
|
+
local.has_hec && local.has_lb ? { "ingest/hec" = google_compute_global_address.lb[0].address } : {},
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
output "resource_refs" {
|
|
32
|
+
description = "Map of plan_key -> external cloud reference (instance self-link / secret id / IP). Drives per-resource status-back."
|
|
33
|
+
value = merge(
|
|
34
|
+
{ for k, inst in google_compute_instance.node : k => inst.self_link },
|
|
35
|
+
local.infra_refs,
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
output "subnet_id" {
|
|
40
|
+
description = "Primary compute subnetwork self-link."
|
|
41
|
+
value = local.compute_subnet_ids[0]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
output "security_group_id" {
|
|
45
|
+
description = "The stack network tag — GCP's SG-to-SG identifier (applied to every instance and referenced by the firewall rules)."
|
|
46
|
+
value = local.stack_tag
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
output "network_id" {
|
|
50
|
+
description = "Self-link of the network this stack runs in — the created network for dedicated, the looked-up network for shared/existing."
|
|
51
|
+
value = local.network_self_link
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
output "instance_ids" {
|
|
55
|
+
description = "plan_key -> Compute Engine instance self-link for compute nodes."
|
|
56
|
+
value = { for k, inst in google_compute_instance.node : k => inst.self_link }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
output "instance_private_ips" {
|
|
60
|
+
description = "plan_key -> private IP for compute nodes."
|
|
61
|
+
value = { for k, inst in google_compute_instance.node : k => inst.network_interface[0].network_ip }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
output "node_fqdns" {
|
|
65
|
+
description = <<-EOT
|
|
66
|
+
plan_key -> function FQDN (e.g. idx1.<dns_domain>, sh1.<dns_domain>) for each
|
|
67
|
+
compute node. The bring-up layer uses these to build its inventory
|
|
68
|
+
(cluster/SHC peer resolution). Populated whenever dns_domain is set; the
|
|
69
|
+
matching A records are only created when a private zone is present
|
|
70
|
+
(create_private_zone or private_zone_id). Empty when dns_domain is unset.
|
|
71
|
+
EOT
|
|
72
|
+
value = local.node_fqdns
|
|
73
|
+
}
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# GCP environment module — input variables.
|
|
3
|
+
#
|
|
4
|
+
# One BYOL environment (a "stack") = one dedicated /24 subnetwork carved from the
|
|
5
|
+
# shared Veltrix Network (VPC), plus one Compute Engine instance per plan item and
|
|
6
|
+
# the storage / secrets / TLS / LB / DNS a tool tier needs. The shared network is
|
|
7
|
+
# Veltrix-owned and is looked up (data source), NEVER created here.
|
|
8
|
+
#
|
|
9
|
+
# This is a faithful translation of sdk/opentofu/modules/aws: it honors the SAME
|
|
10
|
+
# variable + output contract. Spec-derived variables (foundation_kinds,
|
|
11
|
+
# compute_kinds, security_rules, load_balancer, dns_prefixes, waf_enabled,
|
|
12
|
+
# alb_auth) are byte-identical to AWS — they come from the app's InfraSpec via
|
|
13
|
+
# renderInfraVars and MUST match across clouds. Deployment variables keep the same
|
|
14
|
+
# NAMES as AWS; GCP specifics (project/zone/image/machine-type) are added.
|
|
15
|
+
#
|
|
16
|
+
# The `plan` list is the SAME topology the app persists as its BYOL-resource
|
|
17
|
+
# rows. Keying compute by `plan_key` is the contract that lets the CI apply report
|
|
18
|
+
# `resource.status` back per row (see outputs.tf + ci/emit-status.mjs).
|
|
19
|
+
# =============================================================================
|
|
20
|
+
|
|
21
|
+
# --- Identity / naming / tenancy ------------------------------------------
|
|
22
|
+
|
|
23
|
+
variable "app_id" {
|
|
24
|
+
description = "Owning app id, e.g. my-app. Used for naming + state key."
|
|
25
|
+
type = string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
variable "customer_id" {
|
|
29
|
+
description = "Tenant (customer) id. Used for naming + label veltrix_customer."
|
|
30
|
+
type = string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
variable "infrastructure_id" {
|
|
34
|
+
description = "BYOL infrastructure (environment/stack) id. Unique per stack."
|
|
35
|
+
type = string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
variable "provider_code" {
|
|
39
|
+
description = "Cloud provider code (informational; this module is GCP). One of aws|azure|gcp|hetzner."
|
|
40
|
+
type = string
|
|
41
|
+
default = "gcp"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# --- GCP placement --------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
variable "project" {
|
|
47
|
+
description = "GCP project id to deploy into. For BYOC this is the customer's project; for hosted it is the Veltrix project."
|
|
48
|
+
type = string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
variable "region" {
|
|
52
|
+
description = "GCP region for regional resources (subnetwork, router/NAT, bucket), e.g. us-central1."
|
|
53
|
+
type = string
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
variable "zone" {
|
|
57
|
+
description = "GCP zone for zonal compute (instances + the LB instance group), e.g. us-central1-a. Must be within var.region."
|
|
58
|
+
type = string
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# --- Network (mode: hosted-shared vs BYOC dedicated/existing) --------------
|
|
62
|
+
# network_mode is a DEPLOYMENT-TARGET var set by the worker per environment — it
|
|
63
|
+
# is NOT part of the app's InfraSpec (which describes only the tool). One app
|
|
64
|
+
# spec deploys hosted OR into a customer's own account, any cloud.
|
|
65
|
+
|
|
66
|
+
variable "network_mode" {
|
|
67
|
+
description = <<-EOT
|
|
68
|
+
How the environment's network is sourced:
|
|
69
|
+
shared — Veltrix-hosted: data-source the shared network (network_ref) and
|
|
70
|
+
create the IPAM-allocated per-stack subnetwork (subnet_cidr).
|
|
71
|
+
dedicated — BYOC: CREATE a fresh custom-mode network (auto_create_subnetworks
|
|
72
|
+
= false) + a subnetwork (vpc_cidr) + Cloud Router + Cloud NAT for
|
|
73
|
+
private egress, isolated per env.
|
|
74
|
+
existing — BYOC: data-source a customer-designated network (network_ref) and
|
|
75
|
+
create a subnetwork inside it (subnet_cidr).
|
|
76
|
+
EOT
|
|
77
|
+
type = string
|
|
78
|
+
default = "shared"
|
|
79
|
+
|
|
80
|
+
validation {
|
|
81
|
+
condition = contains(["shared", "dedicated", "existing"], var.network_mode)
|
|
82
|
+
error_message = "network_mode must be one of: shared, dedicated, existing."
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
variable "network_ref" {
|
|
87
|
+
description = <<-EOT
|
|
88
|
+
Reference to the network to deploy into, for network_mode = shared|existing.
|
|
89
|
+
GCP networks are identified by NAME (there is no tag-based lookup), so this is
|
|
90
|
+
the network name (e.g. `vpc-veltrix-usc1-shared` or a customer network name).
|
|
91
|
+
Ignored for network_mode = dedicated (the network is created).
|
|
92
|
+
EOT
|
|
93
|
+
type = string
|
|
94
|
+
default = ""
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
variable "network_lookup_by" {
|
|
98
|
+
description = <<-EOT
|
|
99
|
+
How to resolve network_ref. Kept for cross-cloud contract parity with AWS;
|
|
100
|
+
GCP has no tag-based network lookup, so both `tag` and `id` resolve
|
|
101
|
+
network_ref as the network NAME. Used for shared|existing.
|
|
102
|
+
EOT
|
|
103
|
+
type = string
|
|
104
|
+
default = "tag"
|
|
105
|
+
|
|
106
|
+
validation {
|
|
107
|
+
condition = contains(["tag", "id"], var.network_lookup_by)
|
|
108
|
+
error_message = "network_lookup_by must be either \"tag\" or \"id\"."
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
variable "subnet_cidr" {
|
|
113
|
+
description = <<-EOT
|
|
114
|
+
Per-stack subnetwork CIDR for network_mode = shared|existing (IPAM-allocated
|
|
115
|
+
for hosted). Ignored for dedicated (the subnetwork range is vpc_cidr). Empty is
|
|
116
|
+
allowed only when dedicated.
|
|
117
|
+
EOT
|
|
118
|
+
type = string
|
|
119
|
+
default = ""
|
|
120
|
+
|
|
121
|
+
validation {
|
|
122
|
+
condition = var.subnet_cidr == "" || can(cidrhost(var.subnet_cidr, 0))
|
|
123
|
+
error_message = "subnet_cidr must be empty or a valid IPv4 CIDR, e.g. 10.20.16.0/24."
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
variable "vpc_cidr" {
|
|
128
|
+
description = <<-EOT
|
|
129
|
+
CIDR for the subnetwork of the network CREATED when network_mode = dedicated
|
|
130
|
+
(BYOC). GCP custom-mode networks have no parent range; this whole CIDR becomes
|
|
131
|
+
the stack's single regional subnetwork. Ignored otherwise.
|
|
132
|
+
EOT
|
|
133
|
+
type = string
|
|
134
|
+
default = "10.60.0.0/16"
|
|
135
|
+
|
|
136
|
+
validation {
|
|
137
|
+
condition = can(cidrhost(var.vpc_cidr, 0))
|
|
138
|
+
error_message = "vpc_cidr must be a valid IPv4 CIDR, e.g. 10.60.0.0/16."
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
variable "admin_cidr" {
|
|
143
|
+
description = "CIDR allowed to reach management ports + the web UI when there is no LB. Typically the Veltrix control-plane / customer bastion range."
|
|
144
|
+
type = string
|
|
145
|
+
default = "10.0.0.0/8"
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
# --- The plan (topology) --------------------------------------------------
|
|
149
|
+
|
|
150
|
+
variable "plan" {
|
|
151
|
+
description = <<-EOT
|
|
152
|
+
Ordered resource plan from the app topology. One object per resource the
|
|
153
|
+
environment needs. `plan_key` is the stable key that maps 1:1 to a
|
|
154
|
+
BYOL-resource row; the CI apply emits resource.status per plan_key.
|
|
155
|
+
EOT
|
|
156
|
+
type = list(object({
|
|
157
|
+
plan_key = string
|
|
158
|
+
tier = string # foundation | control-plane | data | search | ingest
|
|
159
|
+
kind = string # network | storage | indexer | search-head | management-node | ...
|
|
160
|
+
name = optional(string, "")
|
|
161
|
+
role = optional(string, "")
|
|
162
|
+
region = optional(string, null)
|
|
163
|
+
# Multi-AZ placement: the GCP zone (e.g. us-central1-b) this node is pinned to
|
|
164
|
+
# (null = var.zone). GCP subnets are regional, so only the instance zone varies.
|
|
165
|
+
zone = optional(string, null)
|
|
166
|
+
roles = optional(list(string), [])
|
|
167
|
+
}))
|
|
168
|
+
|
|
169
|
+
validation {
|
|
170
|
+
condition = length(var.plan) > 0
|
|
171
|
+
error_message = "plan must contain at least one item."
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# --- Per-tier compute sizing ----------------------------------------------
|
|
176
|
+
|
|
177
|
+
variable "machine_types" {
|
|
178
|
+
description = <<-EOT
|
|
179
|
+
Per-tier Compute Engine machine type override, keyed by tier
|
|
180
|
+
(foundation|control-plane|data|search|ingest). Missing tiers fall back to
|
|
181
|
+
default_machine_type. Kind-level overrides go in machine_types_by_kind.
|
|
182
|
+
EOT
|
|
183
|
+
type = map(string)
|
|
184
|
+
default = {}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
variable "machine_types_by_kind" {
|
|
188
|
+
description = "Per-kind machine type override (wins over machine_types). e.g. { indexer = \"n2-standard-8\" }."
|
|
189
|
+
type = map(string)
|
|
190
|
+
default = {}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
variable "default_machine_type" {
|
|
194
|
+
description = "Fallback machine type for any compute plan item with no tier/kind override."
|
|
195
|
+
type = string
|
|
196
|
+
default = "e2-medium"
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
variable "boot_disk_gb" {
|
|
200
|
+
description = "Boot disk size (GiB) for each compute instance."
|
|
201
|
+
type = number
|
|
202
|
+
default = 100
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
# --- Machine image / access -----------------------------------------------
|
|
206
|
+
|
|
207
|
+
variable "image" {
|
|
208
|
+
description = <<-EOT
|
|
209
|
+
Boot image for compute instances — an image self-link, `project/family`, or
|
|
210
|
+
image name. If empty, the module falls back to the latest Debian 12 image
|
|
211
|
+
(scaffold only) — production MUST supply a hardened, tool-preinstalled image.
|
|
212
|
+
EOT
|
|
213
|
+
type = string
|
|
214
|
+
default = ""
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
variable "ssh_public_key" {
|
|
218
|
+
description = <<-EOT
|
|
219
|
+
Optional SSH public key for break-glass access, in GCP metadata format
|
|
220
|
+
`user:ssh-rsa AAAA... comment`. Empty = none (published to the instance's
|
|
221
|
+
ssh-keys metadata when set).
|
|
222
|
+
EOT
|
|
223
|
+
type = string
|
|
224
|
+
default = ""
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# --- Foundation options ----------------------------------------------------
|
|
228
|
+
|
|
229
|
+
variable "dns_domain" {
|
|
230
|
+
description = "Base domain for the environment (e.g. <cust>-<env>.veltrixsecops.com, or a customer domain in BYOC). Required only if the plan includes a dns item."
|
|
231
|
+
type = string
|
|
232
|
+
default = ""
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
variable "dns_mode" {
|
|
236
|
+
description = <<-EOT
|
|
237
|
+
How the PUBLIC (analyst-facing) DNS name + TLS cert are handled. The PRIVATE
|
|
238
|
+
intra-cluster zone is always created in the deploy account's network regardless.
|
|
239
|
+
managed — the module creates the public A record (→ the LB's global IP)
|
|
240
|
+
in public_dns_managed_zone + a Google-managed SSL cert (works
|
|
241
|
+
for hosted on the Veltrix zone AND a BYOC customer-owned zone —
|
|
242
|
+
both live in the deploy account).
|
|
243
|
+
delegated — BYOC cross-account: the WORKER writes the public record into
|
|
244
|
+
Veltrix's zone; the module makes NO public record/cert and uses
|
|
245
|
+
certificate_arn (a self-provided SSL cert self-link) on the
|
|
246
|
+
HTTPS proxy instead.
|
|
247
|
+
private-only — no public DNS; reached via the customer network (ZTNA/VPN).
|
|
248
|
+
EOT
|
|
249
|
+
type = string
|
|
250
|
+
default = "managed"
|
|
251
|
+
|
|
252
|
+
validation {
|
|
253
|
+
condition = contains(["managed", "delegated", "private-only"], var.dns_mode)
|
|
254
|
+
error_message = "dns_mode must be one of: managed, delegated, private-only."
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
variable "public_dns_managed_zone" {
|
|
259
|
+
description = "Cloud DNS managed zone NAME for the PUBLIC record, when dns_mode = managed. The zone must be in the DEPLOY project (Veltrix's for hosted, the customer's for BYOC customer-owned)."
|
|
260
|
+
type = string
|
|
261
|
+
default = ""
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
variable "certificate_arn" {
|
|
265
|
+
description = <<-EOT
|
|
266
|
+
Pre-provisioned SSL certificate reference for the HTTPS proxy, used when
|
|
267
|
+
dns_mode = delegated (the worker provisions + validates the cert). On GCP this
|
|
268
|
+
is a `google_compute_ssl_certificate` / managed-cert SELF-LINK (the name is
|
|
269
|
+
kept as `certificate_arn` for cross-cloud contract uniformity). Empty otherwise
|
|
270
|
+
(the module issues its own managed cert in dns_mode = managed).
|
|
271
|
+
EOT
|
|
272
|
+
type = string
|
|
273
|
+
default = ""
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
variable "alb_auth" {
|
|
277
|
+
description = <<-EOT
|
|
278
|
+
Optional OIDC/MFA enforced at the front door, in front of the tool's web UI.
|
|
279
|
+
The field shape is identical to AWS (Cognito) for cross-cloud contract parity.
|
|
280
|
+
On GCP the analog is Identity-Aware Proxy (IAP); it is a larger lift and is
|
|
281
|
+
DOCUMENTED AS A FOLLOW-ON — this module NO-OPS the variable (no IAP resources
|
|
282
|
+
are created). When enabled, all three user_pool_* fields are required so the
|
|
283
|
+
tfvars stay valid across clouds.
|
|
284
|
+
EOT
|
|
285
|
+
type = object({
|
|
286
|
+
enabled = optional(bool, false)
|
|
287
|
+
user_pool_arn = optional(string, "")
|
|
288
|
+
user_pool_client_id = optional(string, "")
|
|
289
|
+
user_pool_domain = optional(string, "")
|
|
290
|
+
})
|
|
291
|
+
default = {}
|
|
292
|
+
|
|
293
|
+
validation {
|
|
294
|
+
condition = !var.alb_auth.enabled || (
|
|
295
|
+
var.alb_auth.user_pool_arn != "" &&
|
|
296
|
+
var.alb_auth.user_pool_client_id != "" &&
|
|
297
|
+
var.alb_auth.user_pool_domain != ""
|
|
298
|
+
)
|
|
299
|
+
error_message = "When alb_auth.enabled is true, user_pool_arn, user_pool_client_id and user_pool_domain are all required."
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
variable "create_private_zone" {
|
|
304
|
+
description = <<-EOT
|
|
305
|
+
Create a Cloud DNS PRIVATE managed zone for dns_domain, associated with the
|
|
306
|
+
stack network, to hold per-node function FQDNs (idx1.<domain>, sh1.<domain>, ...).
|
|
307
|
+
Ignored when dns_domain is empty. Mutually complementary with private_zone_id:
|
|
308
|
+
set private_zone_id instead to reuse an existing zone.
|
|
309
|
+
EOT
|
|
310
|
+
type = bool
|
|
311
|
+
default = false
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
variable "private_zone_id" {
|
|
315
|
+
description = <<-EOT
|
|
316
|
+
Existing Cloud DNS private managed zone NAME to publish per-node function FQDNs
|
|
317
|
+
into. Takes precedence over create_private_zone when non-empty. Leave empty
|
|
318
|
+
(and set create_private_zone) to have the module create the zone. Named
|
|
319
|
+
private_zone_id for cross-cloud parity; on GCP it is the managed-zone name.
|
|
320
|
+
EOT
|
|
321
|
+
type = string
|
|
322
|
+
default = ""
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
# --- Declarative infra spec (rendered from the app's InfraSpec) -----------
|
|
326
|
+
# These are what make the module tool-agnostic. The app declares its ports /
|
|
327
|
+
# front-door / DNS as DATA (sdk/src/opentofu/spec.ts InfraSpec) and the SDK
|
|
328
|
+
# renders them here. NOTHING below is app-specific. This block is
|
|
329
|
+
# byte-identical to sdk/opentofu/modules/aws/variables.tf.
|
|
330
|
+
|
|
331
|
+
variable "foundation_kinds" {
|
|
332
|
+
description = <<-EOT
|
|
333
|
+
Plan `kind`s the module realizes as shared FOUNDATION infra (not compute).
|
|
334
|
+
Any plan item whose kind is NOT in this set (and not named by compute_kinds)
|
|
335
|
+
is a compute node. Kept in sync with FOUNDATION_KINDS in spec.ts.
|
|
336
|
+
EOT
|
|
337
|
+
type = list(string)
|
|
338
|
+
default = [
|
|
339
|
+
"network", "storage", "secrets", "tls",
|
|
340
|
+
"load-balancer", "dns", "license-file", "hec",
|
|
341
|
+
]
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
variable "compute_kinds" {
|
|
345
|
+
description = <<-EOT
|
|
346
|
+
Optional explicit allow-list of compute kinds. When non-empty, ONLY these
|
|
347
|
+
kinds become compute nodes. When empty (default), compute = any plan item
|
|
348
|
+
whose kind is not in foundation_kinds (so an app's roles are compute
|
|
349
|
+
automatically).
|
|
350
|
+
EOT
|
|
351
|
+
type = list(string)
|
|
352
|
+
default = []
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
variable "security_rules" {
|
|
356
|
+
description = <<-EOT
|
|
357
|
+
Firewall ingress rules from the app's InfraSpec. Each rule opens `port` to one
|
|
358
|
+
or more `sources`: "self" (peer nodes — the stack network tag referencing
|
|
359
|
+
itself), "alb" (the front-door LB; on GCP this maps to Google's LB health-check
|
|
360
|
+
+ proxy ranges 130.211.0.0/22 and 35.191.0.0/16; ignored when there is no load
|
|
361
|
+
balancer), or "admin" (var.admin_cidr). Replaces any hardcoded, tool-specific
|
|
362
|
+
port list.
|
|
363
|
+
EOT
|
|
364
|
+
type = list(object({
|
|
365
|
+
port = number
|
|
366
|
+
protocol = optional(string, "tcp")
|
|
367
|
+
sources = list(string)
|
|
368
|
+
description = optional(string, "")
|
|
369
|
+
}))
|
|
370
|
+
default = []
|
|
371
|
+
|
|
372
|
+
validation {
|
|
373
|
+
condition = alltrue([
|
|
374
|
+
for r in var.security_rules : alltrue([
|
|
375
|
+
for s in r.sources : contains(["self", "alb", "admin"], s)
|
|
376
|
+
])
|
|
377
|
+
])
|
|
378
|
+
error_message = "Every security_rules[*].sources entry must be one of: self, alb, admin."
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
variable "load_balancer" {
|
|
383
|
+
description = <<-EOT
|
|
384
|
+
Front-door LB spec from the app's InfraSpec. Null for headless / forwarder-
|
|
385
|
+
only tools. When set (and the plan carries a load-balancer item), the module
|
|
386
|
+
builds the health check + backend service + HTTPS proxy + forwarding rule from
|
|
387
|
+
this. `target_kinds` are the compute kinds that sit behind the LB.
|
|
388
|
+
EOT
|
|
389
|
+
type = object({
|
|
390
|
+
target_port = number
|
|
391
|
+
target_protocol = optional(string, "HTTP")
|
|
392
|
+
health_check_path = string
|
|
393
|
+
health_check_matcher = optional(string, "200-399")
|
|
394
|
+
health_check_protocol = optional(string, "")
|
|
395
|
+
target_kinds = list(string)
|
|
396
|
+
listener_port = optional(number, 443)
|
|
397
|
+
})
|
|
398
|
+
default = null
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
variable "dns_prefixes" {
|
|
402
|
+
description = <<-EOT
|
|
403
|
+
kind -> DNS label prefix for per-node function FQDNs (e.g.
|
|
404
|
+
{ indexer = "idx", search-head = "sh", cluster-manager = "mgmt" }). A compute
|
|
405
|
+
kind absent from the map falls back to the kind string itself.
|
|
406
|
+
EOT
|
|
407
|
+
type = map(string)
|
|
408
|
+
default = {}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
variable "waf_enabled" {
|
|
412
|
+
description = "Attach a Cloud Armor security policy (OWASP preconfigured rules + IP rate limit) to the LB backend service. Ignored when there is no load balancer."
|
|
413
|
+
type = bool
|
|
414
|
+
default = true
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
# --- Labels (threaded onto every labelable resource) ----------------------
|
|
418
|
+
|
|
419
|
+
variable "tags" {
|
|
420
|
+
description = <<-EOT
|
|
421
|
+
Canonical Veltrix tag set. Named `tags` for cross-cloud contract parity, but on
|
|
422
|
+
GCP these are SANITIZED into labels (lowercase [a-z0-9_-], key starts with a
|
|
423
|
+
letter, <=63 chars) before being applied — the raw keys (Veltrix:Customer,
|
|
424
|
+
CostCenter, ...) are invalid GCP label keys. Must include Veltrix:ManagedBy
|
|
425
|
+
(the OIDC/WIF apply identity is scoped by veltrix_managedby = veltrix).
|
|
426
|
+
EOT
|
|
427
|
+
type = map(string)
|
|
428
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# GCP environment module — provider/version pinning (OpenTofu-compatible HCL).
|
|
3
|
+
#
|
|
4
|
+
# OpenTofu resolves providers from registry.opentofu.org by default but the
|
|
5
|
+
# canonical `hashicorp/google` source address is mirrored there, so this block is
|
|
6
|
+
# byte-for-byte compatible with both `tofu` and `terraform`. We pin OpenTofu
|
|
7
|
+
# >= 1.6.0 (the first stable OpenTofu line) rather than a Terraform version.
|
|
8
|
+
#
|
|
9
|
+
# Provider: google ~> 6.0 (current major line). Every resource used here is in
|
|
10
|
+
# the GA `google` provider (no google-beta needed). random is used for the
|
|
11
|
+
# object-storage bucket suffix, exactly as in the AWS reference module.
|
|
12
|
+
# =============================================================================
|
|
13
|
+
|
|
14
|
+
terraform {
|
|
15
|
+
required_version = ">= 1.6.0"
|
|
16
|
+
|
|
17
|
+
required_providers {
|
|
18
|
+
google = {
|
|
19
|
+
source = "hashicorp/google"
|
|
20
|
+
version = "~> 6.0"
|
|
21
|
+
}
|
|
22
|
+
random = {
|
|
23
|
+
source = "hashicorp/random"
|
|
24
|
+
version = "~> 3.6"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|