@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,72 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# Azure 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. Names are IDENTICAL to the AWS module.
|
|
9
|
+
# =============================================================================
|
|
10
|
+
|
|
11
|
+
locals {
|
|
12
|
+
# Foundation-tier refs, included only when the tier is in the plan. Mirrors the
|
|
13
|
+
# AWS infra_refs map key-for-key (Azure resource ids in place of AWS arns).
|
|
14
|
+
infra_refs = merge(
|
|
15
|
+
{ "foundation/network" = local.network_id },
|
|
16
|
+
local.has_storage ? { "foundation/storage" = azurerm_storage_account.objstore[0].id } : {},
|
|
17
|
+
local.has_secrets ? { "foundation/secrets" = azurerm_key_vault.secrets[0].id } : {},
|
|
18
|
+
local.has_license_file ? { "foundation/license-file" = azurerm_key_vault_secret.license[0].id } : {},
|
|
19
|
+
# TLS: Azure has no in-module cert issuance, so the ref is the caller-supplied
|
|
20
|
+
# Key Vault cert id (var.certificate_arn) when a tls tier is planned.
|
|
21
|
+
(local.has_tls && var.certificate_arn != "") ? { "foundation/tls" = var.certificate_arn } : {},
|
|
22
|
+
local.has_lb_spec ? { "foundation/load-balancer" = azurerm_application_gateway.env[0].id } : {},
|
|
23
|
+
(local.dns_managed && local.has_dns && local.has_lb_spec) ? { "foundation/dns" = azurerm_dns_a_record.env[0].fqdn } : {},
|
|
24
|
+
# HEC maps to the front-door public IP when present. When there is no LB it is
|
|
25
|
+
# a post-config step (reported via deployment.step, not a discrete resource).
|
|
26
|
+
(local.has_hec && local.has_lb_spec) ? { "ingest/hec" = azurerm_public_ip.appgw[0].ip_address } : {},
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
output "resource_refs" {
|
|
31
|
+
description = "Map of plan_key -> external cloud reference (VM id / resource id / fqdn). Drives per-resource status-back."
|
|
32
|
+
value = merge(
|
|
33
|
+
{ for k, vm in azurerm_linux_virtual_machine.node : k => vm.id },
|
|
34
|
+
local.infra_refs,
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
output "subnet_id" {
|
|
39
|
+
description = "Primary compute subnet id (the allocated subnet for shared/existing; the private subnet for dedicated)."
|
|
40
|
+
value = local.compute_subnet_ids[0]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
output "security_group_id" {
|
|
44
|
+
description = "Id of the compute-node network security group (NSG)."
|
|
45
|
+
value = azurerm_network_security_group.node.id
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
output "network_id" {
|
|
49
|
+
description = "Id of the network (VNet) this stack runs in — the created VNet for dedicated, the looked-up VNet for shared/existing."
|
|
50
|
+
value = local.network_id
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
output "instance_ids" {
|
|
54
|
+
description = "plan_key -> VM id for compute nodes."
|
|
55
|
+
value = { for k, vm in azurerm_linux_virtual_machine.node : k => vm.id }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
output "instance_private_ips" {
|
|
59
|
+
description = "plan_key -> private IP for compute nodes."
|
|
60
|
+
value = { for k, nic in azurerm_network_interface.node : k => nic.private_ip_address }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
output "node_fqdns" {
|
|
64
|
+
description = <<-EOT
|
|
65
|
+
plan_key -> function FQDN (e.g. idx1.<dns_domain>, sh1.<dns_domain>) for each
|
|
66
|
+
compute node. The bring-up layer uses these to build its inventory (cluster
|
|
67
|
+
peer resolution). Populated whenever dns_domain is set; the matching A records
|
|
68
|
+
are only created when a private zone is present (create_private_zone or
|
|
69
|
+
private_zone_id). Empty when dns_domain is unset.
|
|
70
|
+
EOT
|
|
71
|
+
value = local.node_fqdns
|
|
72
|
+
}
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# Azure environment module — input variables.
|
|
3
|
+
#
|
|
4
|
+
# One BYOL environment (a "stack") = one dedicated subnet carved from a Veltrix
|
|
5
|
+
# or customer VNet, plus one compute VM per plan item and the storage / secrets /
|
|
6
|
+
# TLS / LB / DNS a tool tier needs. The shared VNet is looked up (data source),
|
|
7
|
+
# NEVER created here; a dedicated (BYOC) VNet IS created.
|
|
8
|
+
#
|
|
9
|
+
# The `plan` list is the SAME topology the app persists as its BYOL resource
|
|
10
|
+
# rows. Keying compute by `plan_key` is the contract that lets the CI apply
|
|
11
|
+
# report `resource.status` back per row (see outputs.tf).
|
|
12
|
+
#
|
|
13
|
+
# This file mirrors sdk/opentofu/modules/aws/variables.tf 1:1. The SPEC-DERIVED
|
|
14
|
+
# block (foundation_kinds, compute_kinds, security_rules, load_balancer,
|
|
15
|
+
# dns_prefixes, waf_enabled, alb_auth) is byte-identical across all clouds — it
|
|
16
|
+
# is what `renderInfraVars` emits. The DEPLOYMENT vars keep the same names as AWS
|
|
17
|
+
# and adapt only the Azure specifics (AMI -> image, instance types -> vm sizes,
|
|
18
|
+
# route53 -> Azure DNS zone, ACM arn -> Key Vault cert id).
|
|
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 + tag 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 Azure). One of aws|azure|gcp|hetzner."
|
|
40
|
+
type = string
|
|
41
|
+
default = "azure"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
variable "region" {
|
|
45
|
+
description = "Azure location to deploy into, e.g. eastus. (Named `region` for cross-cloud contract parity.)"
|
|
46
|
+
type = string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# --- Network (mode: hosted-shared vs BYOC dedicated/existing) --------------
|
|
50
|
+
# network_mode is a DEPLOYMENT-TARGET var set by the worker per environment — it
|
|
51
|
+
# is NOT part of the app's InfraSpec (which describes only the tool). One app
|
|
52
|
+
# spec deploys hosted OR into a customer's own account, any cloud.
|
|
53
|
+
|
|
54
|
+
variable "network_mode" {
|
|
55
|
+
description = <<-EOT
|
|
56
|
+
How the environment's network is sourced:
|
|
57
|
+
shared — Veltrix-hosted: data-source the shared VNet (network_ref in
|
|
58
|
+
network_resource_group) and create the allocated per-stack
|
|
59
|
+
subnet (subnet_cidr).
|
|
60
|
+
dedicated — BYOC: CREATE a fresh VNet (vpc_cidr) + an App Gateway subnet +
|
|
61
|
+
a private compute subnet + a NAT gateway for private egress,
|
|
62
|
+
isolated per env.
|
|
63
|
+
existing — BYOC: data-source a customer-designated VNet (network_ref in
|
|
64
|
+
network_resource_group) and create subnets inside it.
|
|
65
|
+
EOT
|
|
66
|
+
type = string
|
|
67
|
+
default = "shared"
|
|
68
|
+
|
|
69
|
+
validation {
|
|
70
|
+
condition = contains(["shared", "dedicated", "existing"], var.network_mode)
|
|
71
|
+
error_message = "network_mode must be one of: shared, dedicated, existing."
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
variable "network_ref" {
|
|
76
|
+
description = <<-EOT
|
|
77
|
+
Name of the VNet to deploy into, for network_mode = shared|existing (a
|
|
78
|
+
Veltrix-managed name such as `vnet-veltrix-eastus-shared`, or a customer VNet
|
|
79
|
+
name). Resolved together with network_resource_group. Ignored for
|
|
80
|
+
network_mode = dedicated (the VNet is created).
|
|
81
|
+
EOT
|
|
82
|
+
type = string
|
|
83
|
+
default = ""
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
variable "network_lookup_by" {
|
|
87
|
+
description = <<-EOT
|
|
88
|
+
Contract parity with the other clouds. Azure resolves the VNet by NAME
|
|
89
|
+
(network_ref) + resource group (network_resource_group) via the
|
|
90
|
+
azurerm_virtual_network data source — there is no by-id VNet data source — so
|
|
91
|
+
"id" is accepted but treated the same as "name". Kept for uniform tfvars.
|
|
92
|
+
EOT
|
|
93
|
+
type = string
|
|
94
|
+
default = "name"
|
|
95
|
+
|
|
96
|
+
validation {
|
|
97
|
+
condition = contains(["name", "id", "tag"], var.network_lookup_by)
|
|
98
|
+
error_message = "network_lookup_by must be one of: name, id, tag (Azure resolves by name+resource group regardless)."
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
variable "network_resource_group" {
|
|
103
|
+
description = "Resource group holding the existing VNet (network_ref), for network_mode = shared|existing. The per-stack subnet is created into this RG (subnets belong to their VNet's RG). Ignored for dedicated."
|
|
104
|
+
type = string
|
|
105
|
+
default = ""
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
variable "subnet_cidr" {
|
|
109
|
+
description = <<-EOT
|
|
110
|
+
Per-stack compute subnet CIDR for network_mode = shared|existing
|
|
111
|
+
(IPAM-allocated for hosted). Ignored for dedicated (subnets are carved from
|
|
112
|
+
vpc_cidr). Empty is allowed only when dedicated.
|
|
113
|
+
EOT
|
|
114
|
+
type = string
|
|
115
|
+
default = ""
|
|
116
|
+
|
|
117
|
+
validation {
|
|
118
|
+
condition = var.subnet_cidr == "" || can(cidrhost(var.subnet_cidr, 0))
|
|
119
|
+
error_message = "subnet_cidr must be empty or a valid IPv4 CIDR, e.g. 10.20.16.0/24."
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
variable "vpc_cidr" {
|
|
124
|
+
description = <<-EOT
|
|
125
|
+
Address space for the VNet CREATED when network_mode = dedicated (BYOC). An
|
|
126
|
+
App Gateway /20 (index 0) and a private compute /20 (index 1) are carved from
|
|
127
|
+
it. Named `vpc_cidr` for cross-cloud contract parity. Ignored otherwise.
|
|
128
|
+
EOT
|
|
129
|
+
type = string
|
|
130
|
+
default = "10.60.0.0/16"
|
|
131
|
+
|
|
132
|
+
validation {
|
|
133
|
+
condition = can(cidrhost(var.vpc_cidr, 0))
|
|
134
|
+
error_message = "vpc_cidr must be a valid IPv4 CIDR, e.g. 10.60.0.0/16."
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
variable "appgw_subnet_cidr" {
|
|
139
|
+
description = <<-EOT
|
|
140
|
+
Dedicated subnet CIDR for the Application Gateway when network_mode =
|
|
141
|
+
shared|existing (an Azure Application Gateway REQUIRES its own empty subnet —
|
|
142
|
+
it cannot share the compute subnet). This is the Azure analogue of the AWS
|
|
143
|
+
module's extra_lb_subnet_ids. Required for a load-balancer in shared|existing;
|
|
144
|
+
ignored for dedicated (the module carves the App Gateway subnet from vpc_cidr).
|
|
145
|
+
EOT
|
|
146
|
+
type = string
|
|
147
|
+
default = ""
|
|
148
|
+
|
|
149
|
+
validation {
|
|
150
|
+
condition = var.appgw_subnet_cidr == "" || can(cidrhost(var.appgw_subnet_cidr, 0))
|
|
151
|
+
error_message = "appgw_subnet_cidr must be empty or a valid IPv4 CIDR, e.g. 10.20.17.0/26."
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
variable "admin_cidr" {
|
|
156
|
+
description = "CIDR allowed to reach management ports + the web UI when there is no load balancer. Typically the Veltrix control-plane / customer bastion range."
|
|
157
|
+
type = string
|
|
158
|
+
default = "10.0.0.0/8"
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
# --- The plan (topology) --------------------------------------------------
|
|
162
|
+
|
|
163
|
+
variable "plan" {
|
|
164
|
+
description = <<-EOT
|
|
165
|
+
Ordered resource plan from the app topology. One object per resource the
|
|
166
|
+
environment needs. `plan_key` is the stable key that maps 1:1 to a BYOL
|
|
167
|
+
resource row; the CI apply emits resource.status per plan_key.
|
|
168
|
+
EOT
|
|
169
|
+
type = list(object({
|
|
170
|
+
plan_key = string
|
|
171
|
+
tier = string # foundation | control-plane | data | search | ingest
|
|
172
|
+
kind = string # network | storage | indexer | search-head | management-node | ...
|
|
173
|
+
name = optional(string, "")
|
|
174
|
+
role = optional(string, "")
|
|
175
|
+
region = optional(string, null)
|
|
176
|
+
# Multi-AZ placement: the Azure availability zone ("1"|"2"|"3") this node is
|
|
177
|
+
# pinned to (null = non-zonal). Azure subnets are regional; only the VM zone varies.
|
|
178
|
+
zone = optional(string, null)
|
|
179
|
+
roles = optional(list(string), [])
|
|
180
|
+
}))
|
|
181
|
+
|
|
182
|
+
validation {
|
|
183
|
+
condition = length(var.plan) > 0
|
|
184
|
+
error_message = "plan must contain at least one item."
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# --- Per-tier compute sizing (Azure VM sizes; mirrors AWS instance_types) ---
|
|
189
|
+
|
|
190
|
+
variable "vm_sizes" {
|
|
191
|
+
description = <<-EOT
|
|
192
|
+
Per-tier Azure VM size override, keyed by tier
|
|
193
|
+
(foundation|control-plane|data|search|ingest). Missing tiers fall back to
|
|
194
|
+
vm_size. Kind-level overrides go in vm_sizes_by_kind. (Mirrors AWS
|
|
195
|
+
instance_types.)
|
|
196
|
+
EOT
|
|
197
|
+
type = map(string)
|
|
198
|
+
default = {}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
variable "vm_sizes_by_kind" {
|
|
202
|
+
description = "Per-kind Azure VM size override (wins over vm_sizes). e.g. { indexer = \"Standard_D8s_v5\" }. (Mirrors AWS instance_types_by_kind.)"
|
|
203
|
+
type = map(string)
|
|
204
|
+
default = {}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
variable "vm_size" {
|
|
208
|
+
description = "Fallback Azure VM size for any compute plan item with no tier/kind override. (Mirrors AWS default_instance_type.)"
|
|
209
|
+
type = string
|
|
210
|
+
default = "Standard_B2s"
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
variable "os_disk_gb" {
|
|
214
|
+
description = "OS managed-disk size (GiB) for each compute node. (Mirrors AWS root_volume_gb.)"
|
|
215
|
+
type = number
|
|
216
|
+
default = 100
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
# --- Machine image / access -----------------------------------------------
|
|
220
|
+
|
|
221
|
+
variable "image_ref" {
|
|
222
|
+
description = <<-EOT
|
|
223
|
+
Managed image / Shared Image Gallery version resource id for compute nodes.
|
|
224
|
+
If empty, the module falls back to the latest Ubuntu 22.04 LTS marketplace
|
|
225
|
+
image (scaffold only) — production MUST supply a hardened, tool-preinstalled
|
|
226
|
+
image id. (Mirrors AWS ami_id.)
|
|
227
|
+
EOT
|
|
228
|
+
type = string
|
|
229
|
+
default = ""
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
variable "admin_username" {
|
|
233
|
+
description = "Admin username created on each compute VM. Azure disallows reserved names (admin, root, ...)."
|
|
234
|
+
type = string
|
|
235
|
+
default = "veltrixadmin"
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
variable "admin_ssh_public_key" {
|
|
239
|
+
description = <<-EOT
|
|
240
|
+
Optional OpenSSH public key for break-glass SSH access. When set, VMs use
|
|
241
|
+
key-only auth (password auth disabled). When empty, the module generates a
|
|
242
|
+
complex random password per apply and enables password auth (so a minimal
|
|
243
|
+
plan still applies). (Mirrors AWS key_name.)
|
|
244
|
+
EOT
|
|
245
|
+
type = string
|
|
246
|
+
default = ""
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
# --- Foundation options ----------------------------------------------------
|
|
250
|
+
|
|
251
|
+
variable "dns_domain" {
|
|
252
|
+
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."
|
|
253
|
+
type = string
|
|
254
|
+
default = ""
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
variable "dns_mode" {
|
|
258
|
+
description = <<-EOT
|
|
259
|
+
How the PUBLIC (analyst-facing) DNS name + TLS cert are handled. The PRIVATE
|
|
260
|
+
intra-cluster zone is always created in the deploy account regardless.
|
|
261
|
+
managed — the module creates the public A record in public_dns_zone_name
|
|
262
|
+
(works for hosted on the Veltrix zone AND a BYOC customer-owned
|
|
263
|
+
zone — both live in the deploy subscription). See certificate_arn
|
|
264
|
+
for the TLS handling (Azure has no in-module public CA issuance).
|
|
265
|
+
delegated — BYOC cross-account: the WORKER writes the public record into
|
|
266
|
+
Veltrix's zone; the module makes NO public record and uses
|
|
267
|
+
certificate_arn on the App Gateway listener instead.
|
|
268
|
+
private-only — no public DNS; reached via the customer network (ZTNA/VPN).
|
|
269
|
+
EOT
|
|
270
|
+
type = string
|
|
271
|
+
default = "managed"
|
|
272
|
+
|
|
273
|
+
validation {
|
|
274
|
+
condition = contains(["managed", "delegated", "private-only"], var.dns_mode)
|
|
275
|
+
error_message = "dns_mode must be one of: managed, delegated, private-only."
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
variable "public_dns_zone_name" {
|
|
280
|
+
description = <<-EOT
|
|
281
|
+
Azure Public DNS zone NAME for the public record, when dns_mode = managed
|
|
282
|
+
(e.g. `veltrixsecops.com`). The zone must live in the DEPLOY subscription
|
|
283
|
+
(Veltrix's for hosted, the customer's for BYOC customer-owned). This holds the
|
|
284
|
+
ROLE of AWS's route53_zone_id.
|
|
285
|
+
EOT
|
|
286
|
+
type = string
|
|
287
|
+
default = ""
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
variable "public_dns_rg" {
|
|
291
|
+
description = "Resource group of public_dns_zone_name. Required when dns_mode = managed and the plan carries a dns item."
|
|
292
|
+
type = string
|
|
293
|
+
default = ""
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
variable "certificate_arn" {
|
|
297
|
+
description = <<-EOT
|
|
298
|
+
Key Vault certificate/secret id for the App Gateway HTTPS listener
|
|
299
|
+
(secret-identifier form, e.g.
|
|
300
|
+
https://<vault>.vault.azure.net/secrets/<name>/<version>). Named
|
|
301
|
+
`certificate_arn` for cross-cloud contract parity; on Azure it holds a Key
|
|
302
|
+
Vault cert id, NOT an ACM ARN.
|
|
303
|
+
|
|
304
|
+
Unlike AWS (which auto-issues a DNS-validated ACM cert in dns_mode = managed),
|
|
305
|
+
Azure has NO in-module public CA issuance — the App Gateway consumes a Key
|
|
306
|
+
Vault certificate that must be provisioned out-of-band. Therefore BOTH managed
|
|
307
|
+
and delegated modes reference this var for the HTTPS listener. Pair it with
|
|
308
|
+
appgw_identity_id (a user-assigned identity with GET access to that Key Vault).
|
|
309
|
+
Empty => the App Gateway serves HTTP only (no HTTPS listener).
|
|
310
|
+
EOT
|
|
311
|
+
type = string
|
|
312
|
+
default = ""
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
variable "appgw_identity_id" {
|
|
316
|
+
description = <<-EOT
|
|
317
|
+
Resource id of a user-assigned managed identity that has GET access to the Key
|
|
318
|
+
Vault holding certificate_arn. Required for the HTTPS listener (App Gateway
|
|
319
|
+
reads the KV cert through this identity). Provisioning the identity + KV access
|
|
320
|
+
is a worker/caller responsibility (analogous to AWS's cross-account ACM
|
|
321
|
+
validation in delegated mode). Empty => no HTTPS listener.
|
|
322
|
+
EOT
|
|
323
|
+
type = string
|
|
324
|
+
default = ""
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
variable "web_ingress_cidr" {
|
|
328
|
+
description = <<-EOT
|
|
329
|
+
CIDR allowed to reach the PUBLIC App Gateway on 443/80. Declared for
|
|
330
|
+
cross-cloud contract parity. NOTE: Azure Application Gateway v2 subnets require
|
|
331
|
+
permissive GatewayManager/AzureLoadBalancer NSG rules, so source-IP restriction
|
|
332
|
+
is best expressed as a WAF custom rule rather than a subnet NSG — that is a
|
|
333
|
+
follow-on and is NOT wired here (the module does not narrow ingress by this var).
|
|
334
|
+
EOT
|
|
335
|
+
type = string
|
|
336
|
+
default = "0.0.0.0/0"
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
variable "alb_auth" {
|
|
340
|
+
description = <<-EOT
|
|
341
|
+
Optional OIDC/Cognito-style MFA at the front door, in front of the tool's web
|
|
342
|
+
UI. Shape kept byte-identical to AWS for uniform tfvars. NOTE: Azure
|
|
343
|
+
Application Gateway has NO direct equivalent of AWS ALB authenticate-cognito;
|
|
344
|
+
the Azure follow-on is Azure AD Application Proxy / Front Door + Entra ID. This
|
|
345
|
+
var is ACCEPTED but NO-OP'd on Azure (documented gap) — see main.tf.
|
|
346
|
+
EOT
|
|
347
|
+
type = object({
|
|
348
|
+
enabled = optional(bool, false)
|
|
349
|
+
user_pool_arn = optional(string, "")
|
|
350
|
+
user_pool_client_id = optional(string, "")
|
|
351
|
+
user_pool_domain = optional(string, "")
|
|
352
|
+
})
|
|
353
|
+
default = {}
|
|
354
|
+
|
|
355
|
+
validation {
|
|
356
|
+
condition = !var.alb_auth.enabled || (
|
|
357
|
+
var.alb_auth.user_pool_arn != "" &&
|
|
358
|
+
var.alb_auth.user_pool_client_id != "" &&
|
|
359
|
+
var.alb_auth.user_pool_domain != ""
|
|
360
|
+
)
|
|
361
|
+
error_message = "When alb_auth.enabled is true, user_pool_arn, user_pool_client_id and user_pool_domain are all required."
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
variable "create_private_zone" {
|
|
366
|
+
description = <<-EOT
|
|
367
|
+
Create an Azure Private DNS zone for dns_domain, linked to the deploy VNet, to
|
|
368
|
+
hold per-node function FQDNs (idx1.<domain>, sh1.<domain>, ...). Ignored when
|
|
369
|
+
dns_domain is empty. Mutually complementary with private_zone_id: set
|
|
370
|
+
private_zone_id instead to reuse an existing zone.
|
|
371
|
+
EOT
|
|
372
|
+
type = bool
|
|
373
|
+
default = false
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
variable "private_zone_id" {
|
|
377
|
+
description = <<-EOT
|
|
378
|
+
Set non-empty to REUSE an existing Private DNS zone (named dns_domain) instead
|
|
379
|
+
of creating one; takes precedence over create_private_zone. Kept as
|
|
380
|
+
`private_zone_id` for cross-cloud parity — on Azure the zone is addressed by
|
|
381
|
+
NAME (dns_domain) + private_zone_resource_group, so this value is only used as
|
|
382
|
+
the "reuse an existing zone" flag. Leave empty (and set create_private_zone) to
|
|
383
|
+
have the module create the zone.
|
|
384
|
+
EOT
|
|
385
|
+
type = string
|
|
386
|
+
default = ""
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
variable "private_zone_resource_group" {
|
|
390
|
+
description = "Resource group of the existing Private DNS zone to reuse (when private_zone_id is set). Ignored when the module creates the zone (records go into the per-stack RG)."
|
|
391
|
+
type = string
|
|
392
|
+
default = ""
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
# --- Declarative infra spec (rendered from the app's InfraSpec) -----------
|
|
396
|
+
# These are what make the module tool-agnostic. The app declares its ports /
|
|
397
|
+
# front-door / DNS as DATA (sdk/src/opentofu/spec.ts InfraSpec) and the SDK
|
|
398
|
+
# renders them here. NOTHING below is app-specific. This block is BYTE-IDENTICAL
|
|
399
|
+
# across the aws/azure/gcp/hetzner modules — it is exactly what renderInfraVars emits.
|
|
400
|
+
|
|
401
|
+
variable "foundation_kinds" {
|
|
402
|
+
description = <<-EOT
|
|
403
|
+
Plan `kind`s the module realizes as shared FOUNDATION infra (not compute).
|
|
404
|
+
Any plan item whose kind is NOT in this set (and not named by compute_kinds)
|
|
405
|
+
is a compute node. Kept in sync with FOUNDATION_KINDS in spec.ts.
|
|
406
|
+
EOT
|
|
407
|
+
type = list(string)
|
|
408
|
+
default = [
|
|
409
|
+
"network", "storage", "secrets", "tls",
|
|
410
|
+
"load-balancer", "dns", "license-file", "hec",
|
|
411
|
+
]
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
variable "compute_kinds" {
|
|
415
|
+
description = <<-EOT
|
|
416
|
+
Optional explicit allow-list of compute kinds. When non-empty, ONLY these
|
|
417
|
+
kinds become compute nodes. When empty (default), compute = any plan item
|
|
418
|
+
whose kind is not in foundation_kinds (so an app's roles are compute
|
|
419
|
+
automatically).
|
|
420
|
+
EOT
|
|
421
|
+
type = list(string)
|
|
422
|
+
default = []
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
variable "security_rules" {
|
|
426
|
+
description = <<-EOT
|
|
427
|
+
Ingress rules from the app's InfraSpec. Each rule opens `port` to one or
|
|
428
|
+
more `sources`: "self" (peer nodes — the node ASG referencing itself),
|
|
429
|
+
"alb" (the front-door / App Gateway subnet; ignored when there is no load
|
|
430
|
+
balancer), or "admin" (var.admin_cidr). Replaces any hardcoded, tool-specific
|
|
431
|
+
port list.
|
|
432
|
+
EOT
|
|
433
|
+
type = list(object({
|
|
434
|
+
port = number
|
|
435
|
+
protocol = optional(string, "tcp")
|
|
436
|
+
sources = list(string)
|
|
437
|
+
description = optional(string, "")
|
|
438
|
+
}))
|
|
439
|
+
default = []
|
|
440
|
+
|
|
441
|
+
validation {
|
|
442
|
+
condition = alltrue([
|
|
443
|
+
for r in var.security_rules : alltrue([
|
|
444
|
+
for s in r.sources : contains(["self", "alb", "admin"], s)
|
|
445
|
+
])
|
|
446
|
+
])
|
|
447
|
+
error_message = "Every security_rules[*].sources entry must be one of: self, alb, admin."
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
variable "load_balancer" {
|
|
452
|
+
description = <<-EOT
|
|
453
|
+
Front-door spec from the app's InfraSpec. Null for headless / forwarder-only
|
|
454
|
+
tools. When set (and the plan carries a load-balancer item), the module builds
|
|
455
|
+
the App Gateway backend pool + health probe + listeners from this.
|
|
456
|
+
`target_kinds` are the compute kinds that sit behind the front door.
|
|
457
|
+
EOT
|
|
458
|
+
type = object({
|
|
459
|
+
target_port = number
|
|
460
|
+
target_protocol = optional(string, "HTTP")
|
|
461
|
+
health_check_path = string
|
|
462
|
+
health_check_matcher = optional(string, "200-399")
|
|
463
|
+
health_check_protocol = optional(string, "")
|
|
464
|
+
target_kinds = list(string)
|
|
465
|
+
listener_port = optional(number, 443)
|
|
466
|
+
})
|
|
467
|
+
default = null
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
variable "dns_prefixes" {
|
|
471
|
+
description = <<-EOT
|
|
472
|
+
kind -> DNS label prefix for per-node function FQDNs (e.g.
|
|
473
|
+
{ indexer = "idx", search-head = "sh", cluster-manager = "mgmt" }). A compute
|
|
474
|
+
kind absent from the map falls back to the kind string itself.
|
|
475
|
+
EOT
|
|
476
|
+
type = map(string)
|
|
477
|
+
default = {}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
variable "waf_enabled" {
|
|
481
|
+
description = "Attach a WAF policy (OWASP managed rules) to the App Gateway (WAF_v2 SKU). Ignored when there is no load balancer."
|
|
482
|
+
type = bool
|
|
483
|
+
default = true
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
# --- Tags (threaded onto every taggable resource) --------------------------
|
|
487
|
+
|
|
488
|
+
variable "tags" {
|
|
489
|
+
description = <<-EOT
|
|
490
|
+
Canonical Veltrix tag set applied to every taggable resource
|
|
491
|
+
(Veltrix:Customer, Veltrix:Environment, Veltrix:App, Veltrix:ManagedBy,
|
|
492
|
+
CostCenter, Owner, ...). The apply identity is scoped by
|
|
493
|
+
Veltrix:ManagedBy = Veltrix, so this map MUST include it. NOTE: Azure subnets,
|
|
494
|
+
NSG rules, storage containers, and NIC associations are not taggable and carry
|
|
495
|
+
no tags.
|
|
496
|
+
EOT
|
|
497
|
+
type = map(string)
|
|
498
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# Azure environment module — provider/version pinning (OpenTofu-compatible HCL).
|
|
3
|
+
#
|
|
4
|
+
# OpenTofu resolves providers from registry.opentofu.org by default but the
|
|
5
|
+
# canonical `hashicorp/azurerm` source address is mirrored there, so this block
|
|
6
|
+
# is 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
|
+
# NOTE: this module declares NO `provider "azurerm"` block — the platform
|
|
10
|
+
# render+apply worker configures the provider (subscription_id, features {}, and
|
|
11
|
+
# the customer credential for BYOC) at the root, and the module inherits it.
|
|
12
|
+
# =============================================================================
|
|
13
|
+
|
|
14
|
+
terraform {
|
|
15
|
+
required_version = ">= 1.6.0"
|
|
16
|
+
|
|
17
|
+
required_providers {
|
|
18
|
+
azurerm = {
|
|
19
|
+
source = "hashicorp/azurerm"
|
|
20
|
+
version = "~> 4.0"
|
|
21
|
+
}
|
|
22
|
+
random = {
|
|
23
|
+
source = "hashicorp/random"
|
|
24
|
+
version = "~> 3.6"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|