@veltrixsecops/app-sdk 3.1.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-6KUTYJW5.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 +127 -6
- package/dist/client/index.d.ts +127 -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 +7 -0
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +24 -3
- package/dist/hooks/index.d.ts +24 -3
- package/dist/hooks/index.js +7 -1
- package/dist/hooks/index.js.map +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 +248 -0
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +238 -1
- package/dist/ui/index.d.ts +238 -1
- package/dist/ui/index.js +39 -573
- package/dist/ui/index.js.map +1 -1
- package/dist/{use-app-context-SEU1tyZd.d.cts → use-app-context-Byv_fam0.d.cts} +241 -4
- package/dist/{use-app-context-SEU1tyZd.d.ts → use-app-context-Byv_fam0.d.ts} +241 -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-6KUTYJW5.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,90 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# AWS environment module — outputs.
|
|
3
|
+
#
|
|
4
|
+
# `resource_refs` is THE contract with the status-back path: a map of
|
|
5
|
+
# plan_key -> external cloud ref. The CI apply reads this (via `tofu output
|
|
6
|
+
# -json`) and/or the parsed apply stream and emits `resource.status
|
|
7
|
+
# {planKey, status, externalRef}` per key, which the app's onEvent hook maps
|
|
8
|
+
# onto the matching BYOL-resource row.
|
|
9
|
+
# =============================================================================
|
|
10
|
+
|
|
11
|
+
locals {
|
|
12
|
+
# Foundation-tier refs, included only when the tier is in the plan.
|
|
13
|
+
infra_refs = merge(
|
|
14
|
+
{ "foundation/network" = local.network_id },
|
|
15
|
+
local.has_storage ? { "foundation/storage" = aws_s3_bucket.objstore[0].arn } : {},
|
|
16
|
+
local.has_secrets ? { "foundation/secrets" = aws_secretsmanager_secret.env[0].arn } : {},
|
|
17
|
+
local.has_license_file ? { "foundation/license-file" = aws_secretsmanager_secret.license[0].arn } : {},
|
|
18
|
+
(local.dns_managed && local.has_tls && var.dns_domain != "") ? { "foundation/tls" = aws_acm_certificate.env[0].arn } : {},
|
|
19
|
+
local.has_lb ? { "foundation/load-balancer" = aws_lb.env[0].arn } : {},
|
|
20
|
+
local.dns_managed && local.has_dns && local.has_lb ? { "foundation/dns" = aws_route53_record.env[0].fqdn } : {},
|
|
21
|
+
# HEC is realized as an endpoint on the LB (external) or on the standalone
|
|
22
|
+
# instance; it maps to the LB ref when present. When there is no LB it is a
|
|
23
|
+
# post-config step (reported via deployment.step, not a discrete resource).
|
|
24
|
+
local.has_hec && local.has_lb ? { "ingest/hec" = aws_lb.env[0].dns_name } : {},
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
output "resource_refs" {
|
|
29
|
+
description = "Map of plan_key -> external cloud reference (instance id / arn / fqdn). Drives per-resource status-back."
|
|
30
|
+
value = merge(
|
|
31
|
+
{ for k, inst in aws_instance.node : k => inst.id },
|
|
32
|
+
local.infra_refs,
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
output "subnet_id" {
|
|
37
|
+
description = "Primary compute subnet id (the allocated subnet for shared/existing; the first private subnet for dedicated)."
|
|
38
|
+
value = local.compute_subnet_ids[0]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
output "security_group_id" {
|
|
42
|
+
description = "Id of the compute-node security group."
|
|
43
|
+
value = aws_security_group.splunk.id
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
output "network_id" {
|
|
47
|
+
description = "Id of the network (VPC) this stack runs in — the created VPC for dedicated, the looked-up VPC for shared/existing."
|
|
48
|
+
value = local.network_id
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
output "vpc_id" {
|
|
52
|
+
description = "Alias of network_id (AWS VPC id) for back-compat."
|
|
53
|
+
value = local.network_id
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
output "instance_ids" {
|
|
57
|
+
description = "plan_key -> EC2 instance id for compute nodes."
|
|
58
|
+
value = { for k, inst in aws_instance.node : k => inst.id }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
output "instance_private_ips" {
|
|
62
|
+
description = "plan_key -> private IP for compute nodes."
|
|
63
|
+
value = { for k, inst in aws_instance.node : k => inst.private_ip }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
output "node_fqdns" {
|
|
67
|
+
description = <<-EOT
|
|
68
|
+
plan_key -> function FQDN (e.g. idx1.<dns_domain>, sh1.<dns_domain>) for each
|
|
69
|
+
compute node. The bring-up layer uses these to build its cluster inventory
|
|
70
|
+
(cluster peer resolution). Populated whenever dns_domain is set; the
|
|
71
|
+
matching A records are only created when a private zone is present
|
|
72
|
+
(create_private_zone or private_zone_id). Empty when dns_domain is unset.
|
|
73
|
+
EOT
|
|
74
|
+
value = local.node_fqdns
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
output "vpc_cidr" {
|
|
78
|
+
description = "CIDR of the VPC this stack runs in — the created VPC (dedicated) or the allocated subnet block otherwise. Used by the root to wire cross-region peering routes + peer security-group rules."
|
|
79
|
+
value = local.is_dedicated ? var.vpc_cidr : var.subnet_cidr
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
output "private_route_table_id" {
|
|
83
|
+
description = "Id of the private (compute) route table for dedicated fabrics; empty otherwise. The root adds cross-region VPC-peering routes to it for multi-region satellites."
|
|
84
|
+
value = local.is_dedicated ? aws_route_table.private[0].id : ""
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
output "private_zone_id" {
|
|
88
|
+
description = "Id of the private DNS zone (when dns_domain is set + a zone is created/supplied); empty otherwise. The root associates it with multi-region satellite VPCs so their nodes resolve the main region's function FQDNs."
|
|
89
|
+
value = local.want_private_dns ? local.private_zone_id : ""
|
|
90
|
+
}
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# AWS environment module — input variables.
|
|
3
|
+
#
|
|
4
|
+
# One BYOL environment (a "stack") = one dedicated /24 subnet carved from the
|
|
5
|
+
# shared Veltrix Network (VPC), plus one compute resource per plan item and the
|
|
6
|
+
# storage / secrets / TLS / LB / DNS an app tier needs. The shared VPC is
|
|
7
|
+
# Veltrix-owned and is looked up (data source), NEVER created here.
|
|
8
|
+
#
|
|
9
|
+
# The `plan` list is the SAME topology the app persists as its BYOL-resource
|
|
10
|
+
# rows (see the app's byolTopology). Keying compute by
|
|
11
|
+
# `plan_key` is the contract that lets the CI apply report `resource.status`
|
|
12
|
+
# back per row (see outputs.tf + ci/emit-status.mjs).
|
|
13
|
+
# =============================================================================
|
|
14
|
+
|
|
15
|
+
# --- Identity / naming / tenancy ------------------------------------------
|
|
16
|
+
|
|
17
|
+
variable "app_id" {
|
|
18
|
+
description = "Owning app id, e.g. my-app. Used for naming + state key."
|
|
19
|
+
type = string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
variable "customer_id" {
|
|
23
|
+
description = "Tenant (customer) id. Used for naming + tag Veltrix:Customer."
|
|
24
|
+
type = string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
variable "infrastructure_id" {
|
|
28
|
+
description = "BYOL infrastructure (environment/stack) id. Unique per stack."
|
|
29
|
+
type = string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
variable "provider_code" {
|
|
33
|
+
description = "Cloud provider code (informational; this module is AWS). One of aws|azure|gcp|hetzner."
|
|
34
|
+
type = string
|
|
35
|
+
default = "aws"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
variable "region" {
|
|
39
|
+
description = "AWS region to deploy into, e.g. us-east-1."
|
|
40
|
+
type = string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# --- Network (mode: hosted-shared vs BYOC dedicated/existing) --------------
|
|
44
|
+
# network_mode is a DEPLOYMENT-TARGET var set by the worker per environment — it
|
|
45
|
+
# is NOT part of the app's InfraSpec (which describes only the tool). One app
|
|
46
|
+
# spec deploys hosted OR into a customer's own account, any cloud.
|
|
47
|
+
|
|
48
|
+
variable "network_mode" {
|
|
49
|
+
description = <<-EOT
|
|
50
|
+
How the environment's network is sourced:
|
|
51
|
+
shared — Veltrix-hosted: data-source the shared VPC (network_ref) and
|
|
52
|
+
create the IPAM-allocated per-stack subnet (subnet_cidr).
|
|
53
|
+
dedicated — BYOC: CREATE a fresh VPC (vpc_cidr) + public/private subnets
|
|
54
|
+
across 2 AZs + internet gateway + NAT, isolated per env.
|
|
55
|
+
existing — BYOC: data-source a customer-designated VPC (network_ref) and
|
|
56
|
+
create subnets inside it (subnet_cidr).
|
|
57
|
+
EOT
|
|
58
|
+
type = string
|
|
59
|
+
default = "shared"
|
|
60
|
+
|
|
61
|
+
validation {
|
|
62
|
+
condition = contains(["shared", "dedicated", "existing"], var.network_mode)
|
|
63
|
+
error_message = "network_mode must be one of: shared, dedicated, existing."
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
variable "network_ref" {
|
|
68
|
+
description = <<-EOT
|
|
69
|
+
Reference to the VPC to deploy into, for network_mode = shared|existing.
|
|
70
|
+
Matched against the VPC's `Name` tag by default (a Veltrix-managed name such
|
|
71
|
+
as `vpc-veltrix-use1-shared`, or a customer VPC name); pass a vpc-id and set
|
|
72
|
+
network_lookup_by = "id" to resolve by id. Ignored for network_mode =
|
|
73
|
+
dedicated (the VPC is created).
|
|
74
|
+
EOT
|
|
75
|
+
type = string
|
|
76
|
+
default = ""
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
variable "network_lookup_by" {
|
|
80
|
+
description = "How to resolve network_ref: `tag` (Name tag) or `id` (vpc-xxxx). Used for shared|existing."
|
|
81
|
+
type = string
|
|
82
|
+
default = "tag"
|
|
83
|
+
|
|
84
|
+
validation {
|
|
85
|
+
condition = contains(["tag", "id"], var.network_lookup_by)
|
|
86
|
+
error_message = "network_lookup_by must be either \"tag\" or \"id\"."
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
variable "subnet_cidr" {
|
|
91
|
+
description = <<-EOT
|
|
92
|
+
Per-stack subnet CIDR for network_mode = shared|existing (IPAM-allocated for
|
|
93
|
+
hosted). Ignored for dedicated (subnets are carved from vpc_cidr). Empty is
|
|
94
|
+
allowed only when dedicated.
|
|
95
|
+
EOT
|
|
96
|
+
type = string
|
|
97
|
+
default = ""
|
|
98
|
+
|
|
99
|
+
validation {
|
|
100
|
+
condition = var.subnet_cidr == "" || can(cidrhost(var.subnet_cidr, 0))
|
|
101
|
+
error_message = "subnet_cidr must be empty or a valid IPv4 CIDR, e.g. 10.20.16.0/24."
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
variable "vpc_cidr" {
|
|
106
|
+
description = <<-EOT
|
|
107
|
+
CIDR for the VPC CREATED when network_mode = dedicated (BYOC). Public and
|
|
108
|
+
private /20 subnets are carved from it across 2 AZs. Ignored otherwise.
|
|
109
|
+
EOT
|
|
110
|
+
type = string
|
|
111
|
+
default = "10.60.0.0/16"
|
|
112
|
+
|
|
113
|
+
validation {
|
|
114
|
+
condition = can(cidrhost(var.vpc_cidr, 0))
|
|
115
|
+
error_message = "vpc_cidr must be a valid IPv4 CIDR, e.g. 10.60.0.0/16."
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
variable "admin_cidr" {
|
|
120
|
+
description = "CIDR allowed to reach management ports + the web UI when there is no ALB. Typically the Veltrix control-plane / customer bastion range."
|
|
121
|
+
type = string
|
|
122
|
+
default = "10.0.0.0/8"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
variable "peer_cidrs" {
|
|
126
|
+
description = <<-EOT
|
|
127
|
+
CIDRs of PEERED VPCs whose nodes are part of this cluster (multi-region
|
|
128
|
+
indexer/search-head satellites). Each is allowed on the same intra-cluster
|
|
129
|
+
("self") ports so cross-region replication/management works over VPC peering.
|
|
130
|
+
Empty (the single-region default) adds no rules.
|
|
131
|
+
EOT
|
|
132
|
+
type = list(string)
|
|
133
|
+
default = []
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# --- The plan (topology) --------------------------------------------------
|
|
137
|
+
|
|
138
|
+
variable "plan" {
|
|
139
|
+
description = <<-EOT
|
|
140
|
+
Ordered resource plan from the app topology. One object per resource the
|
|
141
|
+
environment needs. `plan_key` is the stable key that maps 1:1 to a
|
|
142
|
+
BYOL-resource row; the CI apply emits resource.status per plan_key.
|
|
143
|
+
EOT
|
|
144
|
+
type = list(object({
|
|
145
|
+
plan_key = string
|
|
146
|
+
tier = string # foundation | control-plane | data | search | ingest
|
|
147
|
+
kind = string # network | storage | indexer | search-head | management-node | ...
|
|
148
|
+
name = optional(string, "")
|
|
149
|
+
role = optional(string, "")
|
|
150
|
+
region = optional(string, null)
|
|
151
|
+
# Multi-AZ placement: the availability zone this node is pinned to (null = default).
|
|
152
|
+
zone = optional(string, null)
|
|
153
|
+
# Consolidated control-plane roles this instance runs (drives bring-up config).
|
|
154
|
+
roles = optional(list(string), [])
|
|
155
|
+
}))
|
|
156
|
+
|
|
157
|
+
validation {
|
|
158
|
+
condition = length(var.plan) > 0
|
|
159
|
+
error_message = "plan must contain at least one item."
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
# --- Per-tier compute sizing ----------------------------------------------
|
|
164
|
+
|
|
165
|
+
variable "instance_types" {
|
|
166
|
+
description = <<-EOT
|
|
167
|
+
Per-tier EC2 instance type override, keyed by tier
|
|
168
|
+
(foundation|control-plane|data|search|ingest). Missing tiers fall back to
|
|
169
|
+
default_instance_type. Kind-level overrides go in instance_types_by_kind.
|
|
170
|
+
EOT
|
|
171
|
+
type = map(string)
|
|
172
|
+
default = {}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
variable "instance_types_by_kind" {
|
|
176
|
+
description = "Per-kind EC2 instance type override (wins over instance_types). e.g. { indexer = \"m6i.2xlarge\" }."
|
|
177
|
+
type = map(string)
|
|
178
|
+
default = {}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
variable "default_instance_type" {
|
|
182
|
+
description = "Fallback EC2 instance type for any compute plan item with no tier/kind override."
|
|
183
|
+
type = string
|
|
184
|
+
default = "t2.medium"
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
variable "root_volume_gb" {
|
|
188
|
+
description = "Root EBS volume size (GiB) for each compute node."
|
|
189
|
+
type = number
|
|
190
|
+
default = 100
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
# --- Machine image / access -----------------------------------------------
|
|
194
|
+
|
|
195
|
+
variable "ami_id" {
|
|
196
|
+
description = <<-EOT
|
|
197
|
+
AMI for app compute nodes. If empty, the module falls back to the latest
|
|
198
|
+
Amazon Linux 2023 image (scaffold only) — production MUST supply a hardened,
|
|
199
|
+
app-preinstalled (custom) AMI id per region.
|
|
200
|
+
EOT
|
|
201
|
+
type = string
|
|
202
|
+
default = ""
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
variable "key_name" {
|
|
206
|
+
description = "Optional EC2 key pair name for SSH break-glass access. Empty = none."
|
|
207
|
+
type = string
|
|
208
|
+
default = ""
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
# --- Foundation options ----------------------------------------------------
|
|
212
|
+
|
|
213
|
+
variable "dns_domain" {
|
|
214
|
+
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."
|
|
215
|
+
type = string
|
|
216
|
+
default = ""
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
variable "private_dns_domain" {
|
|
220
|
+
description = "Domain for the INTERNAL (intra-cluster) private zone + node FQDNs, e.g. my-app.internal. Kept separate from dns_domain so a dedicated fabric with no public domain gets cm1/idx1/sh1 FQDNs without triggering public DNS/ACM. Empty falls back to dns_domain."
|
|
221
|
+
type = string
|
|
222
|
+
default = ""
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
variable "dns_mode" {
|
|
226
|
+
description = <<-EOT
|
|
227
|
+
How the PUBLIC (analyst-facing) DNS name + TLS cert are handled. The PRIVATE
|
|
228
|
+
intra-cluster zone is always created in the deploy account's VPC regardless.
|
|
229
|
+
managed — the module creates the public A/alias record + ACM cert in
|
|
230
|
+
route53_zone_id (works for hosted on the Veltrix zone AND
|
|
231
|
+
BYOC customer-owned zone — both live in the deploy account).
|
|
232
|
+
delegated — BYOC cross-account: the WORKER writes the public record +
|
|
233
|
+
ACM-validation record into Veltrix's zone; the module makes
|
|
234
|
+
NO public record/cert and uses certificate_arn on the
|
|
235
|
+
listener instead.
|
|
236
|
+
private-only — no public DNS; reached via the customer network (ZTNA/VPN).
|
|
237
|
+
EOT
|
|
238
|
+
type = string
|
|
239
|
+
default = "managed"
|
|
240
|
+
|
|
241
|
+
validation {
|
|
242
|
+
condition = contains(["managed", "delegated", "private-only"], var.dns_mode)
|
|
243
|
+
error_message = "dns_mode must be one of: managed, delegated, private-only."
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
variable "route53_zone_id" {
|
|
248
|
+
description = "Route53 hosted zone id for the PUBLIC record, when dns_mode = managed. The zone must be in the DEPLOY account (Veltrix's for hosted, the customer's for BYOC customer-owned)."
|
|
249
|
+
type = string
|
|
250
|
+
default = ""
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
variable "certificate_arn" {
|
|
254
|
+
description = <<-EOT
|
|
255
|
+
Pre-validated ACM certificate ARN for the HTTPS listener, used when
|
|
256
|
+
dns_mode = delegated (the worker provisions + cross-account-validates the
|
|
257
|
+
cert). Empty otherwise (the module issues its own ACM cert in dns_mode =
|
|
258
|
+
managed).
|
|
259
|
+
EOT
|
|
260
|
+
type = string
|
|
261
|
+
default = ""
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
variable "extra_lb_subnet_ids" {
|
|
265
|
+
description = <<-EOT
|
|
266
|
+
Additional subnet ids (in OTHER AZs) for the ALB, which needs >= 2 subnets
|
|
267
|
+
across >= 2 AZs. Used for network_mode = shared|existing where the per-stack
|
|
268
|
+
subnet is single-AZ. Ignored for dedicated (the module creates multi-AZ
|
|
269
|
+
public subnets itself). Required for a load-balancer in shared|existing.
|
|
270
|
+
EOT
|
|
271
|
+
type = list(string)
|
|
272
|
+
default = []
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
variable "web_ingress_cidr" {
|
|
276
|
+
description = <<-EOT
|
|
277
|
+
CIDR allowed to reach the PUBLIC ALB on 443/80 (the app's web UI). Defaults to
|
|
278
|
+
0.0.0.0/0 because the v1 posture is a public ALB fronted by WAF + optional
|
|
279
|
+
Cognito MFA. Narrow this to an office/VPN range to restrict access.
|
|
280
|
+
EOT
|
|
281
|
+
type = string
|
|
282
|
+
default = "0.0.0.0/0"
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
variable "alb_auth" {
|
|
286
|
+
description = <<-EOT
|
|
287
|
+
Optional OIDC/Cognito MFA enforced at the ALB, in front of the app's web UI. When
|
|
288
|
+
enabled, the HTTPS listener authenticates against the given Cognito user pool
|
|
289
|
+
BEFORE forwarding to the search target group. Leave disabled (default) for a
|
|
290
|
+
v1 public ALB + WAF posture without ALB-level auth. When enabled, all three
|
|
291
|
+
user_pool_* fields are required.
|
|
292
|
+
EOT
|
|
293
|
+
type = object({
|
|
294
|
+
enabled = optional(bool, false)
|
|
295
|
+
user_pool_arn = optional(string, "")
|
|
296
|
+
user_pool_client_id = optional(string, "")
|
|
297
|
+
user_pool_domain = optional(string, "")
|
|
298
|
+
})
|
|
299
|
+
default = {}
|
|
300
|
+
|
|
301
|
+
validation {
|
|
302
|
+
condition = !var.alb_auth.enabled || (
|
|
303
|
+
var.alb_auth.user_pool_arn != "" &&
|
|
304
|
+
var.alb_auth.user_pool_client_id != "" &&
|
|
305
|
+
var.alb_auth.user_pool_domain != ""
|
|
306
|
+
)
|
|
307
|
+
error_message = "When alb_auth.enabled is true, user_pool_arn, user_pool_client_id and user_pool_domain are all required."
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
variable "create_private_zone" {
|
|
312
|
+
description = <<-EOT
|
|
313
|
+
Create a Route53 PRIVATE hosted zone for dns_domain, associated with the
|
|
314
|
+
shared VPC, to hold per-node function FQDNs (idx1.<domain>, sh1.<domain>, ...).
|
|
315
|
+
Ignored when dns_domain is empty. Mutually complementary with
|
|
316
|
+
private_zone_id: set private_zone_id instead to reuse an existing zone.
|
|
317
|
+
EOT
|
|
318
|
+
type = bool
|
|
319
|
+
default = false
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
variable "private_zone_id" {
|
|
323
|
+
description = <<-EOT
|
|
324
|
+
Existing Route53 private hosted zone id to publish per-node function FQDNs
|
|
325
|
+
into. Takes precedence over create_private_zone when non-empty. Leave empty
|
|
326
|
+
(and set create_private_zone) to have the module create the zone.
|
|
327
|
+
EOT
|
|
328
|
+
type = string
|
|
329
|
+
default = ""
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
# --- Declarative infra spec (rendered from the app's InfraSpec) -----------
|
|
333
|
+
# These are what make the module tool-agnostic. The app declares its ports /
|
|
334
|
+
# front-door / DNS as DATA (sdk/src/opentofu/spec.ts InfraSpec) and the SDK
|
|
335
|
+
# renders them here. NOTHING below is app-specific.
|
|
336
|
+
|
|
337
|
+
variable "foundation_kinds" {
|
|
338
|
+
description = <<-EOT
|
|
339
|
+
Plan `kind`s the module realizes as shared FOUNDATION infra (not compute).
|
|
340
|
+
Any plan item whose kind is NOT in this set (and not named by compute_kinds)
|
|
341
|
+
is a compute node. Kept in sync with FOUNDATION_KINDS in spec.ts.
|
|
342
|
+
EOT
|
|
343
|
+
type = list(string)
|
|
344
|
+
default = [
|
|
345
|
+
"network", "storage", "secrets", "tls",
|
|
346
|
+
"load-balancer", "dns", "license-file", "hec",
|
|
347
|
+
]
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
variable "compute_kinds" {
|
|
351
|
+
description = <<-EOT
|
|
352
|
+
Optional explicit allow-list of compute kinds. When non-empty, ONLY these
|
|
353
|
+
kinds become compute nodes. When empty (default), compute = any plan item
|
|
354
|
+
whose kind is not in foundation_kinds (so an app's roles are compute
|
|
355
|
+
automatically).
|
|
356
|
+
EOT
|
|
357
|
+
type = list(string)
|
|
358
|
+
default = []
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
variable "security_rules" {
|
|
362
|
+
description = <<-EOT
|
|
363
|
+
SG ingress rules from the app's InfraSpec. Each rule opens `port` to one or
|
|
364
|
+
more `sources`: "self" (peer nodes — the node SG referencing itself),
|
|
365
|
+
"alb" (the public ALB SG; ignored when there is no load balancer), or
|
|
366
|
+
"admin" (var.admin_cidr). Replaces any hardcoded, tool-specific port list.
|
|
367
|
+
EOT
|
|
368
|
+
type = list(object({
|
|
369
|
+
port = number
|
|
370
|
+
protocol = optional(string, "tcp")
|
|
371
|
+
sources = list(string)
|
|
372
|
+
description = optional(string, "")
|
|
373
|
+
}))
|
|
374
|
+
default = []
|
|
375
|
+
|
|
376
|
+
validation {
|
|
377
|
+
condition = alltrue([
|
|
378
|
+
for r in var.security_rules : alltrue([
|
|
379
|
+
for s in r.sources : contains(["self", "alb", "admin"], s)
|
|
380
|
+
])
|
|
381
|
+
])
|
|
382
|
+
error_message = "Every security_rules[*].sources entry must be one of: self, alb, admin."
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
variable "load_balancer" {
|
|
387
|
+
description = <<-EOT
|
|
388
|
+
Front-door ALB spec from the app's InfraSpec. Null for headless / forwarder-
|
|
389
|
+
only tools. When set (and the plan carries a load-balancer item), the module
|
|
390
|
+
builds the target group + health check + listeners from this. `target_kinds`
|
|
391
|
+
are the compute kinds that sit behind the ALB.
|
|
392
|
+
EOT
|
|
393
|
+
type = object({
|
|
394
|
+
target_port = number
|
|
395
|
+
target_protocol = optional(string, "HTTP")
|
|
396
|
+
health_check_path = string
|
|
397
|
+
health_check_matcher = optional(string, "200-399")
|
|
398
|
+
health_check_protocol = optional(string, "")
|
|
399
|
+
target_kinds = list(string)
|
|
400
|
+
listener_port = optional(number, 443)
|
|
401
|
+
})
|
|
402
|
+
default = null
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
variable "dns_prefixes" {
|
|
406
|
+
description = <<-EOT
|
|
407
|
+
kind -> DNS label prefix for per-node function FQDNs (e.g.
|
|
408
|
+
{ indexer = "idx", search-head = "sh", cluster-manager = "mgmt" }). A compute
|
|
409
|
+
kind absent from the map falls back to the kind string itself.
|
|
410
|
+
EOT
|
|
411
|
+
type = map(string)
|
|
412
|
+
default = {}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
variable "waf_enabled" {
|
|
416
|
+
description = "Attach a WAFv2 web ACL (managed rules + IP rate limit) to the ALB. Ignored when there is no load balancer."
|
|
417
|
+
type = bool
|
|
418
|
+
default = true
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
variable "artifacts_bucket" {
|
|
422
|
+
description = "S3 bucket holding the app's staged install artifacts the bring-up layer installs from. Empty ('') disables the artifacts IAM grant on the node role."
|
|
423
|
+
type = string
|
|
424
|
+
default = ""
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
# --- Tags (threaded onto every resource) ----------------------------------
|
|
428
|
+
|
|
429
|
+
variable "tags" {
|
|
430
|
+
description = <<-EOT
|
|
431
|
+
Canonical Veltrix tag set applied to every resource
|
|
432
|
+
(Veltrix:Customer, Veltrix:Environment, Veltrix:App, Veltrix:ManagedBy,
|
|
433
|
+
CostCenter, Owner, ...). The OIDC apply role is scoped by
|
|
434
|
+
Veltrix:ManagedBy = Veltrix, so this map MUST include it.
|
|
435
|
+
EOT
|
|
436
|
+
type = map(string)
|
|
437
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# AWS environment module — provider/version pinning (OpenTofu-compatible HCL).
|
|
3
|
+
#
|
|
4
|
+
# OpenTofu resolves providers from registry.opentofu.org by default but the
|
|
5
|
+
# canonical `hashicorp/aws` 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
|
+
|
|
10
|
+
terraform {
|
|
11
|
+
required_version = ">= 1.6.0"
|
|
12
|
+
|
|
13
|
+
required_providers {
|
|
14
|
+
aws = {
|
|
15
|
+
source = "hashicorp/aws"
|
|
16
|
+
version = "~> 5.40"
|
|
17
|
+
}
|
|
18
|
+
random = {
|
|
19
|
+
source = "hashicorp/random"
|
|
20
|
+
version = "~> 3.6"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|